From fa794a5ab3f18a04d072c7f2fc923f4b678a8fea Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 06:50:36 +0800 Subject: [PATCH 01/54] refactor(editor): unify UI ownership and editing workflows Separate feature operations from presentation, own UI state per session, and unify identity-aware selection and reversible editing through Core services. Add scoped Play identity routing, UI-neutral labels, guarded native borrows, architecture constraints, verified callers and current acceptance evidence. --- AGENTS.md | 10 + InnoEngine.sln | 87 +- docs/README.md | 2 + docs/architecture/CURRENT_ACCEPTANCE.md | 2 + docs/architecture/EDITOR_UI_ARCHITECTURE.md | 81 + .../ENGINE_ARCHITECTURE_OVERVIEW.md | 2 +- docs/architecture/README.md | 2 + docs/editor/Inno.Editor.Annotations.md | 284 +- docs/editor/Inno.Editor.Assets.ImGui.md | 113 + docs/editor/Inno.Editor.Assets.md | 328 +- docs/editor/Inno.Editor.Audio.md | 56 +- docs/editor/Inno.Editor.Core.md | 560 ++- docs/editor/Inno.Editor.Diagnostics.md | 132 +- docs/editor/Inno.Editor.Exporting.md | 63 +- docs/editor/Inno.Editor.Graph.ImGui.md | 58 + docs/editor/Inno.Editor.Graph.md | 136 +- docs/editor/Inno.Editor.Hosting.md | 155 +- docs/editor/Inno.Editor.ImGui.md | 3019 +++++++++++++---- docs/editor/Inno.Editor.Inspection.md | 425 +-- docs/editor/Inno.Editor.Interactions.md | 1870 +++++----- docs/editor/Inno.Editor.Panel.FileBrowser.md | 249 +- docs/editor/Inno.Editor.Panel.GameView.md | 33 +- docs/editor/Inno.Editor.Panel.Hierarchy.md | 56 +- docs/editor/Inno.Editor.Panel.Inspector.md | 75 +- docs/editor/Inno.Editor.Panel.Logging.md | 28 +- docs/editor/Inno.Editor.Panel.SceneView.md | 36 +- docs/editor/Inno.Editor.Panel.Settings.md | 26 +- docs/editor/Inno.Editor.Panel.ShaderEditor.md | 54 +- docs/editor/Inno.Editor.Panel.Stats.md | 16 +- docs/editor/Inno.Editor.PlayMode.md | 163 +- docs/editor/Inno.Editor.Rendering.md | 1282 +++---- docs/editor/Inno.Editor.Scene.md | 192 +- docs/editor/Inno.Editor.Scripting.md | 102 +- docs/editor/Inno.Editor.Settings.md | 237 +- docs/editor/Inno.Editor.Shaders.ImGui.md | 151 + docs/editor/Inno.Editor.Shaders.md | 394 +-- ...nel.Global.md => Inno.Editor.Workbench.md} | 42 +- docs/editor/README.md | 16 +- .../testing/Inno.Editor.Interactions.Tests.md | 2 +- docs/testing/README.md | 3 + .../EDITOR_UI_IMPLEMENTATION_PLAN.md | 54 + .../EDITOR_UI_ACCEPTANCE_2026_10_27.md | 120 + src/composition/editor/EditorProduct.props | 5 +- .../Documents/AssetDraftDocuments.cs | 275 +- .../Editing}/AssetCreationRegistry.cs | 58 +- .../Editing/AssetEditingModule.Creation.cs} | 56 +- .../Editing/AssetEditingModule.Selection.cs | 144 + .../Editing/AssetEditingModule.cs | 651 ++++ .../Editing}/AssetEditor.cs | 7 +- .../Editing}/AssetEditorAttribute.cs | 5 +- .../Editing}/AssetEditorContext.cs | 16 +- .../Editing}/AssetEditorRegistry.cs | 50 +- .../Editing}/AssetOperationValidation.cs | 5 +- .../History/AssetHistoryData.cs | 6 +- .../History/AssetHistoryHandler.cs | 100 +- .../History/AssetHistoryKinds.cs | 3 +- .../History/AssetHistoryOperationKind.cs | 3 +- .../History/AssetSourceArchive.cs | 32 +- .../AssetImportSettingsEdits.cs | 60 +- .../Inno.Editor.Assets.csproj | 22 +- .../Properties/ScriptingApi.cs | 13 +- .../Samples/AssetSampleImportModule.cs | 95 +- .../Inno.Editor.Exporting.csproj | 24 +- ...WindowModule.cs => EditorExportService.cs} | 343 +- .../Inno.Editor.PlayMode/IEditorPlayMode.cs | 13 +- .../Inno.Editor.PlayMode.csproj | 29 +- .../Runtime/EditorPlayModeController.cs | 54 +- .../Runtime/EditorPlayModeModule.cs | 10 +- .../Inno.Editor.Rendering.csproj | 44 +- .../Pipelines/PipelineDocuments.cs | 94 +- .../EditorSceneWorkspace.PlayMode.cs | 46 +- .../Documents/SceneEdits.Elements.cs | 261 +- .../Documents/SceneEdits.Selection.cs | 278 ++ .../Inno.Editor.Scripting.csproj | 36 +- .../Runtime/EditorScripting.cs | 106 +- .../Runtime/IEditorScriptCompilation.cs | 19 +- .../ScriptingReloadActions.cs | 13 +- .../Inno.Editor.Shaders.csproj | 44 +- .../Materials/MaterialDocuments.cs | 139 +- .../Properties/ScriptingApi.cs | 5 - .../Extensions/EditorTypeSpecificity.cs | 59 + .../Presentation/EditorDisplayNames.cs | 54 + .../Properties/ScriptingApi.cs | 2 + .../Inno.Editor.Diagnostics/EditorConsole.cs | 23 +- .../BuiltInInspectorAttributeDrawers.cs | 67 +- .../Inno.Editor.Inspection.csproj | 18 +- .../ObjectDrawing/AssetInspectionSelection.cs | 34 - .../ObjectDrawing/DefaultInspectionDrawer.cs | 14 +- .../Properties/ScriptingApi.cs | 7 +- .../PropertyDrawing/BooleanPropertyDrawer.cs | 5 +- .../CollectionPropertyDrawer.cs | 140 +- .../PropertyDrawing/EnumPropertyDrawer.cs | 33 +- .../PropertyDrawing/GuidPropertyDrawer.cs | 15 +- .../IInspectionPropertyEditService.cs | 15 +- .../MathematicsPropertyDrawers.cs | 147 +- .../PropertyDrawing/NullablePropertyDrawer.cs | 26 +- .../PropertyDrawing/NumericPropertyDrawer.cs | 56 +- .../PropertyDrawing/PropertyDrawContext.cs | 100 +- .../SerializableObjectPropertyDrawer.cs | 28 +- .../SerializedPropertyRenderer.cs | 127 +- .../PropertyDrawing/StringPropertyDrawer.cs | 4 +- .../PropertyDrawing/StructPropertyDrawer.cs | 72 +- .../UnsupportedPropertyDrawer.cs | 10 +- .../Actions/EditorAction.cs | 101 +- .../Actions/EditorActionContext.cs | 21 +- .../Actions/EditorActionRouter.cs | 162 +- .../Actions/EditorActionState.cs | 18 +- .../Discovery/EditorExtensionCatalog.cs | 539 ++- .../Documents/EditorDocumentService.cs | 55 +- .../Editing/EditorEditGesture.cs | 96 + .../Editing/EditorOperationPreparingEvent.cs | 10 + .../History/EditorHistory.cs | 255 +- .../Menus/EditorMenuCatalog.cs | 99 +- .../Properties/ScriptingApi.cs | 24 +- .../Runtime/EditorInteraction.cs | 42 +- .../Runtime/EditorInteractions.cs | 416 ++- .../Runtime/EditorSelectionState.cs | 79 - .../Runtime/EditorTargetReference.cs | 64 + .../Runtime/IEditorSelectionCoordinator.cs | 27 +- .../Selection/EditorSelectionSnapshot.cs | 65 + .../Selection/EditorSelectionState.cs | 207 ++ .../Viewport/EditorViewportToolContext.cs | 22 +- .../Inno.Editor.Settings.csproj | 19 +- .../Runtime/EditorSettings.cs | 87 +- .../Runtime/EditorSettingsChangedEvent.cs | 7 + .../Hosting/EditorLayer.cs | 36 +- .../AssetEditors/AssetEditorModule.cs | 734 ---- .../AssetEditors/AssetTypeDistance.cs | 37 - .../Browser/FileBrowserChangeTracker.cs | 72 +- .../Browser/FileBrowserContextMenu.cs | 47 +- .../Browser/FileBrowserData.cs | 48 +- .../Browser/FileBrowserDragDrop.cs | 47 +- .../Browser/FileBrowserNavigation.cs | 30 +- .../Browser/FileBrowserPanel.Filters.cs | 34 +- .../Browser/FileBrowserPanel.Grid.cs | 268 +- .../Browser/FileBrowserPanel.Layout.cs | 245 +- .../Browser/FileBrowserPanel.List.cs | 282 +- .../Browser/FileBrowserPanel.Navigation.cs | 152 +- .../Browser/FileBrowserPanel.cs | 68 +- .../Browser/FileBrowserRename.cs | 50 +- .../Browser/FileBrowserTree.cs | 143 +- .../Browser/FileBrowserUtility.cs | 131 +- .../FileBrowserInteractionIds.cs | 4 +- .../Inno.Editor.Panel.FileBrowser.csproj | 44 +- .../Interactions/CreateAssetCommand.cs | 47 +- .../Interactions/CreateAssetFolderCommand.cs | 69 +- .../Interactions/DeleteAssetCommand.cs | 30 +- .../Interactions/ImportAssetSampleCommand.cs | 13 +- .../Interactions/MoveAssetEntryDropHandler.cs | 32 +- .../Interactions/OpenAssetCommand.cs | 32 +- .../Interactions/RenameAssetCommand.cs | 58 +- .../Properties/ScriptingApi.cs | 18 - .../Samples/AssetSampleImportModal.cs | 13 +- .../GamePresentationSetting.cs | 16 +- .../GameViewBackgroundSetting.cs | 14 +- .../GameViewPanel.cs | 144 +- .../Inno.Editor.Panel.GameView.csproj | 29 +- .../Inno.Editor.Panel.Global.csproj | 39 - .../Settings/IconSettings.cs | 284 -- .../Inno.Editor.Panel.Hierarchy.csproj | 39 +- .../Interactions/DeleteGameObjectCommand.cs | 33 +- .../MoveGameObjectToSceneDropHandler.cs | 49 +- .../RenameHierarchyTargetCommand.cs | 51 +- .../ReparentGameObjectDropHandler.cs | 95 +- .../Interactions/SaveCommand.cs | 19 +- .../Presentation/HierarchyDropVisual.cs | 24 +- .../Presentation/HierarchyPanel.cs | 334 +- .../Presentation/HierarchySelection.cs | 34 +- .../Inno.Editor.Panel.Inspector.csproj | 62 +- .../Interactions/AddComponentCommand.cs | 30 +- .../Interactions/RemoveComponentCommand.cs | 16 +- .../Interactions/RemoveSystemCommand.cs | 13 +- .../Interactions/ResetComponentCommand.cs | 12 +- .../Interactions/ResetSystemCommand.cs | 13 +- .../Layers/GameLayersSetting.cs | 194 +- .../Layers/GameObjectLayerSelector.cs | 50 +- .../Presentation/InspectorCardControls.cs | 192 +- .../Presentation/InspectorPanel.cs | 60 +- .../Presentation/InspectorTargetHeader.cs | 146 +- .../Presentation/InspectorTypeOrigin.cs | 12 +- .../AssetReferencePropertyDrawer.cs | 104 +- .../EngineObjectReferencePropertyDrawer.cs | 48 +- .../SceneInspectionPropertyEditService.cs | 37 +- .../Targets/ComponentEditorTarget.cs | 42 - .../Targets/GameObjectInspectionDrawer.cs | 243 +- .../Targets/GameObjectTagSelector.cs | 54 +- .../Targets/GameSceneInspectionDrawer.cs | 186 +- .../Targets/GameTagsSetting.cs | 110 +- .../Targets}/PipelineInspectionDrawer.cs | 247 +- .../Targets/SelectionInspectionDrawer.cs | 205 ++ .../Targets/SystemEditorTarget.cs | 42 - .../Targets/TransformInspectionDrawer.cs | 84 +- .../Console/ConsoleEntryCopyTarget.cs | 15 +- .../Console/ConsoleEntryImGuiIdentity.cs | 7 +- .../Console/ConsolePanel.cs | 294 +- .../Console/ConsolePanelContent.cs | 28 +- .../Console/CopyConsoleDetailsCommand.cs | 6 +- .../Console/CopyConsoleMessageCommand.cs | 6 +- .../Inno.Editor.Panel.Logging.csproj | 31 +- .../Logging/LoggingModule.cs | 32 +- .../Settings/ConsoleSettings.cs | 10 +- .../Inno.Editor.Panel.SceneView.csproj | 38 +- .../SceneViewBackgroundSetting.cs | 14 +- .../SceneViewPanel.cs | 915 ++--- .../Inno.Editor.Panel.Settings.csproj | 23 +- .../Presentation/BuildSettingsPresentation.cs | 226 +- .../ProjectIdentitySettingsEditor.cs | 9 +- .../Presentation/SettingsModal.cs | 200 +- .../Presentation/SettingsPageView.cs | 215 +- .../Presentation/SettingsTree.cs | 88 +- .../Canvas/ShaderEditorCanvas.Controls.cs | 312 +- .../Canvas/ShaderEditorCanvas.Drawing.cs | 146 - .../ShaderEditorCanvas.InputDefaults.cs | 51 +- .../Canvas/ShaderEditorCanvas.MiniMap.cs | 122 +- .../Canvas/ShaderEditorCanvas.Parameters.cs | 161 +- .../Canvas/ShaderEditorCanvas.PassSettings.cs | 103 +- .../Canvas/ShaderEditorCanvas.PortRepair.cs | 25 +- .../ShaderEditorCanvas.SourceSettings.cs | 77 +- .../Canvas/ShaderEditorCanvas.Target.cs | 10 +- .../Canvas/ShaderEditorCanvas.Techniques.cs | 68 +- .../Canvas/ShaderEditorCanvas.cs | 753 ++-- .../Documents/ShaderEditorDocuments.cs | 158 +- .../Inno.Editor.Panel.ShaderEditor.csproj | 60 +- .../Inspection/ShaderInspectionDrawers.cs | 164 +- .../Interactions/CreateShaderAsset.cs | 34 +- .../Interactions/ShaderCanvasActions.cs | 241 +- .../ShaderCanvasOrganizationActions.cs | 245 +- .../Presentation/ShaderCheckModal.cs | 25 +- .../ShaderEditorPanel.cs | 25 +- .../Inno.Editor.Panel.Stats.csproj | 6 - .../Inno.Editor.Panel.Stats/StatsPanel.cs | 74 +- .../Icons/AssetIconAttribute.cs | 18 +- .../Icons/AssetIconRegistry.cs | 88 +- .../Icons/BuiltInAssetIcons.cs | 7 +- .../Inno.Editor.Assets.ImGui.csproj | 28 + .../AssetSelectionInspectionDrawer.cs | 41 +- .../Navigation/AssetBrowserModule.cs | 150 + .../Navigation}/AssetBrowserState.cs | 111 +- .../Properties/ScriptingApi.cs | 7 + .../Canvas/GraphCanvasView.cs | 168 + .../Inno.Editor.Graph.ImGui.csproj | 23 + .../Properties/ScriptingApi.cs | 5 + .../Controls/ImGui.Controls.cs | 138 + .../Controls/ImGui.Drawing.cs | 70 + .../Inno.Editor.ImGui/Controls/ImGui.Input.cs | 278 ++ .../Controls/ImGui.Layout.cs | 282 ++ .../Controls/ImGui.Tables.cs | 46 + .../Controls/Options/ImGuiButtonFlags.cs | 34 + .../Controls/Options/ImGuiChildFlags.cs | 50 + .../Controls/Options/ImGuiCol.cs | 261 ++ .../Controls/Options/ImGuiColorEditFlags.cs | 138 + .../Controls/Options/ImGuiComboFlags.cs | 50 + .../Controls/Options/ImGuiCond.cs | 30 + .../Controls/Options/ImGuiFocusedFlags.cs | 38 + .../Controls/Options/ImGuiHoveredFlags.cs | 94 + .../Controls/Options/ImGuiInputTextFlags.cs | 114 + .../Controls/Options/ImGuiKey.cs | 669 ++++ .../Controls/Options/ImGuiMouseButton.cs | 25 + .../Controls/Options/ImGuiMouseCursor.cs | 61 + .../Controls/Options/ImGuiPopupFlags.cs | 62 + .../Controls/Options/ImGuiSelectableFlags.cs | 42 + .../Controls/Options/ImGuiSliderFlags.cs | 54 + .../Controls/Options/ImGuiStyleVar.cs | 177 + .../Controls/Options/ImGuiTabBarFlags.cs | 62 + .../Controls/Options/ImGuiTabItemFlags.cs | 50 + .../Controls/Options/ImGuiTableBgTarget.cs | 25 + .../Controls/Options/ImGuiTableColumnFlags.cs | 122 + .../Controls/Options/ImGuiTableFlags.cs | 158 + .../Controls/Options/ImGuiTableRowFlags.cs | 18 + .../Controls/Options/ImGuiTreeNodeFlags.cs | 98 + .../Controls/Options/ImGuiWindowFlags.cs | 130 + .../Drawing/EditorDrawList.cs | 204 ++ .../Drawing/ImGui.Drawing.cs | 135 + .../presentation/Inno.Editor.ImGui/ImGui.cs | 473 ++- .../Inno.Editor.ImGui.csproj | 26 +- .../Properties/ScriptingApi.cs | 39 +- .../Runtime/EditorDragDropRenderer.cs | 26 +- .../Runtime/EditorMenuRenderer.cs | 218 +- .../Runtime/EditorModalHost.cs | 36 +- .../Runtime/EditorModalRenderer.cs | 37 +- .../Runtime/EditorUiInput.cs | 59 + .../Runtime/EditorUiSession.cs | 185 + .../Runtime/ImGuiEditorRuntime.cs | 118 +- .../Styling/EditorUiLayout.cs | 54 + .../TextInput/EditorIconCatalog.cs | 15 + .../Inno.Editor.ImGui/TextInput/ImGuiFont.cs | 102 + .../Inno.Editor.ImGui/TextInput/ImGuiIcon.cs | 179 + .../TextInput/ImGuiUtf8Buffer.cs | 61 +- .../Widgets/ImGuiWidget.Card.cs | 84 +- .../Widgets/ImGuiWidget.ContextMenu.cs | 84 +- .../Widgets/ImGuiWidget.Controls.cs | 96 +- .../Widgets/ImGuiWidget.DragDrop.cs | 35 +- .../Widgets/ImGuiWidget.Editing.cs | 152 + .../Widgets/ImGuiWidget.Gizmo.cs | 257 ++ .../Widgets/ImGuiWidget.HelpBox.cs | 162 +- .../Widgets/ImGuiWidget.InlineRename.cs | 67 +- .../Widgets/ImGuiWidget.Lifetime.cs | 36 + .../Widgets/ImGuiWidget.MixedValue.cs | 46 + .../Widgets/ImGuiWidget.Panel.cs | 136 +- .../Widgets/ImGuiWidget.Property.cs | 187 +- .../Widgets/ImGuiWidget.Search.cs | 12 +- .../Widgets/ImGuiWidget.Selection.cs | 44 + .../Widgets/ImGuiWidget.Selector.cs | 66 +- .../Widgets/ImGuiWidget.Style.cs | 26 +- .../Widgets/ImGuiWidget.Text.cs | 273 +- .../Widgets/ImGuiWidget.Tree.cs | 212 +- .../Inno.Editor.Shaders.ImGui.csproj | 40 + .../Materials/MaterialInspectionDrawer.cs | 279 +- .../Materials/MaterialSelectionDrawer.cs | 269 +- .../Materials/ShaderPropertyInspector.cs | 99 +- .../Nodes/ShaderNodeDrawer.cs | 77 +- .../Nodes/ShaderNodeDrawerRegistry.cs | 16 +- .../Previews/ShaderPreviews.cs | 69 +- .../Properties/ScriptingApi.cs | 9 + .../Actions/HistoryActions.cs | 35 +- .../Actions/SelectionActions.cs | 3 +- .../Actions/TogglePanelAction.cs | 12 +- .../Commands/CommandSearchAction.cs | 14 + .../Commands/CommandSearchModal.cs | 47 + .../Commands/CommandSearchModule.cs | 74 + .../Exporting}/ExportActions.cs | 9 +- .../Exporting}/ExportInteractionIds.cs | 3 +- .../Exporting/ExportWindowModule.cs | 125 + .../Exporting}/GameExportModal.cs | 118 +- .../Exporting}/GameExportProgressModal.cs | 17 +- .../Exporting}/PluginExportModal.cs | 72 +- .../GlobalInteractionIds.cs | 6 +- .../Inno.Editor.Workbench.csproj | 28 + .../Menus/WorkbenchMenuSource.cs | 26 + .../Scripting}/ScriptCompilationModal.cs | 16 +- .../Settings/DensitySetting.cs | 18 +- .../Settings/FramePacingSettings.cs | 27 +- .../Settings/GlobalSettingsPages.cs | 3 +- .../Settings/IconSettings.cs | 218 ++ .../Zoom/ActualSizeSetting.cs | 32 +- .../Zoom/EditorZoomModule.cs | 50 +- .../Zoom/ZoomActions.cs | 21 +- tests/Invoke-EditorSmoke.ps1 | 5 + tests/Invoke-IsolatedTests.ps1 | 9 +- .../GraphCanvasViewTests.cs | 41 + .../Inno.Editor.Graph.Tests.csproj | 2 +- .../EditorUiSessionTests.cs | 59 + .../Inno.Editor.ImGui.Tests.csproj | 3 +- .../ViewportMetricsTests.cs | 112 +- .../WidgetLayoutTests.cs | 225 +- .../ConditionalInspectionTests.cs | 68 +- .../Inno.Editor.Inspection.Tests.csproj | 7 +- .../AssetHistoryTests.cs | 123 +- .../EditorHistoryTests.cs | 208 +- .../EditorRuntimeTests.Selection.cs | 207 ++ .../EditorRuntimeTests.cs | 413 +-- .../EditorSettingsTests.cs | 140 +- .../Inno.Editor.Interactions.Tests.csproj | 4 +- .../SceneHistoryTests.Selection.cs | 131 + .../SceneHistoryTests.cs | 273 +- .../ConsolePanelNativeLayoutTests.cs | 14 +- .../EditorPlayModeTests.cs | 224 +- .../EditorPopupLayoutTests.cs | 48 +- .../Inno.Editor.PlayMode.Tests.csproj | 2 + .../PlayModeLogRetentionTests.cs | 13 +- .../EditorStyleMetricsTests.cs | 517 ++- .../Inno.Editor.Scripting.Tests.csproj | 7 + .../InspectionPipelineTests.cs | 160 +- .../InspectorPresentationLayoutTests.cs | 49 +- .../ScriptingPipelineTests.cs | 934 ++--- .../ShaderEditorWorkflowTests.cs | 442 ++- .../IntegrationBoundaryTests.cs | 8 +- .../ArchitectureRules.cs | 739 ++-- .../ArchitectureValidator.cs | 7 +- .../EditorUiBoundaryValidator.cs | 55 + 370 files changed, 24518 insertions(+), 19085 deletions(-) create mode 100644 docs/architecture/EDITOR_UI_ARCHITECTURE.md create mode 100644 docs/editor/Inno.Editor.Assets.ImGui.md create mode 100644 docs/editor/Inno.Editor.Graph.ImGui.md create mode 100644 docs/editor/Inno.Editor.Shaders.ImGui.md rename docs/editor/{Inno.Editor.Panel.Global.md => Inno.Editor.Workbench.md} (75%) create mode 100644 records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md create mode 100644 records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetCreationRegistry.cs (53%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs => features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs} (54%) create mode 100644 src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs create mode 100644 src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetEditor.cs (98%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetEditorAttribute.cs (97%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetEditorContext.cs (89%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetEditorRegistry.cs (61%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/Editing}/AssetOperationValidation.cs (97%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/History/AssetHistoryData.cs (97%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/History/AssetHistoryHandler.cs (75%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/History/AssetHistoryKinds.cs (74%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/History/AssetHistoryOperationKind.cs (75%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/History/AssetSourceArchive.cs (91%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => features/Inno.Editor.Assets/ImportSettings}/AssetImportSettingsEdits.cs (87%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => features/Inno.Editor.Assets}/Samples/AssetSampleImportModule.cs (68%) rename src/composition/editor/features/Inno.Editor.Exporting/Runtime/{ExportWindowModule.cs => EditorExportService.cs} (50%) create mode 100644 src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs delete mode 100644 src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs delete mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs create mode 100644 src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettingsChangedEvent.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetTypeDistance.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Properties/ScriptingApi.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/IconSettings.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/ComponentEditorTarget.cs rename src/composition/editor/{features/Inno.Editor.Rendering/Pipelines => panels/Inno.Editor.Panel.Inspector/Targets}/PipelineInspectionDrawer.cs (62%) create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SystemEditorTarget.cs delete mode 100644 src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Drawing.cs rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => presentation/Inno.Editor.Assets.ImGui}/Icons/AssetIconAttribute.cs (94%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => presentation/Inno.Editor.Assets.ImGui}/Icons/AssetIconRegistry.cs (60%) rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser => presentation/Inno.Editor.Assets.ImGui}/Icons/BuiltInAssetIcons.cs (88%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/AssetEditors => presentation/Inno.Editor.Assets.ImGui/Inspection}/AssetSelectionInspectionDrawer.cs (76%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs rename src/composition/editor/{panels/Inno.Editor.Panel.FileBrowser/Browser => presentation/Inno.Editor.Assets.ImGui/Navigation}/AssetBrowserState.cs (71%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Inno.Editor.Shaders.ImGui.csproj rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Materials/MaterialInspectionDrawer.cs (62%) rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Materials/MaterialSelectionDrawer.cs (62%) rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Materials/ShaderPropertyInspector.cs (70%) rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Nodes/ShaderNodeDrawer.cs (82%) rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Nodes/ShaderNodeDrawerRegistry.cs (93%) rename src/composition/editor/{features/Inno.Editor.Shaders => presentation/Inno.Editor.Shaders.ImGui}/Previews/ShaderPreviews.cs (82%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Properties/ScriptingApi.cs rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Actions/HistoryActions.cs (74%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Actions/SelectionActions.cs (96%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Actions/TogglePanelAction.cs (62%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchAction.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModal.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModule.cs rename src/composition/editor/{features/Inno.Editor.Exporting/Interactions => presentation/Inno.Editor.Workbench/Exporting}/ExportActions.cs (86%) rename src/composition/editor/{features/Inno.Editor.Exporting => presentation/Inno.Editor.Workbench/Exporting}/ExportInteractionIds.cs (87%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportWindowModule.cs rename src/composition/editor/{features/Inno.Editor.Exporting/Presentation => presentation/Inno.Editor.Workbench/Exporting}/GameExportModal.cs (56%) rename src/composition/editor/{features/Inno.Editor.Exporting/Presentation => presentation/Inno.Editor.Workbench/Exporting}/GameExportProgressModal.cs (70%) rename src/composition/editor/{features/Inno.Editor.Exporting/Presentation => presentation/Inno.Editor.Workbench/Exporting}/PluginExportModal.cs (53%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/GlobalInteractionIds.cs (69%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Menus/WorkbenchMenuSource.cs rename src/composition/editor/{features/Inno.Editor.Scripting/Runtime => presentation/Inno.Editor.Workbench/Scripting}/ScriptCompilationModal.cs (68%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Settings/DensitySetting.cs (78%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Settings/FramePacingSettings.cs (85%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Settings/GlobalSettingsPages.cs (96%) create mode 100644 src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Zoom/ActualSizeSetting.cs (77%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Zoom/EditorZoomModule.cs (70%) rename src/composition/editor/{panels/Inno.Editor.Panel.Global => presentation/Inno.Editor.Workbench}/Zoom/ZoomActions.cs (89%) create mode 100644 tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs create mode 100644 tests/editor/Inno.Editor.ImGui.Tests/EditorUiSessionTests.cs create mode 100644 tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs create mode 100644 tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.Selection.cs create mode 100644 tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs diff --git a/AGENTS.md b/AGENTS.md index d8d950633..e91a80b49 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -316,3 +316,13 @@ public void AAA( - Pull Request 正文必须根据目标分支的完整差异说明问题、最终行为与实际验证,并明确未执行的检查;创建前检索已有 PR,避免重复,不得将部分验收描述为完整矩阵通过。 - Git 提交的 author 和 committer 均使用 `Aaron Liao `;不得使用 Codex/bot 身份或添加任何 `Co-authored-by` 署名。Pull Request 使用用户授权的 GitHub 账号创建。 - BGCS 缺陷必须通过公开 API 独立复现并明确标记;不得修改 BGCS 源码、包、二进制或手工修改生成物来绕过缺陷。 + +## 32. Editor UI ownership and acceptance + +- Domain editing in `features` must remain independent from native UI, Panels and Workbench. Panels compose feature APIs; they do not reference other Panels. Reusable controls belong to the session-owned ImGui adapter, and generic Graph presentation cannot depend on Shader semantics. +- Every interactive host owns one `EditorUiSession`. Transient widget/menu state belongs to its session and stable presentation owner. Native drawing borrows cannot survive their frame, window, context or owner thread. `current` is only a synchronous scoped accessor, never a global state owner. +- Continuous Inspector and viewport edits use one `EditorEditGesture` over `EditorInteractions.history`. Each sample records immediately; Escape rolls back the same transaction. Save, commands and generation changes finish the gesture before their own operation. No feature may delay History publication behind a separate UI-active flag. +- Selection, deferred commands and cross-frame tools resolve identities through Core Identity. Batch operations validate the complete selection, normalize overlapping roots and roll back atomically. Mixed, Missing, read-only and unassigned states remain distinct. +- Final delivery must evaluate architecture, organization, state/lifetime cleanliness, abstraction, reuse, visual consistency, extensibility, interaction, Undo/Redo, menu clarity and component granularity individually. Each evaluation must identify actual evidence, limitations and remaining improvements; directory changes and interface counts alone are insufficient. +- UI-neutral name formatting and Action/Menu queries must work without a native context or drawing session. Keep pure label formatting in Editor Core; only drawing and native borrows require an active UI scope. +- Temporary presentation worlds register their borrowed Core allocator through the existing selection coordinator's routing lease. Restore Edit selection before releasing that lease; failed retirement must preserve its owner. The routing table never becomes a second live-object authority. diff --git a/InnoEngine.sln b/InnoEngine.sln index 518746974..d7b7e434f 100644 --- a/InnoEngine.sln +++ b/InnoEngine.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -111,7 +111,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Panel.FileBrows EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Panel.GameView", "src\composition\editor\panels\Inno.Editor.Panel.GameView\Inno.Editor.Panel.GameView.csproj", "{7EF925D1-9D9C-44D4-9B41-9D8A07A000BA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Panel.Global", "src\composition\editor\panels\Inno.Editor.Panel.Global\Inno.Editor.Panel.Global.csproj", "{A32DDE6E-BA0D-40AA-BED3-1A10049D59BC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Workbench", "src\composition\editor\presentation\Inno.Editor.Workbench\Inno.Editor.Workbench.csproj", "{A32DDE6E-BA0D-40AA-BED3-1A10049D59BC}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Panel.Hierarchy", "src\composition\editor\panels\Inno.Editor.Panel.Hierarchy\Inno.Editor.Panel.Hierarchy.csproj", "{720483C6-D06B-481D-8899-E1D9BD654EDA}" EndProject @@ -767,6 +767,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Integration.MacOSArm64 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Integration.BrowserWasm32.Build", "platforms\BrowserWasm32\integrations\Inno.Integration.BrowserWasm32.Build\Inno.Integration.BrowserWasm32.Build.csproj", "{1848B25F-5C18-41DD-8B7D-56E17CBDA88C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Assets.ImGui", "src\composition\editor\presentation\Inno.Editor.Assets.ImGui\Inno.Editor.Assets.ImGui.csproj", "{7CF807CF-4294-5C9F-9207-FEF4887D4164}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Graph.ImGui", "src\composition\editor\presentation\Inno.Editor.Graph.ImGui\Inno.Editor.Graph.ImGui.csproj", "{802F03CC-512F-59F1-9A3D-F7F7339CEE8C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inno.Editor.Shaders.ImGui", "src\composition\editor\presentation\Inno.Editor.Shaders.ImGui\Inno.Editor.Shaders.ImGui.csproj", "{691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -783,6 +789,78 @@ Global SourceRelease|x86 = SourceRelease|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|x64.ActiveCfg = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|x64.Build.0 = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|x86.ActiveCfg = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Debug|x86.Build.0 = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|Any CPU.Build.0 = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|x64.ActiveCfg = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|x64.Build.0 = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|x86.ActiveCfg = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.Release|x86.Build.0 = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|Any CPU.ActiveCfg = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|Any CPU.Build.0 = Debug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|x64.ActiveCfg = SourceDebug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|x64.Build.0 = SourceDebug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|x86.ActiveCfg = SourceDebug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceDebug|x86.Build.0 = SourceDebug|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|Any CPU.ActiveCfg = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|Any CPU.Build.0 = Release|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|x64.ActiveCfg = SourceRelease|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|x64.Build.0 = SourceRelease|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|x86.ActiveCfg = SourceRelease|Any CPU + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1}.SourceRelease|x86.Build.0 = SourceRelease|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|x64.ActiveCfg = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|x64.Build.0 = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|x86.ActiveCfg = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Debug|x86.Build.0 = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|Any CPU.Build.0 = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|x64.ActiveCfg = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|x64.Build.0 = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|x86.ActiveCfg = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.Release|x86.Build.0 = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|Any CPU.ActiveCfg = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|Any CPU.Build.0 = Debug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|x64.ActiveCfg = SourceDebug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|x64.Build.0 = SourceDebug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|x86.ActiveCfg = SourceDebug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceDebug|x86.Build.0 = SourceDebug|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|Any CPU.ActiveCfg = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|Any CPU.Build.0 = Release|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|x64.ActiveCfg = SourceRelease|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|x64.Build.0 = SourceRelease|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|x86.ActiveCfg = SourceRelease|Any CPU + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C}.SourceRelease|x86.Build.0 = SourceRelease|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|x64.Build.0 = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Debug|x86.Build.0 = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|Any CPU.Build.0 = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|x64.ActiveCfg = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|x64.Build.0 = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|x86.ActiveCfg = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.Release|x86.Build.0 = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|Any CPU.ActiveCfg = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|Any CPU.Build.0 = Debug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|x64.ActiveCfg = SourceDebug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|x64.Build.0 = SourceDebug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|x86.ActiveCfg = SourceDebug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceDebug|x86.Build.0 = SourceDebug|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|Any CPU.ActiveCfg = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|Any CPU.Build.0 = Release|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|x64.ActiveCfg = SourceRelease|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|x64.Build.0 = SourceRelease|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|x86.ActiveCfg = SourceRelease|Any CPU + {7CF807CF-4294-5C9F-9207-FEF4887D4164}.SourceRelease|x86.Build.0 = SourceRelease|Any CPU {E05AA6C5-47EA-46E2-AB87-AAB36667880D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E05AA6C5-47EA-46E2-AB87-AAB36667880D}.Debug|Any CPU.Build.0 = Debug|Any CPU {E05AA6C5-47EA-46E2-AB87-AAB36667880D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -6478,6 +6556,9 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {691683C5-2477-5FA0-AFCA-9A56E3ADDAD1} = {DB68A96A-C62D-504F-BD0B-71520E141558} + {802F03CC-512F-59F1-9A3D-F7F7339CEE8C} = {DB68A96A-C62D-504F-BD0B-71520E141558} + {7CF807CF-4294-5C9F-9207-FEF4887D4164} = {DB68A96A-C62D-504F-BD0B-71520E141558} {E05AA6C5-47EA-46E2-AB87-AAB36667880D} = {FB6B5AAC-8FC0-40B6-993E-9B06BD070F2B} {6B47D935-1188-4474-BFC8-032732BD2276} = {FB6B5AAC-8FC0-40B6-993E-9B06BD070F2B} {F96214FE-3779-5DE2-891C-9F770F0035F6} = {B4620EEB-BE09-5C67-9437-083242A0513D} @@ -6530,7 +6611,7 @@ Global {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} = {8B7360D5-B8A7-5423-BD4A-8D41C720C3E6} {3F3E5448-3C50-455E-A18A-8057D5153231} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} {7EF925D1-9D9C-44D4-9B41-9D8A07A000BA} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} - {A32DDE6E-BA0D-40AA-BED3-1A10049D59BC} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} + {A32DDE6E-BA0D-40AA-BED3-1A10049D59BC} = {DB68A96A-C62D-504F-BD0B-71520E141558} {720483C6-D06B-481D-8899-E1D9BD654EDA} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} {37EFCF00-19A6-497D-8E6B-49876C9E1985} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} {860DF6A9-D1D5-49E3-9CE5-D2DE347CEED0} = {C88FEFB4-C927-523A-A2CE-3A8BD4D591D7} diff --git a/docs/README.md b/docs/README.md index fe01cd830..537f7d9ba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,3 +58,5 @@ [Inno.Testing.Source](testing/Inno.Testing.Source.md) 为需要 checkout 的测试读取显式 SDK 输入,源码只属于该测试支持程序集。 平台构建接入新增独立标准产品库、Windows 宿主及三个平台 Build integration;各项目的当前公开契约和签名映射见对应分类索引。 + +Editor UI 的程序集边界、当前文件树、手势、选择与基础能力复用见 [Editor UI 架构](architecture/EDITOR_UI_ARCHITECTURE.md)。[Editor 索引](editor/README.md) 包含 Assets.ImGui、Shaders.ImGui、Graph.ImGui 和 Workbench 的独立项目页。 diff --git a/docs/architecture/CURRENT_ACCEPTANCE.md b/docs/architecture/CURRENT_ACCEPTANCE.md index 2f8409866..de6ddf729 100644 --- a/docs/architecture/CURRENT_ACCEPTANCE.md +++ b/docs/architecture/CURRENT_ACCEPTANCE.md @@ -2,6 +2,8 @@ [架构索引](README.md) · [当前问题](CURRENT_ISSUES.md) · [Wiki 首页](../README.md) +当前 Editor UI 的分层、Session、Selection、History、插件调用方及逐项质量评价见[Editor UI 验收](../../records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md)和[当前 UI 架构](EDITOR_UI_ARCHITECTURE.md)。该报告分别列出自动验证、产品运行与未执行的人工/设备检查,不将此前数量累加为本次结果。 + 当前依赖通过标准 `.csproj` / `PackageReference` 和 SDK Restore 解析,源码不保存独立依赖锁清单;本轮范围与证据见[标准 NuGet 还原验收](../../records/architecture/acceptance/STANDARD_NUGET_RESTORE_ACCEPTANCE_2026_10_26.md)。基线保持 .NET SDK 10.0.402、C# 14、BGCS 4.1.2。 此前 BGCS 消费与三项优先修复的独立证据见[BGCS 4.1.2 消费验收](../../records/architecture/acceptance/BGCS412_CONSUMER_ACCEPTANCE_2026_10_25.md)及[专项报告](../../records/architecture/acceptance/PRIORITY_FIX_ACCEPTANCE_2026_10_25.md)。历史通过数量不能当作当前源码已重跑。 diff --git a/docs/architecture/EDITOR_UI_ARCHITECTURE.md b/docs/architecture/EDITOR_UI_ARCHITECTURE.md new file mode 100644 index 000000000..9330732a2 --- /dev/null +++ b/docs/architecture/EDITOR_UI_ARCHITECTURE.md @@ -0,0 +1,81 @@ +# Editor UI 架构与执行边界 + +[架构索引](ENGINE_ARCHITECTURE_OVERVIEW.md) · [Editor Wiki](../editor/README.md) + +## 当前目录 + +```text +src/composition/editor/ +├── contracts/Inno.Editor.Annotations/ # Stable discovery metadata +├── framework/ +│ ├── Inno.Editor.Core/ # Context, Module, Panel, state +│ ├── Inno.Editor.Interactions/ +│ │ ├── Actions/ # Availability and fresh dispatch +│ │ ├── Selection/ # Ordered Core Identity references +│ │ ├── Editing/ # One shared History gesture +│ │ ├── History/ # Neutral records and atomic transactions +│ │ ├── Documents/ # Save/Revert/Apply/Close ownership +│ │ ├── Menus/ # Action-derived menu models +│ │ └── Viewport/ # Generic interaction tools +│ ├── Inno.Editor.Inspection/ # Registries and serialized controls +│ ├── Inno.Editor.Graph/ # Graph document/state/history +│ └── Inno.Editor.Settings/ # Structured settings and Core Events +├── features/ +│ ├── Inno.Editor.Assets/ +│ │ ├── Editing/ # Reusable source mutations +│ │ ├── History/ # Source archives and neutral records +│ │ ├── Documents/ # Immediate draft history/recovery +│ │ └── Samples/ # Formal Sample import +│ ├── Inno.Editor.Scene/Documents/ # Granular SceneEdits and batch operations +│ ├── Inno.Editor.Shaders/Materials/ # Material drafts and source presentation +│ ├── Inno.Editor.Rendering/Pipelines/ # Pipeline source drafts +│ ├── Inno.Editor.Scripting/Runtime/ # Compilation and retirement; no UI +│ └── Inno.Editor.Exporting/Runtime/ # Export ownership; no UI +├── presentation/ +│ ├── Inno.Editor.ImGui/ +│ │ ├── Runtime/ # Session, frame and owner lifetime +│ │ ├── Controls/ # Pointer-free facade and borrows +│ │ ├── Widgets/ # Session-owned controls +│ │ ├── Menus/ # Unified models to ImGui +│ │ └── Styling/ # Palette and density metrics +│ ├── Inno.Editor.Assets.ImGui/ # Browser state, icons, basic Inspector +│ ├── Inno.Editor.Shaders.ImGui/ # Material and node presentation +│ ├── Inno.Editor.Graph.ImGui/ # Generic canvas geometry and hit tests +│ └── Inno.Editor.Workbench/ # Global commands, settings, export UI +├── panels/ # Concrete views, no Panel references +└── hosting/Inno.Editor.Hosting/ # Runtime composition and lifetime +``` + +## 真正的依赖与执行链 + +领域操作和 Undo payload 不包含原生 UI 类型。Panel 读取当前 owner/context,调用 feature API;feature 先验证并执行修改,然后向同一个 History 发布中立记录。ImGui adapter 负责 native context、ID stack、绘制借用和 transient 状态。宿主负责初始化顺序与候选切换。 + +普通菜单、快捷键、工具栏与 command search 消费同一 Action 的 Query/Execute;presentation 不能维护另一份 enabled/checked 业务规则。Action 索引按 active generation 失效。所有跨帧目标用 Core Identity 重新解析,不能保留 Component wrapper 或 collectible Type。 + +连续输入在首个有效 sample 打开 EditorEditGesture,release 提交,Escape 回滚。显式 Selection 切换先提交当前输入,准备失败则保持原选择;关闭 owner 或未完成的控件消失时回滚。命令和 reload 边界同样使用已有 Core Events 准备通知。材质/Pipeline 不再自己维护 deferred gesture;SceneEdits 和 asset draft service 都立即记录。 + +多选包含有序身份和 anchor;visible range 来自同一过滤/排序结果。批量源操作归一化最小 roots,先验证所有 owner,再由既有 History transaction 执行;失败不接受半批结果。共同属性使用 owner serialization context,保持各对象独立的 before 值。 + +## 复用与颗粒度 + +复用 Core Events、Identity、Serialization、AtomicFile 与现有 Graph/History;未新增平行事件总线、Undo 栈、identity registry 或依赖注入框架。Graph.ImGui 只提取实际共用的坐标、网格、曲线及命中机制;Shader 的 ports、参数与诊断仍由 Shader owner 解释。 + +ShaderEditor 是最大的领域专用 view,其 canvas、预览、菜单和文档协调按功能文件组织。该部分仍有降低复杂度的空间;不通过新增空 interface/project 或把全部方法变 public 来伪装解耦。架构评价和实际验收见本轮交付报告。 + +## 自动约束 + +EditorUiBoundaryValidator 校验 feature 无 UI/native/Panel 引用、Panel 无相互引用、native 访问集中在 adapter、Graph 无 Rendering/Shader 依赖。公开 API 依赖分组、XML、逻辑脚本导出、Solution 和 Wiki 继续由仓库统一验证器验证。 + + +## 精确多选分量与 History 查询 + +属性编辑传递 root member、当前手势 key 和可选 scalar component。向量、整数向量及矩形控件明确传递被编辑的公共字段;Euler 控件声明具体轴。SceneEdits 使用当前公开值字段或 Core Quaternion 运算组合目标值,未读取私有字段,也不缓存插件 metadata。持久 History 继续保存既有 root property bytes,未增加第二种序列化格式。 + +History Query 只判断当前 owner/handler 是否可用。草稿的 expected bytes 在实际 Apply 时检查,避免同一事务较早 sample 因当前值已前进而使整个 Undo 永久禁用。Apply 冲突仍明确失败并保留栈位置。 + + +## 临时运行世界的 Identity 路由 + +启动时登记 Asset 与 Edit allocator;Play 的独立 allocator 由 Scene workspace 在准备候选前取得 `RegisterIdentityDomain` 路由 lease。Selection、focus、queued Action 与 native DragDrop 共同读取这一域路由表,实际对象始终由 Core Identity 解析。返回 Edit 并恢复选择成功后再释放 lease,失败保留可重试的 owner;不登记全局 allocator,也不保存第二张 live object 表。 + +`IEditorSelectionCoordinator` 是 Host 组合协议,承担临时 presentation 的域所有权,不再导出给项目脚本。脚本继续通过 `EditorInteractions` 使用选择和交互,不负责创建或登记 allocator。 diff --git a/docs/architecture/ENGINE_ARCHITECTURE_OVERVIEW.md b/docs/architecture/ENGINE_ARCHITECTURE_OVERVIEW.md index 3094c06c7..83521eed3 100644 --- a/docs/architecture/ENGINE_ARCHITECTURE_OVERVIEW.md +++ b/docs/architecture/ENGINE_ARCHITECTURE_OVERVIEW.md @@ -182,7 +182,7 @@ Core Events 是唯一事件机制;跨代 live object 经 Identity 解析。Mis - `Inno.Editor.Hosting`:[`src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj`](../../src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj)。 - `Inno.Editor.Panel.FileBrowser`:[`src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj)。 - `Inno.Editor.Panel.GameView`:[`src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj)。 -- `Inno.Editor.Panel.Global`:[`src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj)。 +- `Inno.Editor.Workbench`:[`src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj`](../../src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj)。 - `Inno.Editor.Panel.Hierarchy`:[`src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj)。 - `Inno.Editor.Panel.Inspector`:[`src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj)。 - `Inno.Editor.Panel.Logging`:[`src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj`](../../src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj)。 diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 11a538845..d7aa573eb 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -27,3 +27,5 @@ 日期化计划、阶段报告和旧审查保存于仓库 records 目录;当前 Wiki 不通过历史完成声明解释当前能力。 - [Native 构建平台职责与文件树](NATIVE_BUILD_PLATFORM_OWNERSHIP.md) + +- [Editor UI 架构与交互所有权](EDITOR_UI_ARCHITECTURE.md) diff --git a/docs/editor/Inno.Editor.Annotations.md b/docs/editor/Inno.Editor.Annotations.md index 023bbcfc8..be12b0f9f 100644 --- a/docs/editor/Inno.Editor.Annotations.md +++ b/docs/editor/Inno.Editor.Annotations.md @@ -53,68 +53,33 @@ public sealed class Mover : GameBehavior ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Annotations.HeaderAttribute` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.HeaderAttribute.HeaderAttribute(string title, string description = "")`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L38) | Creates a section header with optional explanatory text.
`title`:Visible section title.
`description`:Optional explanatory text displayed when the title is hovered. | — | — | -| [`public string Inno.Editor.Annotations.HeaderAttribute.description { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L55) | Gets the optional explanatory text. | — | — | -| [`public string Inno.Editor.Annotations.HeaderAttribute.title { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L50) | Gets the visible section title. | — | — | -| [`public sealed class Inno.Editor.Annotations.HeaderAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L27) | Starts a visually separated Inspector section before the annotated property. | — | — | - -### `Inno.Editor.Annotations.HelpBoxAttribute` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L438) | Creates an Inspector help message.
`text`:Non-empty message text.
`messageType`:Visual severity. | — | — | -| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, string conditionMember, Inno.Editor.Annotations.InspectorCondition condition, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L461) | Creates an Inspector help message shown when a sibling satisfies a named condition.
`text`:Non-empty message text.
`conditionMember`:Sibling field or property name.
`condition`:Comparison applied to the sibling value.
`messageType`:Visual severity. | — | — | -| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, string conditionMember, object? expectedValue, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L486) | Creates an Inspector help message shown when a sibling equals a compile-time value.
`text`:Non-empty message text.
`conditionMember`:Sibling field or property name.
`expectedValue`:The expected value consumed by help box attribute; ownership remains with the caller unless explicitly stated otherwise.
`messageType`:Visual severity. | — | — | -| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.HelpBoxAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L529) | Gets the optional sibling comparison. | — | — | -| [`public string Inno.Editor.Annotations.HelpBoxAttribute.conditionMember { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L524) | Gets the optional sibling member controlling message visibility. | — | — | -| [`public object? Inno.Editor.Annotations.HelpBoxAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L534) | Gets the optional expected constant. | — | — | -| [`public Inno.Editor.Annotations.InspectorMessageType Inno.Editor.Annotations.HelpBoxAttribute.messageType { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L519) | Gets the visual severity. | — | — | -| [`public string Inno.Editor.Annotations.HelpBoxAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L514) | Gets the message text. | — | — | -| [`public sealed class Inno.Editor.Annotations.HelpBoxAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L427) | Draws a wrapped explanatory message before the annotated Inspector property. | — | — | - -### `Inno.Editor.Annotations.HideIfAttribute` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName, Inno.Editor.Annotations.InspectorCondition condition)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L367) | Creates a named condition that does not require an expected value.
`memberName`:Sibling field or property name.
`condition`:Comparison applied to the sibling value. | — | — | -| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName, object? expectedValue)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L350) | Creates an equality condition.
`memberName`:Sibling field or property name.
`expectedValue`:Compile-time value that hides the property. | — | — | -| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L336) | Creates a truthiness condition.
`memberName`:Sibling field or property name. | — | — | -| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.HideIfAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L394) | Gets the comparison. | — | — | -| [`public object? Inno.Editor.Annotations.HideIfAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L399) | Gets the optional expected constant. | — | — | -| [`public string Inno.Editor.Annotations.HideIfAttribute.memberName { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L389) | Gets the sibling member name. | — | — | -| [`public sealed class Inno.Editor.Annotations.HideIfAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L328) | Hides the annotated Inspector property when a sibling member satisfies a condition. | — | — | - -### `Inno.Editor.Annotations.InspectorCondition` - + + + + + + + + + + + + + + + + + + @@ -124,83 +89,6 @@ public sealed class Mover : GameBehavior - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Annotations.InspectorCondition.Assigned = 6`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L238) | Requires a reference-like value to expose an assigned resource. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.Equal = 2`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L218) | Compares the value with the supplied expected constant. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.Falsy = 1`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L213) | Shows a value when it evaluates as false, null, or empty. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.NotAssigned = 7`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L243) | Requires a reference-like value not to expose an assigned resource. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.NotEqual = 3`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L223) | Requires the value to differ from the supplied expected constant. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.NotNull = 5`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L233) | Requires a non-null value. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.Null = 4`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L228) | Requires a null value. | — | — | -| [`Inno.Editor.Annotations.InspectorCondition.Truthy = 0`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L208) | Shows a value when it evaluates as true or non-empty. | — | — | -| [`public enum Inno.Editor.Annotations.InspectorCondition : int`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L203) | Defines the comparison used by conditional Inspector presentation. | — | — | - -### `Inno.Editor.Annotations.InspectorMessageType` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Annotations.InspectorMessageType.Error = 2`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L420) | An invalid or unusable configuration. | — | — | -| [`Inno.Editor.Annotations.InspectorMessageType.Info = 0`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L410) | Neutral explanatory information. | — | — | -| [`Inno.Editor.Annotations.InspectorMessageType.Warning = 1`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L415) | A non-blocking configuration warning. | — | — | -| [`public enum Inno.Editor.Annotations.InspectorMessageType : int`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L405) | Selects the visual severity of an Inspector help message. | — | — | - -### `Inno.Editor.Annotations.InspectorNameAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.InspectorNameAttribute.InspectorNameAttribute(string name)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L142) | Creates a label override.
`name`:Non-empty visible label. | — | — | -| [`public string Inno.Editor.Annotations.InspectorNameAttribute.name { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L151) | Gets the visible label. | — | — | -| [`public sealed class Inno.Editor.Annotations.InspectorNameAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L134) | Replaces the generated Inspector label for one property. | — | — | - -### `Inno.Editor.Annotations.InspectorPresentationAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Annotations.InspectorPresentationAttribute.InspectorPresentationAttribute()`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L15) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public int Inno.Editor.Annotations.InspectorPresentationAttribute.order { get; set; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L20) | Gets or sets the relative order among presentation attributes declared on the same member. | — | — | -| [`public abstract class Inno.Editor.Annotations.InspectorPresentationAttribute : System.Attribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L15) | Provides editor-only presentation metadata for one serialized field or property. | Presentation attributes never participate in serialization. Concrete attributes are conditional on INNO_EDITOR, so their applications are omitted from Player script assemblies. | — | - -### `Inno.Editor.Annotations.InspectorReadOnlyAttribute` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.InspectorReadOnlyAttribute.InspectorReadOnlyAttribute()`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L196) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public sealed class Inno.Editor.Annotations.InspectorReadOnlyAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L196) | Makes an otherwise writable serialized property read-only in the Inspector. | — | — | - -### `Inno.Editor.Annotations.RangeAttribute` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.RangeAttribute.RangeAttribute(double minimum, double maximum)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L169) | Creates an inclusive numeric range.
`minimum`:Inclusive lower bound.
`maximum`:Inclusive upper bound. | — | — | -| [`public double Inno.Editor.Annotations.RangeAttribute.maximum { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L189) | Gets the inclusive upper bound. | — | — | -| [`public double Inno.Editor.Annotations.RangeAttribute.minimum { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L184) | Gets the inclusive lower bound. | — | — | -| [`public sealed class Inno.Editor.Annotations.RangeAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L158) | Constrains a numeric Inspector control to an inclusive range. | — | — | - -### `Inno.Editor.Annotations.ShowIfAttribute` - @@ -208,54 +96,94 @@ public sealed class Mover : GameBehavior + + + + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName, Inno.Editor.Annotations.InspectorCondition condition)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L289) | Creates a named condition that does not require an expected value.
`memberName`:Sibling field or property name.
`condition`:Comparison applied to the sibling value. | — | — | -| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName, object? expectedValue)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L272) | Creates an equality condition.
`memberName`:Sibling field or property name.
`expectedValue`:Compile-time value required for visibility. | — | — | -| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L258) | Creates a truthiness condition.
`memberName`:Sibling field or property name. | — | — | -| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.ShowIfAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L316) | Gets the comparison. | — | — | -| [`public object? Inno.Editor.Annotations.ShowIfAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L321) | Gets the optional expected constant. | — | — | -| [`public string Inno.Editor.Annotations.ShowIfAttribute.memberName { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L311) | Gets the sibling member name. | — | — | -| [`public sealed class Inno.Editor.Annotations.ShowIfAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L250) | Shows the annotated Inspector property only when a sibling member satisfies a condition. | — | — | - -### `Inno.Editor.Annotations.SpaceAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.SpaceAttribute.SpaceAttribute(float height = 8)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L94) | Creates a spacing decorator.
`height`:Non-negative logical pixel height. | — | — | -| [`public float Inno.Editor.Annotations.SpaceAttribute.height { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L103) | Gets the logical pixel height. | — | — | -| [`public sealed class Inno.Editor.Annotations.SpaceAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L86) | Inserts vertical spacing before the annotated Inspector property. | — | — | - -### `Inno.Editor.Annotations.TextAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.TextAttribute.TextAttribute(string text)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L70) | Creates persistent Inspector text.
`text`:Non-empty text displayed in the Inspector layout. | — | — | -| [`public string Inno.Editor.Annotations.TextAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L79) | Gets the persistent Inspector text. | — | — | +| [`public abstract class Inno.Editor.Annotations.InspectorPresentationAttribute : System.Attribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L15) | Provides editor-only presentation metadata for one serialized field or property. | — | — | +| [`public int Inno.Editor.Annotations.InspectorPresentationAttribute.order { get; set; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L20) | Gets or sets the relative order among presentation attributes declared on the same member. | — | — | +| [`protected Inno.Editor.Annotations.InspectorPresentationAttribute.InspectorPresentationAttribute()`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Annotations.HeaderAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L27) | Starts a visually separated Inspector section before the annotated property. | — | — | +| [`public Inno.Editor.Annotations.HeaderAttribute.HeaderAttribute(string title, string description = "")`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L38) | Creates a section header with optional explanatory text.
`title`: Visible section title.
`description`: Optional explanatory text displayed when the title is hovered. | — | — | +| [`public string Inno.Editor.Annotations.HeaderAttribute.title { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L50) | Gets the visible section title. | — | — | +| [`public string Inno.Editor.Annotations.HeaderAttribute.description { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L55) | Gets the optional explanatory text. | — | — | | [`public sealed class Inno.Editor.Annotations.TextAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L62) | Draws persistent wrapped explanatory text before the annotated Inspector property. | — | — | - -### `Inno.Editor.Annotations.TooltipAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Annotations.TooltipAttribute.TooltipAttribute(string text)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L118) | Creates property hover help.
`text`:Non-empty tooltip text. | — | — | -| [`public string Inno.Editor.Annotations.TooltipAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L127) | Gets the tooltip text. | — | — | +| [`public Inno.Editor.Annotations.TextAttribute.TextAttribute(string text)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L70) | Creates persistent Inspector text.
`text`: Non-empty text displayed in the Inspector layout. | — | — | +| [`public string Inno.Editor.Annotations.TextAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L79) | Gets the persistent Inspector text. | — | — | +| [`public sealed class Inno.Editor.Annotations.SpaceAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L86) | Inserts vertical spacing before the annotated Inspector property. | — | — | +| [`public Inno.Editor.Annotations.SpaceAttribute.SpaceAttribute(float height = 8)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L94) | Creates a spacing decorator.
`height`: Non-negative logical pixel height. | — | — | +| [`public float Inno.Editor.Annotations.SpaceAttribute.height { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L103) | Gets the logical pixel height. | — | — | | [`public sealed class Inno.Editor.Annotations.TooltipAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L110) | Provides hover help for one Inspector property label. | — | — | +| [`public Inno.Editor.Annotations.TooltipAttribute.TooltipAttribute(string text)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L118) | Creates property hover help.
`text`: Non-empty tooltip text. | — | — | +| [`public string Inno.Editor.Annotations.TooltipAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L127) | Gets the tooltip text. | — | — | +| [`public sealed class Inno.Editor.Annotations.InspectorNameAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L134) | Replaces the generated Inspector label for one property. | — | — | +| [`public Inno.Editor.Annotations.InspectorNameAttribute.InspectorNameAttribute(string name)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L142) | Creates a label override.
`name`: Non-empty visible label. | — | — | +| [`public string Inno.Editor.Annotations.InspectorNameAttribute.name { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L151) | Gets the visible label. | — | — | +| [`public sealed class Inno.Editor.Annotations.RangeAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L158) | Constrains a numeric Inspector control to an inclusive range. | — | — | +| [`public Inno.Editor.Annotations.RangeAttribute.RangeAttribute(double minimum, double maximum)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L169) | Creates an inclusive numeric range.
`minimum`: Inclusive lower bound.
`maximum`: Inclusive upper bound. | — | — | +| [`public double Inno.Editor.Annotations.RangeAttribute.minimum { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L184) | Gets the inclusive lower bound. | — | — | +| [`public double Inno.Editor.Annotations.RangeAttribute.maximum { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L189) | Gets the inclusive upper bound. | — | — | +| [`public sealed class Inno.Editor.Annotations.InspectorReadOnlyAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L196) | Makes an otherwise writable serialized property read-only in the Inspector. | — | — | +| [`public Inno.Editor.Annotations.InspectorReadOnlyAttribute.InspectorReadOnlyAttribute()`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L196) | Inherited or compiler-provided contract. | — | — | +| [`public enum Inno.Editor.Annotations.InspectorCondition : int`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L203) | Defines the comparison used by conditional Inspector presentation. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.Truthy = 0`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L208) | Shows a value when it evaluates as true or non-empty. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.Falsy = 1`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L213) | Shows a value when it evaluates as false, null, or empty. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.Equal = 2`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L218) | Compares the value with the supplied expected constant. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.NotEqual = 3`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L223) | Requires the value to differ from the supplied expected constant. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.Null = 4`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L228) | Requires a null value. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.NotNull = 5`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L233) | Requires a non-null value. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.Assigned = 6`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L238) | Requires a reference-like value to expose an assigned resource. | — | — | +| [`Inno.Editor.Annotations.InspectorCondition.NotAssigned = 7`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L243) | Requires a reference-like value not to expose an assigned resource. | — | — | +| [`public sealed class Inno.Editor.Annotations.ShowIfAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L250) | Shows the annotated Inspector property only when a sibling member satisfies a condition. | — | — | +| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L258) | Creates a truthiness condition.
`memberName`: Sibling field or property name. | — | — | +| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName, object? expectedValue)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L272) | Creates an equality condition.
`memberName`: Sibling field or property name.
`expectedValue`: Compile-time value required for visibility. | — | — | +| [`public Inno.Editor.Annotations.ShowIfAttribute.ShowIfAttribute(string memberName, Inno.Editor.Annotations.InspectorCondition condition)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L289) | Creates a named condition that does not require an expected value.
`memberName`: Sibling field or property name.
`condition`: Comparison applied to the sibling value. | — | — | +| [`public string Inno.Editor.Annotations.ShowIfAttribute.memberName { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L311) | Gets the sibling member name. | — | — | +| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.ShowIfAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L316) | Gets the comparison. | — | — | +| [`public object? Inno.Editor.Annotations.ShowIfAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L321) | Gets the optional expected constant. | — | — | +| [`public sealed class Inno.Editor.Annotations.HideIfAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L328) | Hides the annotated Inspector property when a sibling member satisfies a condition. | — | — | +| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L336) | Creates a truthiness condition.
`memberName`: Sibling field or property name. | — | — | +| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName, object? expectedValue)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L350) | Creates an equality condition.
`memberName`: Sibling field or property name.
`expectedValue`: Compile-time value that hides the property. | — | — | +| [`public Inno.Editor.Annotations.HideIfAttribute.HideIfAttribute(string memberName, Inno.Editor.Annotations.InspectorCondition condition)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L367) | Creates a named condition that does not require an expected value.
`memberName`: Sibling field or property name.
`condition`: Comparison applied to the sibling value. | — | — | +| [`public string Inno.Editor.Annotations.HideIfAttribute.memberName { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L389) | Gets the sibling member name. | — | — | +| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.HideIfAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L394) | Gets the comparison. | — | — | +| [`public object? Inno.Editor.Annotations.HideIfAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L399) | Gets the optional expected constant. | — | — | +| [`public enum Inno.Editor.Annotations.InspectorMessageType : int`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L405) | Selects the visual severity of an Inspector help message. | — | — | +| [`Inno.Editor.Annotations.InspectorMessageType.Info = 0`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L410) | Neutral explanatory information. | — | — | +| [`Inno.Editor.Annotations.InspectorMessageType.Warning = 1`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L415) | A non-blocking configuration warning. | — | — | +| [`Inno.Editor.Annotations.InspectorMessageType.Error = 2`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L420) | An invalid or unusable configuration. | — | — | +| [`public sealed class Inno.Editor.Annotations.HelpBoxAttribute : Inno.Editor.Annotations.InspectorPresentationAttribute`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L427) | Draws a wrapped explanatory message before the annotated Inspector property. | — | — | +| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L438) | Creates an Inspector help message.
`text`: Non-empty message text.
`messageType`: Visual severity. | — | — | +| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, string conditionMember, Inno.Editor.Annotations.InspectorCondition condition, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L461) | Creates an Inspector help message shown when a sibling satisfies a named condition.
`text`: Non-empty message text.
`conditionMember`: Sibling field or property name.
`condition`: Comparison applied to the sibling value.
`messageType`: Visual severity. | — | — | +| [`public Inno.Editor.Annotations.HelpBoxAttribute.HelpBoxAttribute(string text, string conditionMember, object? expectedValue, Inno.Editor.Annotations.InspectorMessageType messageType = Inno.Editor.Annotations.InspectorMessageType.Info)`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L486) | Creates an Inspector help message shown when a sibling equals a compile-time value.
`text`: Non-empty message text.
`conditionMember`: Sibling field or property name.
`expectedValue`: The expected value consumed by help box attribute; ownership remains with the caller unless explicitly stated otherwise.
`messageType`: Visual severity. | — | — | +| [`public string Inno.Editor.Annotations.HelpBoxAttribute.text { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L514) | Gets the message text. | — | — | +| [`public Inno.Editor.Annotations.InspectorMessageType Inno.Editor.Annotations.HelpBoxAttribute.messageType { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L519) | Gets the visual severity. | — | — | +| [`public string Inno.Editor.Annotations.HelpBoxAttribute.conditionMember { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L524) | Gets the optional sibling member controlling message visibility. | — | — | +| [`public Inno.Editor.Annotations.InspectorCondition Inno.Editor.Annotations.HelpBoxAttribute.condition { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L529) | Gets the optional sibling comparison. | — | — | +| [`public object? Inno.Editor.Annotations.HelpBoxAttribute.expectedValue { get; }`](../../src/composition/editor/contracts/Inno.Editor.Annotations/InspectorPresentationAttributes.cs#L534) | Gets the optional expected constant. | — | — | ## 项目依赖 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Assets.ImGui.md b/docs/editor/Inno.Editor.Assets.ImGui.md new file mode 100644 index 000000000..8a918bd24 --- /dev/null +++ b/docs/editor/Inno.Editor.Assets.ImGui.md @@ -0,0 +1,113 @@ +# Inno.Editor.Assets.ImGui + +[Editor 索引](README.md) · [Wiki 首页](../README.md) · [UI 架构](../architecture/EDITOR_UI_ARCHITECTURE.md) + +## 职责与边界 + +Asset 浏览呈现与图标发现。依赖 Assets 编辑 feature、Inspection、Settings 和 ImGui;File Browser 消费此呈现模块。编辑操作、文件归档及 History 不属于此程序集。 + +## 依赖与初始化 + +产品通过 `EditorProduct.props` 显式部署此程序集。由当前 TypeRegistry/Editor Registry 发现声明并在候选成功后切换;不扫描目录、不建立独立注册表或全局状态。 + +## API 与扩展点 + +AssetBrowserState、AssetBrowserRoot、AssetIconAttribute 和 AssetIconRegistry 提供当前浏览位置、来源、图标声明与查询。AssetBrowserModule 组合图标及无界面的 AssetEditingModule;BasicAssetInspectionDrawer 只做显示。 + +## 工作流 + +使用逻辑 namespace `InnoEditor.Assets` 声明 AssetIconAttribute;修改源内容时调用 Assets feature,不能直接写安装包。 + +## 错误、生命周期与热重载 + +控件只在当前 EditorUiSession 的 owner thread、作用域与原生上下文中使用。跨帧只保留稳定 ID 和中立状态;Missing、只读和未赋值状态保持区别。手势失败保留 History 所有权和诊断;扩展在所属 generation 退休,不保留插件 Type、delegate 或实例。 + +所有公开签名的当前映射见本页 API 清单;内部实现不构成稳定扩展契约。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public sealed class Inno.Editor.Assets.ImGui.AssetIconAttribute : System.Attribute`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L14) | Declares the icon used by the Asset Browser for an imported asset type or source extension. | — | — | +| [`public Inno.Editor.Assets.ImGui.AssetIconAttribute.AssetIconAttribute(System.Type assetType, string icon, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L37) | Creates an icon declaration using a glyph from the Editor icon catalog.
`assetType`: The imported asset type represented by the icon.
`icon`: The Editor icon glyph to render.
`useForChildren`: Whether the declaration may also represent derived asset types.
`priority`: The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Assets.ImGui.AssetIconAttribute.AssetIconAttribute(string extension, string icon, int priority = 0)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L65) | Creates an icon declaration for files ending with a source extension.
`extension`: The simple or compound file extension. A leading period is optional and matching is case-insensitive.
`icon`: The Editor icon glyph to render.
`priority`: The tie-breaking priority after extension specificity. | — | `System.ArgumentException`: Thrown when is empty or contains a path separator, or when is empty. | +| [`public System.Type? Inno.Editor.Assets.ImGui.AssetIconAttribute.assetType { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L79) | Gets the imported asset type represented by this declaration, or for an extension declaration. | — | — | +| [`public string? Inno.Editor.Assets.ImGui.AssetIconAttribute.extension { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L84) | Gets the normalized source extension represented by this declaration, or for a type declaration. | — | — | +| [`public string Inno.Editor.Assets.ImGui.AssetIconAttribute.icon { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L88) | Gets the Editor icon glyph to render. | — | — | +| [`public bool Inno.Editor.Assets.ImGui.AssetIconAttribute.useForChildren { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L93) | Gets whether a type declaration may also represent derived asset types. Extension declarations always return . | — | — | +| [`public int Inno.Editor.Assets.ImGui.AssetIconAttribute.priority { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs#L97) | Gets the tie-breaking priority after target specificity. | — | — | +| [`public sealed class Inno.Editor.Assets.ImGui.AssetBrowserModule : Inno.Editor.Core.EditorModule, Inno.Editor.Inspection.IInspectionIconProvider`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L17) | Owns asset navigation and icon presentation independently from source mutations. | — | — | +| [`public Inno.Editor.Assets.ImGui.AssetBrowserModule.AssetBrowserModule(Inno.Editor.Assets.AssetEditingModule edits, Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Settings.EditorSettings settings, Inno.Extensibility.Types.TypeCatalog types)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L40) | Creates the asset presentation module for the current editor generation.
`edits`: The independent authoritative asset editing service.
`interactions`: The editor selection and action owner.
`settings`: The editor settings that supply semantic icon choices.
`types`: The active extension type catalog. | — | `System.ArgumentNullException`: A required owner is null. | +| [`public Inno.Editor.Assets.AssetEditingModule Inno.Editor.Assets.ImGui.AssetBrowserModule.edits { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L55) | Gets the source editing service; this module does not implement source transactions. | — | — | +| [`public Inno.Editor.Assets.ImGui.AssetBrowserState Inno.Editor.Assets.ImGui.AssetBrowserModule.browser { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L59) | Gets navigation state; object selection belongs to the shared interaction owner. | — | — | +| [`public string Inno.Editor.Assets.ImGui.AssetBrowserModule.folderIcon { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L63) | Gets the configured folder glyph used by asset navigation. | — | — | +| [`protected override void Inno.Editor.Assets.ImGui.AssetBrowserModule.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L71) | Captures an immutable snapshot of the current observable state.
`state`: The lifecycle or domain state applied by this operation. | — | — | +| [`protected override void Inno.Editor.Assets.ImGui.AssetBrowserModule.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L84) | Restores the supplied snapshot while preserving current invariants.
`state`: The lifecycle or domain state applied by this operation. | — | — | +| [`public string Inno.Editor.Assets.ImGui.AssetBrowserModule.GetIcon(Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L127) | Resolves the presentation icon registered for an asset type or source extension.
`entry`: The source entry whose presentation icon should be resolved. | The most specific registered icon, or the built-in directory or file icon when no registration matches the entry. | `System.ArgumentNullException`: Thrown when is . | +| [`protected override void Inno.Editor.Assets.ImGui.AssetBrowserModule.OnDispose()`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs#L142) | Releases resources owned by this module after it has stopped and left the active extension generation. | — | — | +| [`public enum Inno.Editor.Assets.ImGui.AssetBrowserRoot : int`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L14) | Identifies the authoring or installed-content root displayed by the Asset Browser. | — | — | +| [`Inno.Editor.Assets.ImGui.AssetBrowserRoot.Assets = 0`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L19) | The writable project Assets authoring root. | — | — | +| [`Inno.Editor.Assets.ImGui.AssetBrowserRoot.Plugins = 1`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L23) | The read-only Plugins installation root. | — | — | +| [`public sealed class Inno.Editor.Assets.ImGui.AssetBrowserState`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L29) | Stores asset browser navigation independently from global object selection. | — | — | +| [`public Inno.Editor.Assets.ImGui.AssetBrowserRoot Inno.Editor.Assets.ImGui.AssetBrowserState.root { get; private set; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L58) | Gets the root currently displayed by the Asset Browser. | — | — | +| [`public string Inno.Editor.Assets.ImGui.AssetBrowserState.currentDirectory { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L62) | Gets the current directory inside . An empty value identifies that root's overview. | — | — | +| [`public string Inno.Editor.Assets.ImGui.AssetBrowserState.projectDirectory { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L66) | Gets the most recently visited writable project directory, independently of the displayed root. | — | — | +| [`public string? Inno.Editor.Assets.ImGui.AssetBrowserState.GetSelectedPath(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L80) | Gets the selected asset path when the editor-wide target belongs to the Asset Browser.
`context`: The shared editor context containing the global selection. | The normalized source-relative path, or when another target type is selected. | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.Assets.ImGui.AssetBrowserState.IsSelected(Inno.Editor.Core.EditorContext context, Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L98) | Checks whether the source entry belongs to the current asset selection.
`context`: The current editor callback context.
`entry`: The indexed source identity to inspect. | True when the entry is selected; otherwise false. | — | +| [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.ToggleSelection(Inno.Editor.Core.EditorContext context, Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L111) | Adds or removes an asset from the current selection without retaining runtime asset objects.
`context`: The current editor callback context.
`entry`: The indexed source identity to toggle. | — | — | +| [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.SetRoot(Inno.Editor.Assets.ImGui.AssetBrowserRoot value)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L124) | Switches the displayed root while preserving the last directory visited in each root.
`value`: The authoring or installed-content root to display. | — | — | +| [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.SetCurrentDirectory(string relativePath)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L131) | Sets the current Asset Browser directory and infers its root from the isolated source identity.
`relativePath`: The isolated source-relative directory path, or an empty value for the currently displayed root. | — | — | +| [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.Select(Inno.Editor.Core.EditorContext context, string? relativePath)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L158) | Selects an asset path through the editor-wide selection state, or clears Asset selection.
`context`: The shared editor context whose selection should be updated.
`relativePath`: The source-relative path to select, or an empty value to clear selection. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public string Inno.Editor.Assets.ImGui.AssetBrowserState.GetDirectory(Inno.Editor.Assets.ImGui.AssetBrowserRoot targetRoot)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L184) | Gets the last visited isolated directory for a navigation root.
`targetRoot`: The root whose independent navigation position is requested. | The source-relative directory, or an empty string for the root. | — | +| [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.SetLocation(Inno.Editor.Assets.ImGui.AssetBrowserRoot targetRoot, string directory)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L207) | Changes the displayed source root and its isolated navigation directory.
`targetRoot`: The navigation root that owns the requested directory.
`directory`: The isolated source-relative directory; empty selects the root. | — | `System.ArgumentException`: The directory belongs to a different source root. | + +## 项目依赖 + +| 工程 | 边界 | +| --- | --- | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Editor.Assets` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Inspection` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.Editor.Settings` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Assets.md b/docs/editor/Inno.Editor.Assets.md index 315e4ce0f..8d22acb06 100644 --- a/docs/editor/Inno.Editor.Assets.md +++ b/docs/editor/Inno.Editor.Assets.md @@ -10,7 +10,7 @@ ## Asset 创建扩展 -`AssetCreationTemplate` 的派生关系是发现协议;`AssetCreationMenuAttribute` 只携带无法从 `AssetObject` 推导的稳定模板 ID、菜单路径、扩展名、默认文件名和排序。File Browser Registry 构造并持有全部模板,因此模板本身不重复声明实例 ID。一个插件新增资产类型时不需要修改 File Browser 中央分支: +`AssetCreationTemplate` 的派生关系是发现协议;`AssetCreationMenuAttribute` 只携带无法从 `AssetObject` 推导的稳定模板 ID、菜单路径、扩展名、默认文件名和排序。Assets feature Registry 构造并持有全部模板,因此模板本身不重复声明实例 ID。一个插件新增资产类型时不需要修改 File Browser 中央分支: ```csharp using InnoEditor.Assets; @@ -50,9 +50,9 @@ Feature 必须提供相同 historyKind 的 EditorHistoryHandler;不能保存 | `Start()` | 注册共享文档 Provider;重复启动明确失败 | | `Open(path)` | 打开来源与身份,不要求导入成功;不显示第二个 Inspector | | `Read(assetId)` | 解码独立值;引用的 canonical 资产仍是只读输入 | -| `Replace(assetId, candidate, finishGesture)` | 修改草稿;手势结束记录一次 History,不写源 | +| `Replace(assetId, candidate, mergeKey)` | 修改草稿;手势结束记录一次 History,不写源 | | `Commit(assetId)` | 结束手势,不保存 | -| `ReplaceMany(candidates, finishGesture)` / `CommitMany(ids)` | 多文档共用一次 History 事务;不是跨文件原子保存 | +| `ReplaceMany(candidates, mergeKey)` / `ReplaceMany(ids)` | 多文档共用一次 History 事务;不是跨文件原子保存 | | `GetDraft(assetId)` | 获取不含 Asset/Type/delegate 的中立状态对象 | | `TouchInspection(assetId)` | 标记当前帧仍参与编辑;离开 Inspector 后完成遗留手势 | | `ValidateHistory(change, direction)` / `ApplyHistory(change, direction)` | Feature 的注册 Handler 路由公共 History 协议,不独立移动历史栈 | @@ -103,23 +103,19 @@ Store 不保留插件资产实例、设置实例或 Type;消费者在每次绘 模板使用默认 `Encode`,经所属 source 的完整 SerializationContext 保存结构化资产。对应扩展还须提供使用同一格式的 importer;天然文本源才应覆盖编码,不能用自定义文本或 JSON 旁路保存结构化资产。 -## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 -### `Inno.Editor.Assets.AssetCreationContext` +## 共享编辑入口 - - - +AssetEditingModule 拥有创建、删除、重命名、移动、只读校验与 source History;AssetImportSettingsEdits 拥有导入设置;AssetSampleImportModule 复用正式 Sample import。File Browser 只发出交互。 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Assets.AssetCreationContext.AssetCreationContext(Inno.Assets.Pipeline.AssetSourceStore sources)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L180) | Creates a context over the source serializer owned by the active Asset pipeline.
`sources`:Source serializer carrying the active reference context. | — | — | -| [`public byte[] Inno.Editor.Assets.AssetCreationContext.EncodeNative(Inno.Assets.AssetObject asset)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L191) | Encodes an Asset value through the current native structured source serializer.
返回:Complete native source bytes, including the concrete serialized type identity.
`asset`:Detached Asset value to encode. | — | — | -| [`public sealed class Inno.Editor.Assets.AssetCreationContext`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L169) | Provides source encoding services to one detached Asset creation template invocation. | — | — | +AssetDraftDocuments 的 Replace/ReplaceMany 立即记录中立 bytes,不延迟到下一帧或失焦。连续输入由 EditorEditGesture 开启事务;mergeKey 只标识当前逻辑值手势。Cancel 通过同一个 History 回滚。Save 不再承担补记 History 的职责。 -### `Inno.Editor.Assets.AssetCreationMenuAttribute` +批量删除和移动先验证整个选择,再将被选父目录覆盖的子条目归一化,最后用现有 History transaction 原子执行。后续失败自动恢复先前步骤。安装源始终只读。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -130,89 +126,32 @@ Store 不保留插件资产实例、设置实例或 Type;消费者在每次绘 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Assets.AssetCreationMenuAttribute.AssetCreationMenuAttribute(string id, string menuPath, string extension, string defaultName, int groupOrder = 0, int itemOrder = 0, bool separatorBeforeGroup = false)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L42) | Creates one asset creation menu declaration.
`id`:Globally stable template identifier.
`menuPath`:Slash-delimited path below the File Browser Create menu.
`extension`:Native source extension, including its leading dot.
`defaultName`:Default source name without an extension or numeric suffix.
`groupOrder`:Stable order of the top-level category below Create.
`itemOrder`:Stable order of the leaf within its immediate category.
`separatorBeforeGroup`:Whether the top-level category starts after a separator. | — | `System.ArgumentException`:Thrown when an identifier, path, source name, or native extension is invalid. | -| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.defaultName { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L99) | Gets the default source name without an extension or numeric suffix. | — | — | -| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.extension { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L94) | Gets the native source extension, including its leading dot. | — | — | -| [`public int Inno.Editor.Assets.AssetCreationMenuAttribute.groupOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L104) | Gets the stable order of the top-level category below Create. | — | — | -| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L84) | Gets the globally stable template identifier. | — | — | -| [`public int Inno.Editor.Assets.AssetCreationMenuAttribute.itemOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L109) | Gets the stable order of the leaf within its immediate category. | — | — | -| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.menuPath { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L89) | Gets the slash-delimited path below the File Browser Create menu. | — | — | -| [`public bool Inno.Editor.Assets.AssetCreationMenuAttribute.separatorBeforeGroup { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L114) | Gets whether the top-level category starts after a separator. | — | — | -| [`public sealed class Inno.Editor.Assets.AssetCreationMenuAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L13) | Declares the source name, extension, and menu placement that cannot be inferred from an Inno.Assets.AssetObject type alone. | — | — | - -### `Inno.Editor.Assets.AssetCreationTemplate` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Assets.AssetCreationTemplate.AssetCreationTemplate()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L124) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract Inno.Assets.AssetObject Inno.Editor.Assets.AssetCreationTemplate.Create()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L137) | Creates a detached default value that has not been imported or published.
返回:A new asset value owned by the caller. | — | — | -| [`public virtual byte[] Inno.Editor.Assets.AssetCreationTemplate.Encode(Inno.Editor.Assets.AssetCreationContext context, Inno.Assets.AssetObject asset)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L156) | Encodes the detached initial value into the source representation owned by this template.
返回:Complete source bytes ready for an atomic File Browser create operation.
`context`:Creation services scoped to the current Asset source pipeline.
`asset`:The exact value returned by Inno.Editor.Assets.AssetCreationTemplate.Create. | The default implementation uses native structured Asset serialization. Templates paired with a textual or custom binary importer can override this method without changing the File Browser or registering a second creation protocol. | — | -| [`public abstract System.Type Inno.Editor.Assets.AssetCreationTemplate.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L129) | Gets the concrete asset type produced by this template. | — | — | -| [`public abstract class Inno.Editor.Assets.AssetCreationTemplate`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L124) | Produces the detached initial value for one authorable native asset source. | Derivation is the discovery contract. Inno.Editor.Assets.AssetCreationMenuAttribute supplies only source-format and presentation data that the asset inheritance hierarchy cannot express. | — | - -### `Inno.Editor.Assets.AssetCreationTemplate`1` - + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Assets.AssetCreationTemplate.AssetCreationTemplate()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L204) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public override Inno.Assets.AssetObject Inno.Editor.Assets.AssetCreationTemplate.Create()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L218) | Creates a value using this implementation's validated inputs.
返回:The validated asset object that represents the completed operation. | — | — | -| [`protected virtual TAsset Inno.Editor.Assets.AssetCreationTemplate.CreateAsset()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L226) | Creates the initial detached value, allowing specialized deterministic defaults.
返回:A new asset value owned by the caller. | — | — | -| [`public override sealed System.Type Inno.Editor.Assets.AssetCreationTemplate.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L210) | Gets the asset type created by this Editor template. | — | — | -| [`public abstract class Inno.Editor.Assets.AssetCreationTemplate where TAsset : Inno.Assets.AssetObject, new() : Inno.Editor.Assets.AssetCreationTemplate`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L204) | Creates a default-constructed native source for an authorable asset type. | — | — | - -### `Inno.Editor.Assets.AssetDraftDocuments`1` - - - - - - + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Assets.AssetDraftDocuments.AssetDraftDocuments(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Editor.Interactions.EditorInteractions interactions, string providerId, string historyKind, string extension, string label)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L61) | Creates a feature-owned draft store; call Start after document services become available.
`assets`:Owner of source identities, serialization and import.
`serialization`:Host registry for neutral History and recovery records.
`interactions`:Shared documents and History services.
`providerId`:Stable unique document provider identifier.
`historyKind`:Stable protocol handled by the feature's registered History handler.
`extension`:Native source extension including its leading dot.
`label`:Human-readable singular asset kind. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.ApplyHistory(Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L280) | Applies a previously validated neutral record; failed recovery leaves the draft unchanged.
`change`:Record owned by the shared History service.
`direction`:Requested traversal direction. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.Commit(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L158) | Finishes the active edit gesture without saving or applying its draft.
`assetId`:Open asset identity. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.CommitMany(System.Collections.Generic.IEnumerable assetIds)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L204) | Commits all selected draft samples in a single shared history transaction.
`assetIds`:Open asset identities belonging to this gesture. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.Dispose()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L481) | Preserves unsaved recovery and unregisters the current feature provider. | — | — | -| [`public Inno.Editor.Assets.AssetDraftDocuments.Draft Inno.Editor.Assets.AssetDraftDocuments.GetDraft(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L94) | Gets host-owned neutral presentation state for an open document.
返回:Read-only presentation state; it contains no live asset or extension objects.
`assetId`:Persistent source identity. | — | — | -| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L113) | Opens a native asset source in the shared document service without revealing a second Inspector.
返回:The persistent asset identity used for subsequent draft operations.
`path`:Source path, including sources with failed imports. | — | — | -| [`public TAsset Inno.Editor.Assets.AssetDraftDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L132) | Reads a detached current-generation value; callers must never mutate its referenced canonical assets.
返回:A detached editable asset whose changes have not been published.
`assetId`:Open asset identity. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.Replace(System.Guid assetId, TAsset candidate, bool finishGesture = true)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L146) | Updates the open draft through shared History without saving or publishing a runtime asset.
`assetId`:Open asset identity.
`candidate`:Detached edited asset.
`finishGesture`:True completes one history transaction; false continues the same active gesture. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.ReplaceMany(System.Collections.Generic.IReadOnlyDictionary candidates, bool finishGesture = true)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L169) | Edits compatible selected drafts as one gesture without saving any source.
`candidates`:Detached candidate assets indexed by their open asset identities.
`finishGesture`:Whether this sample completes the shared gesture. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.Start()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L407) | Registers the feature provider with the headless document ownership service. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.TouchInspection(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L102) | Marks a document as inspected in this frame so a lost gesture can be completed.
`assetId`:Persistent source identity. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.Update()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L417) | Completes abandoned gestures, imports explicit saves and reconciles source changes. | — | — | -| [`public void Inno.Editor.Assets.AssetDraftDocuments.ValidateHistory(Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L264) | Validates a feature's neutral History record without changing the draft.
`change`:Record owned by the shared History service.
`direction`:Requested traversal direction. | — | — | -| [`public sealed class Inno.Editor.Assets.AssetDraftDocuments where TAsset : Inno.Assets.AssetObject : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L20) | Owns detached native asset drafts with shared document, History, recovery and explicit-save semantics. | — | — | - -### `Inno.Editor.Assets.AssetDraftDocuments`1.Draft` - @@ -220,26 +159,221 @@ Store 不保留插件资产实例、设置实例或 Type;消费者在每次绘 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Draft.documentId { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L595) | Gets the shared document identity used for Save/Revert and close confirmation. | — | — | -| [`public string Inno.Editor.Assets.AssetDraftDocuments.Draft.error { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L607) | Gets the latest source, save or import diagnostic. | — | — | -| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Draft.id { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L591) | Gets the persistent source identity. | — | — | -| [`public bool Inno.Editor.Assets.AssetDraftDocuments.Draft.isDirty { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L611) | Gets whether detached bytes differ from the saved baseline. | — | — | -| [`public Inno.Assets.AssetPath Inno.Editor.Assets.AssetDraftDocuments.Draft.path { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L599) | Gets the current resolved source path. | — | — | -| [`public bool Inno.Editor.Assets.AssetDraftDocuments.Draft.readOnly { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L603) | Gets whether this source is installed read-only content. | — | — | -| [`public sealed class Inno.Editor.Assets.AssetDraftDocuments.Draft`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L564) | Contains neutral draft status; only the owning store can change it. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetCreationMenuAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L13) | Declares the source name, extension, and menu placement that cannot be inferred from an type alone. | — | — | +| [`public Inno.Editor.Assets.AssetCreationMenuAttribute.AssetCreationMenuAttribute(string id, string menuPath, string extension, string defaultName, int groupOrder = 0, int itemOrder = 0, bool separatorBeforeGroup = false)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L42) | Creates one asset creation menu declaration.
`id`: Globally stable template identifier.
`menuPath`: Slash-delimited path below the File Browser Create menu.
`extension`: Native source extension, including its leading dot.
`defaultName`: Default source name without an extension or numeric suffix.
`groupOrder`: Stable order of the top-level category below Create.
`itemOrder`: Stable order of the leaf within its immediate category.
`separatorBeforeGroup`: Whether the top-level category starts after a separator. | — | `System.ArgumentException`: Thrown when an identifier, path, source name, or native extension is invalid. | +| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L84) | Gets the globally stable template identifier. | — | — | +| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.menuPath { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L89) | Gets the slash-delimited path below the File Browser Create menu. | — | — | +| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.extension { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L94) | Gets the native source extension, including its leading dot. | — | — | +| [`public string Inno.Editor.Assets.AssetCreationMenuAttribute.defaultName { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L99) | Gets the default source name without an extension or numeric suffix. | — | — | +| [`public int Inno.Editor.Assets.AssetCreationMenuAttribute.groupOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L104) | Gets the stable order of the top-level category below Create. | — | — | +| [`public int Inno.Editor.Assets.AssetCreationMenuAttribute.itemOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L109) | Gets the stable order of the leaf within its immediate category. | — | — | +| [`public bool Inno.Editor.Assets.AssetCreationMenuAttribute.separatorBeforeGroup { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L114) | Gets whether the top-level category starts after a separator. | — | — | +| [`public abstract class Inno.Editor.Assets.AssetCreationTemplate`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L124) | Produces the detached initial value for one authorable native asset source. | — | — | +| [`public abstract System.Type Inno.Editor.Assets.AssetCreationTemplate.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L129) | Gets the concrete asset type produced by this template. | — | — | +| [`public abstract Inno.Assets.AssetObject Inno.Editor.Assets.AssetCreationTemplate.Create()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L137) | Creates a detached default value that has not been imported or published. | A new asset value owned by the caller. | — | +| [`public virtual byte[] Inno.Editor.Assets.AssetCreationTemplate.Encode(Inno.Editor.Assets.AssetCreationContext context, Inno.Assets.AssetObject asset)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L156) | Encodes the detached initial value into the source representation owned by this template.
`context`: Creation services scoped to the current Asset source pipeline.
`asset`: The exact value returned by . | Complete source bytes ready for an atomic File Browser create operation. | — | +| [`protected Inno.Editor.Assets.AssetCreationTemplate.AssetCreationTemplate()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L124) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetCreationContext`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L169) | Provides source encoding services to one detached Asset creation template invocation. | — | — | +| [`public Inno.Editor.Assets.AssetCreationContext.AssetCreationContext(Inno.Assets.Pipeline.AssetSourceStore sources)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L180) | Creates a context over the source serializer owned by the active Asset pipeline.
`sources`: Source serializer carrying the active reference context. | — | — | +| [`public byte[] Inno.Editor.Assets.AssetCreationContext.EncodeNative(Inno.Assets.AssetObject asset)`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L191) | Encodes an Asset value through the current native structured source serializer.
`asset`: Detached Asset value to encode. | Complete native source bytes, including the concrete serialized type identity. | — | +| [`public abstract class Inno.Editor.Assets.AssetCreationTemplate where TAsset : Inno.Assets.AssetObject, new() : Inno.Editor.Assets.AssetCreationTemplate`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L204) | Creates a default-constructed native source for an authorable asset type.
`TAsset`: Concrete serializable asset type. | — | — | +| [`public override sealed System.Type Inno.Editor.Assets.AssetCreationTemplate.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L210) | Gets the asset type created by this Editor template. | — | — | +| [`public override Inno.Assets.AssetObject Inno.Editor.Assets.AssetCreationTemplate.Create()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L218) | Creates a value using this implementation's validated inputs. | The validated asset object that represents the completed operation. | — | +| [`protected virtual TAsset Inno.Editor.Assets.AssetCreationTemplate.CreateAsset()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L226) | Creates the initial detached value, allowing specialized deterministic defaults. | A new asset value owned by the caller. | — | +| [`protected Inno.Editor.Assets.AssetCreationTemplate.AssetCreationTemplate()`](../../src/composition/editor/features/Inno.Editor.Assets/Creation/AssetCreationTemplate.cs#L204) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetDraftDocuments where TAsset : Inno.Assets.AssetObject : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L19) | Owns detached native asset drafts with shared document, History, recovery and explicit-save semantics.
`TAsset`: Native source type edited by the consuming feature. | — | — | +| [`public Inno.Editor.Assets.AssetDraftDocuments.AssetDraftDocuments(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Editor.Interactions.EditorInteractions interactions, string providerId, string historyKind, string extension, string label)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L57) | Creates a feature-owned draft store; call Start after document services become available.
`assets`: Owner of source identities, serialization and import.
`serialization`: Host registry for neutral History and recovery records.
`interactions`: Shared documents and History services.
`providerId`: Stable unique document provider identifier.
`historyKind`: Stable protocol handled by the feature's registered History handler.
`extension`: Native source extension including its leading dot.
`label`: Human-readable singular asset kind. | — | — | +| [`public Inno.Editor.Assets.AssetDraftDocuments.Draft Inno.Editor.Assets.AssetDraftDocuments.GetDraft(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L95) | Gets host-owned neutral presentation state for an open document.
`assetId`: Persistent source identity. | Read-only presentation state; it contains no live asset or extension objects. | — | +| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L105) | Opens a native asset source in the shared document service without revealing a second Inspector.
`path`: Source path, including sources with failed imports. | The persistent asset identity used for subsequent draft operations. | — | +| [`public TAsset Inno.Editor.Assets.AssetDraftDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L124) | Reads a detached current-generation value; callers must never mutate its referenced canonical assets.
`assetId`: Open asset identity. | A detached editable asset whose changes have not been published. | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.Replace(System.Guid assetId, TAsset candidate, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L137) | Updates the open draft through shared History without saving or publishing a runtime asset.
`assetId`: Open asset identity.
`candidate`: Detached edited asset.
`mergeKey`: The current gesture key, or null for an independent discrete edit. | — | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.ReplaceMany(System.Collections.Generic.IReadOnlyDictionary candidates, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L157) | Records a fully validated selection sample atomically in the shared History.
`candidates`: Detached candidates keyed by their persistent source identity.
`mergeKey`: The owning gesture key, or null for a discrete edit. | — | — | +| [`public bool Inno.Editor.Assets.AssetDraftDocuments.TryMergeHistory(Inno.Editor.Interactions.EditorHistoryChange older, Inno.Editor.Interactions.EditorHistoryChange newer, out Inno.Editor.Interactions.EditorHistoryChange? merged)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L221) | Combines adjacent samples of the same document and gesture without retaining live assets.
`older`: The preceding owned record.
`newer`: The subsequent owned record.
`merged`: Receives a new independent payload when the records form a contiguous edit. | True when both records describe the same continuous value change. | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.ValidateHistory(Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L252) | Validates a feature's neutral History record without changing the draft.
`change`: Record owned by the shared History service.
`direction`: Requested traversal direction. | — | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.ApplyHistory(Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L270) | Applies a previously validated neutral record; failed recovery leaves the draft unchanged.
`change`: Record owned by the shared History service.
`direction`: Requested traversal direction. | — | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.Start()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L402) | Registers the feature provider with the headless document ownership service. | — | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.Update()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L412) | Imports explicit saves and reconciles source changes without owning UI gesture lifetimes. | — | — | +| [`public void Inno.Editor.Assets.AssetDraftDocuments.Dispose()`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L478) | Preserves unsaved recovery and unregisters the current feature provider. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetDraftDocuments.Draft`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L560) | Contains neutral draft status; only the owning store can change it. | — | — | +| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Draft.id { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L586) | Gets the persistent source identity. | — | — | +| [`public System.Guid Inno.Editor.Assets.AssetDraftDocuments.Draft.documentId { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L590) | Gets the shared document identity used for Save/Revert and close confirmation. | — | — | +| [`public Inno.Assets.AssetPath Inno.Editor.Assets.AssetDraftDocuments.Draft.path { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L594) | Gets the current resolved source path. | — | — | +| [`public bool Inno.Editor.Assets.AssetDraftDocuments.Draft.readOnly { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L598) | Gets whether this source is installed read-only content. | — | — | +| [`public string Inno.Editor.Assets.AssetDraftDocuments.Draft.error { get; internal set; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L602) | Gets the latest source, save or import diagnostic. | — | — | +| [`public bool Inno.Editor.Assets.AssetDraftDocuments.Draft.isDirty { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs#L606) | Gets whether detached bytes differ from the saved baseline. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetEditingModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs#L13) | Handles asset creation actions in the Editor file browser. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileEntry Inno.Editor.Assets.AssetEditingModule.CreateDirectory(Inno.Assets.AssetPath parent)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs#L27) | Creates a uniquely named directory and records its removal and restoration in shared History.
`parent`: An existing writable source directory. | The indexed identity of the newly created directory. | `System.InvalidOperationException`: The source is read-only, unavailable, or cannot retain the History entry. | +| [`public Inno.Assets.Pipeline.AssetFileEntry Inno.Editor.Assets.AssetEditingModule.CreateSource(Inno.Assets.AssetPath path, System.ReadOnlySpan bytes)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs#L75) | Creates a native or ordinary-text asset source as one recoverable shared-history operation.
`path`: A new file in an existing writable asset directory.
`bytes`: Complete source bytes supplied by the feature's native writer. | The indexed source identity, even when its first import reports a source error. | `System.IO.IOException`: The source already exists, has no existing parent, or cannot be installed.
`System.InvalidOperationException`: The source mount is unavailable/read-only or history cannot retain the change. | +| [`public Inno.Editor.Assets.AssetEditingModule.AssetEditingModule(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Assets.Pipeline.AssetPipeline pipeline, Inno.Plugins.Authoring.PluginEnvironment plugins, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L50) | Creates the asset editing service for one authoring generation.
`interactions`: The active editor interaction entry point.
`pipeline`: The isolated authoring asset pipeline used by browser operations.
`plugins`: The active Plugin catalog that authoritatively identifies Plugin-owned source mounts.
`types`: The active type catalog used to build extension registry snapshots.
`logs`: The application log router used for Asset Browser operation diagnostics. | — | `System.ArgumentNullException`: Thrown when any required service is . | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Assets.AssetEditingModule.interactions { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L71) | Gets the editor interaction owner used by source operations. | — | — | +| [`public Inno.Assets.Pipeline.AssetPipeline Inno.Editor.Assets.AssetEditingModule.pipeline { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L77) | Gets the authoritative authoring pipeline; mutations must use its existing ownership protocol. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Assets.AssetEditingModule.creationTemplates { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L81) | Gets the current immutable creation menu declarations without retaining template instances. | — | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.CanCreateAsset(string directory, string templateId)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L96) | Checks that a creation template and writable destination are available.
`directory`: The source-relative writable destination directory.
`templateId`: The stable identity of a discovered creation template. | True when the request is accepted; otherwise false. | — | +| [`public Inno.Assets.Pipeline.AssetFileEntry Inno.Editor.Assets.AssetEditingModule.CreateAsset(string directory, string templateId)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L120) | Creates and imports a source using a discovered template and records the committed change.
`directory`: The source-relative writable destination directory.
`templateId`: The stable identity of a discovered creation template. | The committed source identity, or a validation result describing why the operation is unavailable. | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.IsPluginSource(Inno.Assets.AssetSourceId source)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L170) | Determines whether the active Plugin catalog owns a source mount.
`source`: The source mount identity to query. | True when the request is accepted; otherwise false. | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.IsPluginRoot(Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L181) | Determines whether an indexed entry is the root of an installed Plugin.
`entry`: The currently indexed source identity. | True when the request is accepted; otherwise false. | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.TryCreateContext(Inno.Editor.Core.EditorContext editor, string relativePath, out Inno.Editor.Assets.AssetEditorContext? context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L203) | Captures the currently indexed source and extension metadata for one immediate operation.
`editor`: The current editor context used only during this invocation.
`relativePath`: The isolated source-relative path to resolve.
`context`: Receives the current source snapshot, or null when the source is unavailable. | True when the request is accepted; otherwise false. | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.Open(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L230) | Opens a source using its current asset editor extension and reports rejected or failed opens.
`context`: The immutable metadata captured for this immediate source operation. | True when the request is accepted; otherwise false. | — | +| [`public Inno.Editor.Interactions.EditorValidationResult Inno.Editor.Assets.AssetEditingModule.ValidateRename(Inno.Editor.Assets.AssetEditorContext context, string newName)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L260) | Validates a requested name without modifying the source.
`context`: The immutable metadata captured for this immediate source operation.
`newName`: The requested entry name without its protected file extension. | The committed source identity, or a validation result describing why the operation is unavailable. | — | +| [`public void Inno.Editor.Assets.AssetEditingModule.Rename(Inno.Editor.Assets.AssetEditorContext context, string newName)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L278) | Renames a source atomically and records its reversible history payload.
`context`: The immutable metadata captured for this immediate source operation.
`newName`: The requested entry name without its protected file extension. | — | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.CanMoveToDirectory(string sourcePath, string targetDirectory)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L314) | Checks whether a source can move into the destination without a cycle or name collision.
`sourcePath`: The current isolated source-relative path.
`targetDirectory`: The isolated destination directory. | True when the request is accepted; otherwise false. | — | +| [`public Inno.Assets.Pipeline.AssetFileEntry Inno.Editor.Assets.AssetEditingModule.MoveToDirectoryWithHistory(string sourcePath, string targetDirectory, Inno.Editor.Interactions.IEditorHistory history)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L334) | Moves a source and records the committed mutation in the supplied history.
`sourcePath`: The current isolated source-relative path.
`targetDirectory`: The isolated destination directory.
`history`: The authoritative history that receives the committed mutation. | The committed source identity, or a validation result describing why the operation is unavailable. | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.DeleteWithHistory(Inno.Editor.Assets.AssetEditorContext asset, Inno.Editor.Interactions.IEditorHistory history)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L411) | Deletes a source with a recoverable archive and records the committed mutation.
`asset`: The captured source metadata to validate and delete.
`history`: The authoritative History receiving this mutation. | True when the request is accepted; otherwise false. | — | +| [`public void Inno.Editor.Assets.AssetEditingModule.SelectPath(string relativePath)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L462) | Selects the current source identity without retaining an indexed entry across generations.
`relativePath`: The isolated source-relative path to resolve. | — | — | +| [`public bool Inno.Editor.Assets.AssetEditingModule.TryCreateDragData(Inno.Editor.Assets.AssetEditorContext context, out Inno.Editor.Interactions.EditorDragData? data)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L481) | Creates identity-based drag data when the current extension permits dragging.
`context`: The immutable metadata captured for this immediate source operation.
`data`: Receives identity-based drag data, or null when dragging is unavailable. | True when the request is accepted; otherwise false. | — | +| [`protected override void Inno.Editor.Assets.AssetEditingModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L502) | Initializes this feature when its owning runtime becomes active.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Assets.AssetEditingModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L513) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Assets.AssetEditingModule.OnDispose()`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs#L521) | Releases resources retained by this feature after it has stopped. | — | — | +| [`public Inno.Editor.Assets.AssetOperationValidation Inno.Editor.Assets.AssetEditingModule.ValidateDeleteSelection(Inno.Editor.Core.EditorContext editor, System.Collections.Generic.IReadOnlyList targets)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs#L28) | Checks the entire source selection before a batch mutation, including extension validation and read-only ownership.
`editor`: The immediate Editor context.
`targets`: Fresh source entries in selection order. | The first rejection, or a valid result when every source can be deleted. | — | +| [`public void Inno.Editor.Assets.AssetEditingModule.DeleteSelection(Inno.Editor.Core.EditorContext editor, System.Collections.Generic.IReadOnlyList targets)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs#L68) | Deletes minimal selected source roots in one History transaction, restoring earlier deletions if a later one fails.
`editor`: The immediate Editor context.
`targets`: Fresh selected source entries; nested entries are covered by their selected directory. | — | `System.InvalidOperationException`: Validation or a mutation fails; rollback failures remain explicit. | +| [`public bool Inno.Editor.Assets.AssetEditingModule.CanMoveSelection(System.Collections.Generic.IReadOnlyList targets, string targetDirectory)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs#L99) | Validates every minimal selected source and rejects destination collisions before a batch move.
`targets`: Live source entries in selection order.
`targetDirectory`: The writable project destination. | True when every source can move to a distinct destination. | — | +| [`public void Inno.Editor.Assets.AssetEditingModule.MoveSelection(System.Collections.Generic.IReadOnlyList targets, string targetDirectory)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs#L126) | Moves selected source roots in one transaction; a later failure restores earlier moves.
`targets`: Live entries, including descendants covered by a selected directory.
`targetDirectory`: The writable project destination. | — | `System.InvalidOperationException`: Any source or destination rejects the operation. | +| [`public abstract class Inno.Editor.Assets.AssetEditor`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L7) | Customizes editor interactions for one imported asset type. | — | — | +| [`public virtual bool Inno.Editor.Assets.AssetEditor.CanOpen(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L18) | Gets whether the fallback asset editor can open the supplied source entry.
`context`: The immutable source and catalog snapshot for the operation. | when can handle the entry; otherwise, . | — | +| [`public virtual void Inno.Editor.Assets.AssetEditor.Open(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L25) | Opens the supplied asset entry when no more specific typed open action handled it.
`context`: The immutable source and catalog snapshot for the operation. | — | — | +| [`public virtual Inno.Editor.Assets.AssetOperationValidation Inno.Editor.Assets.AssetEditor.ValidateRename(Inno.Editor.Assets.AssetEditorContext context, string targetPath)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L41) | Validates a requested asset move before the AssetPipeline transaction begins.
`context`: The immutable snapshot of the source entry before the move.
`targetPath`: The normalized source-relative destination path. | A valid result when the transaction may proceed, or a diagnostic describing the rejection. | — | +| [`public virtual void Inno.Editor.Assets.AssetEditor.OnRenamed(Inno.Editor.Assets.AssetEditorContext context, string oldPath, string newPath)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L57) | Runs after an asset move transaction commits successfully.
`context`: The immutable snapshot captured before the move.
`oldPath`: The previous source-relative path.
`newPath`: The committed source-relative destination path. | — | — | +| [`public virtual Inno.Editor.Assets.AssetOperationValidation Inno.Editor.Assets.AssetEditor.ValidateDelete(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L73) | Validates a requested asset deletion before the AssetPipeline transaction begins.
`context`: The immutable snapshot of the entry that would be deleted. | A valid result when the transaction may proceed, or a diagnostic describing the rejection. | — | +| [`public virtual void Inno.Editor.Assets.AssetEditor.OnDeleted(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L80) | Runs after an asset deletion transaction commits successfully.
`context`: The immutable snapshot captured before the entry was deleted. | — | — | +| [`public virtual bool Inno.Editor.Assets.AssetEditor.CanStartDrag(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L93) | Gets whether the supplied entry can begin a managed editor drag operation.
`context`: The immutable source and catalog snapshot for the entry. | when drag data can be created; otherwise, . | — | +| [`public virtual Inno.Editor.Interactions.EditorDragData Inno.Editor.Assets.AssetEditor.CreateDragData(Inno.Editor.Assets.AssetEditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L103) | Creates the managed source, preview label, and validity predicate for an asset drag.
`context`: The immutable source and catalog snapshot for the entry. | The managed drag data published by the Asset Browser. | — | +| [`protected Inno.Editor.Assets.AssetEditor.AssetEditor()`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs#L7) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetEditorAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs#L8) | Associates an asset editor with an imported asset type. | — | — | +| [`public Inno.Editor.Assets.AssetEditorAttribute.AssetEditorAttribute(System.Type assetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs#L25) | Creates an asset-editor registration for an imported runtime asset type.
`assetType`: The imported asset type handled by the editor.
`useForChildren`: Whether the editor may also handle assignable derived asset types.
`priority`: The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public System.Type Inno.Editor.Assets.AssetEditorAttribute.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs#L38) | Gets the imported asset type handled by the editor. | — | — | +| [`public bool Inno.Editor.Assets.AssetEditorAttribute.useForChildren { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs#L42) | Gets whether derived asset types are accepted. | — | — | +| [`public int Inno.Editor.Assets.AssetEditorAttribute.priority { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs#L46) | Gets the tie-breaking priority. | — | — | +| [`public sealed class Inno.Editor.Assets.AssetEditorContext`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L10) | Provides an immutable snapshot for an asset editor operation. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Assets.AssetEditorContext.editorContext { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L66) | Gets the shared editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Assets.AssetEditorContext.interactions { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L70) | Gets the active editor interaction entry point. | — | — | +| [`public string Inno.Editor.Assets.AssetEditorContext.relativePath { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L74) | Gets the source-relative path. | — | — | +| [`public string Inno.Editor.Assets.AssetEditorContext.name { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L78) | Gets the final source path segment. | — | — | +| [`public bool Inno.Editor.Assets.AssetEditorContext.isDirectory { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L82) | Gets whether the source represents a directory. | — | — | +| [`public Inno.Assets.AssetInfo? Inno.Editor.Assets.AssetEditorContext.info { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L86) | Gets the cataloged asset information when available. | — | — | +| [`public System.Type? Inno.Editor.Assets.AssetEditorContext.assetType { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs#L90) | Gets the resolved imported asset type when available. | — | — | +| [`public readonly record struct Inno.Editor.Assets.AssetOperationValidation : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Describes whether an editor asset operation may proceed. | — | — | +| [`public Inno.Editor.Assets.AssetOperationValidation.AssetOperationValidation(bool isValid, string message)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L16) | Creates the validation result returned before an asset transaction begins.
`isValid`: Whether the requested asset operation may proceed.
`message`: The user-facing rejection diagnostic, or an empty string for a valid result. | — | — | +| [`public bool Inno.Editor.Assets.AssetOperationValidation.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L27) | Gets whether the operation may proceed. | — | — | +| [`public string Inno.Editor.Assets.AssetOperationValidation.message { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L31) | Gets the validation diagnostic. | — | — | +| [`public static Inno.Editor.Assets.AssetOperationValidation Inno.Editor.Assets.AssetOperationValidation.valid { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L35) | Gets a successful validation result. | — | — | +| [`public static Inno.Editor.Assets.AssetOperationValidation Inno.Editor.Assets.AssetOperationValidation.Invalid(string message)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L46) | Creates a failed asset-operation validation result.
`message`: The user-facing diagnostic explaining why the operation was rejected. | An invalid validation result containing the supplied diagnostic. | — | +| [`public override string Inno.Editor.Assets.AssetOperationValidation.ToString()`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Assets.AssetOperationValidation.operator !=(Inno.Editor.Assets.AssetOperationValidation left, Inno.Editor.Assets.AssetOperationValidation right)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Assets.AssetOperationValidation.operator ==(Inno.Editor.Assets.AssetOperationValidation left, Inno.Editor.Assets.AssetOperationValidation right)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Assets.AssetOperationValidation.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Assets.AssetOperationValidation.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Assets.AssetOperationValidation.Equals(Inno.Editor.Assets.AssetOperationValidation other)`](../../src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs#L5) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Assets.AssetImportSettingsEdits : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs#L15) | Applies importer configuration through the common sidecar pipeline and stable-identity Editor history. | — | — | +| [`public Inno.Editor.Assets.AssetImportSettingsEdits.AssetImportSettingsEdits(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Types.TypeCatalog types, Inno.Editor.Interactions.EditorInteractions interactions)`](../../src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs#L38) | Uses the authoring owners responsible for source identity, converter generations and shared undo.
`assets`: Authoritative asset pipeline.
`serialization`: Current owner converter registry.
`types`: Current type generation owner.
`interactions`: Shared Editor history owner. | — | — | +| [`public bool Inno.Editor.Assets.AssetImportSettingsEdits.Apply(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable settings, string expectedFingerprint)`](../../src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs#L65) | Saves one settings gesture, recording neutral before/after properties even when reimport reports an error.
`path`: Writable source asset path.
`settings`: Detached current-generation settings value.
`expectedFingerprint`: Sidecar fingerprint captured when editing began. | Whether the saved configuration reimported successfully; false does not mean the settings were discarded. | — | +| [`public sealed class Inno.Editor.Assets.AssetSampleImportModule : Inno.Editor.Core.EditorModule, System.IProgress`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L20) | Owns sample import validation, publication and rollback independently of its progress window. | — | — | +| [`public override bool Inno.Editor.Assets.AssetSampleImportModule.blocksFollowingUpdates { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L50) | Defers later modules while the import pins their generation or requires a Host restart. | — | — | +| [`public bool Inno.Editor.Assets.AssetSampleImportModule.isBusy { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L54) | Gets whether an import still owns validation or publication work. | — | — | +| [`public bool Inno.Editor.Assets.AssetSampleImportModule.canImport { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L58) | Gets whether the current authoring generation admits a new sample import. | — | — | +| [`public string Inno.Editor.Assets.AssetSampleImportModule.status { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L63) | Gets an immutable progress message captured under the progress owner lock. | — | — | +| [`public float Inno.Editor.Assets.AssetSampleImportModule.progress { get; }`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L75) | Gets the most recent validation progress fraction. | — | — | +| [`public void Inno.Editor.Assets.AssetSampleImportModule.Request(Inno.Assets.AssetPath source)`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L93) | Begins validating an installed sample before its source copy can be committed.
`source`: The installed sample root to validate and import. | — | `System.InvalidOperationException`: The authoring generation is busy or faulted. | +| [`public void Inno.Editor.Assets.AssetSampleImportModule.Cancel()`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L105) | Requests cancellation without releasing work that has not retired. | — | — | +| [`public void Inno.Editor.Assets.AssetSampleImportModule.Report(Inno.Scripting.Compiler.ScriptCompilationProgress value)`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L118) | Receives asynchronous compilation progress without accessing live Editor state.
`value`: The compiler stage and completion fraction. | — | — | +| [`protected override void Inno.Editor.Assets.AssetSampleImportModule.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L125) | Advances completed background phases and publishes the validated import at a frame safe point.
`context`: The current Editor frame and interaction services. | — | — | +| [`protected override void Inno.Editor.Assets.AssetSampleImportModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L200) | Cancels and retires pending work before the scripting and asset dependencies stop.
`context`: The stopping Editor's interaction services. | — | — | +| [`protected override void Inno.Editor.Assets.AssetSampleImportModule.OnDispose()`](../../src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs#L209) | Retries retirement without releasing dependencies still used by background work. | — | — | ## 项目依赖 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.Scripting` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Plugins.Authoring` | 公开签名依赖 | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Audio.md b/docs/editor/Inno.Editor.Audio.md index 3923ae8b9..631640b34 100644 --- a/docs/editor/Inno.Editor.Audio.md +++ b/docs/editor/Inno.Editor.Audio.md @@ -33,9 +33,7 @@ Audio 的 scope、update 与逆序释放全部由 Session Subsystem Pipeline 管 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Audio.EditorAudioHost` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -44,42 +42,38 @@ Audio 的 scope、update 与逆序释放全部由 Session Subsystem Pipeline 管 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Audio.EditorAudioHost.EditorAudioHost(Inno.Extensibility.Types.TypeCatalog types, Inno.Assets.IAssetArtifactLookup artifacts, Inno.Core.Diagnostics.DiagnosticHub diagnostics, System.Func deviceFactory, System.Func? settingsProvider = null)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L47) | Creates the Editor audio host over the active authoring artifact lookup and settings source.
`types`:Active type catalog used for provider and mixer extension generations.
`artifacts`:Authoring artifact lookup shared by Edit and isolated Play sessions.
`diagnostics`:Shared Editor diagnostic hub that owns current audio issues.
`deviceFactory`:Required host-owned backend-neutral device factory.
`settingsProvider`:Optional host-owned callback that returns the latest isolated audio project settings snapshot. | — | — | -| [`public Inno.Runtime.Contracts.IRuntimeSubsystemFactory Inno.Editor.Audio.EditorAudioHost.CreateRuntimeSubsystemFactory(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L70) | Creates the reusable feature factory used by Edit and Play runtime sessions.
返回:A factory that creates and releases one isolated audio generation per session.
`session`:The explicit session owner; it is never exposed through subsystem contracts. | — | — | -| [`public void Inno.Editor.Audio.EditorAudioHost.Dispose()`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L201) | Releases every active audio generation before their owning Editor sessions are torn down. | — | — | -| [`public System.IDisposable Inno.Editor.Audio.EditorAudioHost.EnterExecutionScope(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L147) | Binds the script-facing audio façade to one session's runtime.
返回:A strict last-in-first-out execution scope.
`session`:Session whose feature pipeline owns an active Editor audio generation. | — | — | -| [`public Inno.Audio.AudioVoiceHandle Inno.Editor.Audio.EditorAudioHost.PlayPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioClipAsset clip, Inno.Audio.AudioPlayOptions? options = null)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L164) | Starts an Editor-owned preview voice through an active Edit Mode session.
返回:The preview voice handle, initially in the preparing state.
`session`:Active Edit Mode session.
`clip`:Imported audio clip to preview.
`options`:Optional playback parameters; omitted values use engine defaults. | — | — | -| [`public bool Inno.Editor.Audio.EditorAudioHost.StopPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioVoiceHandle voice)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L188) | Stops one preview voice owned by an active Edit Mode session.
返回:true when a live preview was stopped.
`session`:Active Edit Mode session.
`voice`:Preview voice to stop. | — | — | -| [`public sealed class Inno.Editor.Audio.EditorAudioHost : Inno.Editor.Audio.IEditorAudioHost`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L19) | Owns Editor audio devices, previews, diagnostics, and isolated Play Mode audio generations. | — | — | - -### `Inno.Editor.Audio.IEditorAudioHost` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`Inno.Runtime.Contracts.IRuntimeSubsystemFactory Inno.Editor.Audio.IEditorAudioHost.CreateRuntimeSubsystemFactory(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L22) | Creates the reusable factory that contributes isolated audio runtimes to Editor sessions.
返回:A factory whose features are owned and released by their runtime sessions.
`session`:The explicit session being composed. | — | — | -| [`System.IDisposable Inno.Editor.Audio.IEditorAudioHost.EnterExecutionScope(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L33) | Binds the script-facing Inno.Editor.Audio façade to one session's audio runtime.
返回:A strict last-in-first-out execution scope.
`session`:Session whose feature pipeline owns an active Editor audio generation. | — | — | -| [`Inno.Audio.AudioVoiceHandle Inno.Editor.Audio.IEditorAudioHost.PlayPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioClipAsset clip, Inno.Audio.AudioPlayOptions? options = null)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L50) | Starts an Editor-owned preview voice through an active Edit session.
返回:The preview voice handle, initially in the preparing state.
`session`:Active Edit Mode session.
`clip`:Imported audio clip to preview.
`options`:Optional playback parameters; omitted values use engine defaults. | — | — | -| [`bool Inno.Editor.Audio.IEditorAudioHost.StopPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioVoiceHandle voice)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L68) | Stops one preview voice owned by an active Edit session.
返回:true when a live preview was stopped.
`session`:Active Edit Mode session.
`voice`:Preview voice to stop. | — | — | +| [`public sealed class Inno.Editor.Audio.EditorAudioHost : Inno.Editor.Audio.IEditorAudioHost`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L19) | Owns Editor audio devices, previews, diagnostics, and isolated Play Mode audio generations. | — | — | +| [`public Inno.Editor.Audio.EditorAudioHost.EditorAudioHost(Inno.Extensibility.Types.TypeCatalog types, Inno.Assets.IAssetArtifactLookup artifacts, Inno.Core.Diagnostics.DiagnosticHub diagnostics, System.Func deviceFactory, System.Func? settingsProvider = null)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L47) | Creates the Editor audio host over the active authoring artifact lookup and settings source.
`types`: Active type catalog used for provider and mixer extension generations.
`artifacts`: Authoring artifact lookup shared by Edit and isolated Play sessions.
`diagnostics`: Shared Editor diagnostic hub that owns current audio issues.
`deviceFactory`: Required host-owned backend-neutral device factory.
`settingsProvider`: Optional host-owned callback that returns the latest isolated audio project settings snapshot. | — | — | +| [`public Inno.Runtime.Contracts.IRuntimeSubsystemFactory Inno.Editor.Audio.EditorAudioHost.CreateRuntimeSubsystemFactory(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L70) | Creates the reusable feature factory used by Edit and Play runtime sessions.
`session`: The explicit session owner; it is never exposed through subsystem contracts. | A factory that creates and releases one isolated audio generation per session. | — | +| [`public System.IDisposable Inno.Editor.Audio.EditorAudioHost.EnterExecutionScope(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L147) | Binds the script-facing audio façade to one session's runtime.
`session`: Session whose feature pipeline owns an active Editor audio generation. | A strict last-in-first-out execution scope. | — | +| [`public Inno.Audio.AudioVoiceHandle Inno.Editor.Audio.EditorAudioHost.PlayPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioClipAsset clip, Inno.Audio.AudioPlayOptions? options = null)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L164) | Starts an Editor-owned preview voice through an active Edit Mode session.
`session`: Active Edit Mode session.
`clip`: Imported audio clip to preview.
`options`: Optional playback parameters; omitted values use engine defaults. | The preview voice handle, initially in the preparing state. | — | +| [`public bool Inno.Editor.Audio.EditorAudioHost.StopPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioVoiceHandle voice)`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L188) | Stops one preview voice owned by an active Edit Mode session.
`session`: Active Edit Mode session.
`voice`: Preview voice to stop. | when a live preview was stopped. | — | +| [`public void Inno.Editor.Audio.EditorAudioHost.Dispose()`](../../src/composition/editor/features/Inno.Editor.Audio/EditorAudioHost.cs#L201) | Releases every active audio generation before their owning Editor sessions are torn down. | — | — | | [`public interface Inno.Editor.Audio.IEditorAudioHost : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L11) | Coordinates isolated audio runtime generations for Edit and Play Mode sessions. | — | — | +| [`Inno.Runtime.Contracts.IRuntimeSubsystemFactory Inno.Editor.Audio.IEditorAudioHost.CreateRuntimeSubsystemFactory(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L22) | Creates the reusable factory that contributes isolated audio runtimes to Editor sessions.
`session`: The explicit session being composed. | A factory whose features are owned and released by their runtime sessions. | — | +| [`System.IDisposable Inno.Editor.Audio.IEditorAudioHost.EnterExecutionScope(Inno.Runtime.RuntimeSession session)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L33) | Binds the script-facing façade to one session's audio runtime.
`session`: Session whose feature pipeline owns an active Editor audio generation. | A strict last-in-first-out execution scope. | — | +| [`Inno.Audio.AudioVoiceHandle Inno.Editor.Audio.IEditorAudioHost.PlayPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioClipAsset clip, Inno.Audio.AudioPlayOptions? options = null)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L50) | Starts an Editor-owned preview voice through an active Edit session.
`session`: Active Edit Mode session.
`clip`: Imported audio clip to preview.
`options`: Optional playback parameters; omitted values use engine defaults. | The preview voice handle, initially in the preparing state. | — | +| [`bool Inno.Editor.Audio.IEditorAudioHost.StopPreview(Inno.Runtime.RuntimeSession session, Inno.Audio.AudioVoiceHandle voice)`](../../src/composition/editor/features/Inno.Editor.Audio/IEditorAudioHost.cs#L68) | Stops one preview voice owned by an active Edit session.
`session`: Active Edit Mode session.
`voice`: Preview voice to stop. | when a live preview was stopped. | — | ## 项目依赖 -- [Inno.Audio.Runtime](../audio/Inno.Audio.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Audio](../audio/Inno.Audio.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Runtime.Contracts](../runtime/Inno.Runtime.Contracts.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Audio.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Audio` | 公开签名依赖 | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Runtime` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Runtime.Contracts` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Core.md b/docs/editor/Inno.Editor.Core.md index 3da450ebd..3ac216a91 100644 --- a/docs/editor/Inno.Editor.Core.md +++ b/docs/editor/Inno.Editor.Core.md @@ -253,93 +253,15 @@ Playing、Preparing、Stopping 禁止手动 Reload;自动变化保留在现有 Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对象操作进入所属 Play Session。Scene 事务每个阶段独立进入 owner scope,Identity 替换不依赖 UI 当前 ambient domain。 -## 当前源码公开 API 清单 - -签名来自当前 SDK 求值与语义符号;按类型列出 public/protected 契约。工作流与生命周期见本页前文。 -### `Inno.Editor.Core.EditorContext` - - - - - - - - - - - - - - - - +## 不依赖绘制的名称格式化 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorContext.EditorContext(string projectDirectory, Inno.Editor.Core.EditorKeyboardPolicy keyboard)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L31) | Creates a passive editor context for one project.
`projectDirectory`:The project root containing Assets and Library.
`keyboard`:The explicit immutable conventions selected by product composition. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:The keyboard policy is null.
`System.ArgumentException`:Thrown when projectDirectory is empty. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorContext.GetLayoutSectionNames(string prefix = "")`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L86) | Gets layout section names matching an ordinal prefix.
返回:A stable sorted snapshot of matching section names.
`prefix`:The prefix to match, or an empty string for every section. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when prefix is null. | -| [`public bool Inno.Editor.Core.EditorContext.RemoveLayoutSection(string sectionName)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L145) | Removes one editor layout section from the in-memory document.
返回:true when an existing section was removed.
`sectionName`:The section name without its INI header syntax. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when sectionName is empty or cannot be represented in the layout. | -| [`public void Inno.Editor.Core.EditorContext.SaveLayout()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L181) | Atomically rewrites the complete project editor layout document. | 约束由所属类型与本页生命周期定义。 | `System.IO.IOException`:Thrown when the layout document cannot be written atomically.
`System.UnauthorizedAccessException`:Thrown when the layout document is inaccessible. | -| [`public bool Inno.Editor.Core.EditorContext.SaveLayoutIfChanged()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L169) | Atomically saves the project editor layout when it changed.
返回:true when a changed document was written. | 约束由所属类型与本页生命周期定义。 | `System.IO.IOException`:Thrown when the layout document cannot be written atomically.
`System.UnauthorizedAccessException`:Thrown when the layout document is inaccessible. | -| [`public void Inno.Editor.Core.EditorContext.SetImGuiLayout(string? value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L154) | Replaces the Dear ImGui layout while retaining editor module and panel state sections.
`value`:The complete layout text returned by Dear ImGui. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorContext.SetLayoutSection(string sectionName, System.Collections.Generic.IEnumerable> values)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L126) | Adds or replaces one human-readable editor layout section in memory.
`sectionName`:The section name without its INI header syntax.
`values`:The complete scalar or JSON-formatted values. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when values is null.
`System.ArgumentException`:Thrown when a section name, key, or value cannot be represented in the layout. | -| [`public bool Inno.Editor.Core.EditorContext.TryGetLayoutSection(string sectionName, out System.Collections.Generic.IReadOnlyDictionary values)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L104) | Tries to read one independent editor layout section snapshot.
返回:true when the requested section exists.
`sectionName`:The section name without its INI header syntax.
`values`:The copied values when the section exists. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when sectionName is empty or cannot be represented in the layout. | -| [`public Inno.Editor.Core.EditorFrame Inno.Editor.Core.EditorContext.frame { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L186) | Gets the latest immutable editor frame snapshot. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorContext.imguiLayout { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L69) | Gets the Dear ImGui layout text without editor module or panel state sections. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorContext.isFocused { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L191) | Gets whether any editor viewport currently owns application focus. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.Core.EditorKeyboardPolicy Inno.Editor.Core.EditorContext.keyboard { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L52) | Gets the immutable keyboard conventions shared by discovery, dispatch and presentation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorContext.layoutPath { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L63) | Gets the absolute path of the project editor layout document. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorContext.projectDirectory { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L47) | Gets the normalized project root directory. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.Core.EditorStatistics Inno.Editor.Core.EditorContext.statistics { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L57) | Gets the frame-scoped statistics exchange used by independent editor features and viewers. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorContext`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L13) | Provides passive project and frame state shared by editor extensions. Interaction routing is supplied separately by Inno.Editor.Interactions. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorFrame` +`EditorDisplayNames.Nicify` 为菜单、Inspector 与插件控件格式化标识符,不需要 ImGui session 或原生 context。空白输入得到空标签;下划线、连字符及小写到大写的边界产生空格。 - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorFrame.EditorFrame(float deltaTime, float totalTime, bool isFocused)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Describes one immutable editor frame.
`deltaTime`:The elapsed frame time in seconds.
`totalTime`:The absolute editor runtime in seconds.
`isFocused`:Whether an editor viewport owns application focus. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorFrame.Deconstruct(out float deltaTime, out float totalTime, out bool isFocused)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorFrame.Equals(Inno.Editor.Core.EditorFrame other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override bool Inno.Editor.Core.EditorFrame.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override int Inno.Editor.Core.EditorFrame.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override string Inno.Editor.Core.EditorFrame.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorFrame.operator ==(Inno.Editor.Core.EditorFrame left, Inno.Editor.Core.EditorFrame right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorFrame.operator !=(Inno.Editor.Core.EditorFrame left, Inno.Editor.Core.EditorFrame right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public float Inno.Editor.Core.EditorFrame.deltaTime { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L16) | The elapsed frame time in seconds. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorFrame.isFocused { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L18) | Whether an editor viewport owns application focus. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public float Inno.Editor.Core.EditorFrame.totalTime { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L17) | The absolute editor runtime in seconds. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public readonly record struct Inno.Editor.Core.EditorFrame : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Describes one immutable editor frame.
`deltaTime`:The elapsed frame time in seconds.
`totalTime`:The absolute editor runtime in seconds.
`isFocused`:Whether an editor viewport owns application focus. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorKeyboardPolicy` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorKeyboardPolicy.EditorKeyboardPolicy(Inno.Core.Input.KeyModifier primaryModifier, string superModifierLabel)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L23) | Defines the primary shortcut modifier and the display name of the Super modifier.
`primaryModifier`:One Control, Super or Alt modifier used by symbolic primary shortcuts.
`superModifierLabel`:The nonempty product-specific name displayed for the Super modifier. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:The modifier is not one supported modifier or the label is empty. | -| [`public Inno.Core.Input.KeyModifier Inno.Editor.Core.EditorKeyboardPolicy.primaryModifier { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L37) | Gets the modifier used when resolving symbolic primary shortcuts. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorKeyboardPolicy.superModifierLabel { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L42) | Gets the product-selected display name of the Super modifier. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorKeyboardPolicy`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L9) | Supplies immutable keyboard conventions selected by the Editor product composition. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | +## 当前源码公开 API 清单 -### `Inno.Editor.Core.EditorModal` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -352,39 +274,11 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Core.EditorModal.EditorModal()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L10) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorModal.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L60) | Draws the modal body inside the runtime-managed centered window.
`context`:The shared editor context containing current project and frame state. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null. | -| [`protected abstract void Inno.Editor.Core.EditorModal.OnDraw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L72) | Draws the modal body inside the runtime-managed centered window.
`context`:The shared editor context containing current project and frame state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorModal.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L36) | Gets whether the modal window owns scrolling when its content overflows. Disable this when a child region owns all scrolling. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorModal.blocksInteraction { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L20) | Gets whether the modal prevents interaction with regular editor views. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorModal.canMove { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L25) | Gets whether the modal window can be moved inside the main viewport. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorModal.canResize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L30) | Gets whether the modal window can be resized by the user. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorModal.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L42) | Gets the initial modal size in unscaled editor units, or P:System.Numerics.Vector2.Zero when the runtime should size the modal from its content. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract bool Inno.Editor.Core.EditorModal.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L15) | Gets whether the modal should currently be visible. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorModal.minimumSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L48) | Gets the minimum modal size in unscaled editor units, or P:System.Numerics.Vector2.Zero when no explicit minimum is required. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract class Inno.Editor.Core.EditorModal`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L10) | Defines non-dockable modal editor content. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorModalAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorModalAttribute.EditorModalAttribute(string id, string title, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L26) | Creates a centered modal registration with a stable identity and draw order.
`id`:The stable identity used to retain modal transition state across refreshes.
`title`:The visible title rendered by the modal host.
`order`:The stable ordering value when multiple modals are visible. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when id or title is empty. | -| [`public string Inno.Editor.Core.EditorModalAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L43) | Gets the stable modal identifier. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public int Inno.Editor.Core.EditorModalAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L53) | Gets the stable draw order. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorModalAttribute.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L48) | Gets the visible modal title. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorModalAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L9) | Registers a centered editor modal for automatic discovery. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorModule` - @@ -397,38 +291,10 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Core.EditorModule.EditorModule()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L13) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L172) | Captures readable project state owned by this module.
`state`:The writable parameter that receives the complete readable state for this module. | Overriding this method opts the module into project-state IO. Modules that keep the base implementation are never registered with the persistence coordinator and therefore perform no state reads or writes. | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L195) | Releases resources owned by this module after it has stopped and left the active extension generation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L133) | Runs after the module generation becomes active and before its first update.
`context`:The shared editor context for the active runtime. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L157) | Runs before the module generation is released and its disposable instances are destroyed.
`context`:The shared editor context for the runtime being stopped. | This includes partial startup compensation. Pending retirement must throw RetirementPendingException and preserve unfinished ownership; completed steps must be idempotent across retries. | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L143) | Runs once per editor frame before views are drawn.
`context`:The shared editor context containing the current frame state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorModule.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L187) | Restores readable project state owned by this module.
`state`:The read-only state parameter for this module. Missing or incompatible values return the fallback supplied to M:Inno.Editor.Core.EditorState.Get``1(System.String,``0). | This method is called only when M:Inno.Editor.Core.EditorModule.Capture(Inno.Editor.Core.EditorState) is overridden. It runs once after the module is started and before the module is allowed to capture replacement state. | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorModule.Start(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L46) | Starts the module after the containing extension generation becomes active.
`context`:The shared editor context for the active runtime. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null.
`System.ObjectDisposedException`:Thrown after the module has been disposed.
`System.InvalidOperationException`:Thrown when the module is already started. | -| [`public void Inno.Editor.Core.EditorModule.Stop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L102) | Stops the module before the containing extension generation is released.
`context`:The shared editor context for the runtime being stopped. | Also compensates a partially failed Start. Completed stops are idempotent; pending stops may be retried. | `System.ArgumentNullException`:Thrown when context is null.
`System.ObjectDisposedException`:Thrown after the module has been disposed.
`Inno.Core.Execution.RetirementPendingException`:Live work still depends on this module. Its owner must retain it and retry Stop before disposal. | -| [`public void Inno.Editor.Core.EditorModule.Update(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L74) | Updates the module once per editor frame before panels and modals are drawn.
`context`:The shared editor context containing the current frame state. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null.
`System.ObjectDisposedException`:Thrown after the module has been disposed.
`System.InvalidOperationException`:Thrown when the module is not started. | -| [`public virtual bool Inno.Editor.Core.EditorModule.blocksFollowingUpdates { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L28) | Gets whether modules ordered after this module must defer their updates for the current frame. | A module may return true while completing a project-wide bootstrap or atomic transition that later modules must not observe partially. Panels and modals remain drawable. | 具体失败按方法与类型契约处理。 | -| [`public abstract class Inno.Editor.Core.EditorModule : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L13) | Owns optional shared state and lifecycle for one editor feature. Simple panels and actions do not need a module. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorModuleAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorModuleAttribute.EditorModuleAttribute(string id, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L24) | Creates a module registration with stable identity and deterministic lifecycle order.
`id`:The globally unique module identifier used for discovery diagnostics and optional persisted module state.
`order`:The ascending order used to start and update the module. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when id is empty. | -| [`public string Inno.Editor.Core.EditorModuleAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L37) | Gets the stable module identifier. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public int Inno.Editor.Core.EditorModuleAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L42) | Gets the stable lifecycle order. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorModuleAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L9) | Registers an editor feature module with a stable identity for automatic discovery. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorPanel` - @@ -443,26 +309,6 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Core.EditorPanel.EditorPanel()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L13) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorPanel.Attach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L56) | Attaches the panel after its extension generation becomes active.
`context`:The shared editor context for the active runtime. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null.
`System.InvalidOperationException`:Thrown when the panel is already attached. | -| [`protected virtual void Inno.Editor.Core.EditorPanel.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L168) | Captures readable project state owned by this panel.
`state`:The writable parameter that receives the complete readable state for this panel. | Overriding this method opts the panel into project-state IO. Panels that keep the base implementation are never registered with the persistence coordinator and therefore perform no state reads or writes. Panel visibility is persisted separately for every registered panel. | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorPanel.Detach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L82) | Detaches the panel before its extension generation is released.
`context`:The shared editor context for the runtime being detached. | Also compensates partial attachment. Completed detachment is idempotent; a pending panel cannot draw. | `System.ArgumentNullException`:Thrown when context is null.
`Inno.Core.Execution.RetirementPendingException`:Panel work is still active. The owner must retain the panel and its dependencies and retry detachment. | -| [`public void Inno.Editor.Core.EditorPanel.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L117) | Draws the complete dockable contents of the panel for the current frame.
`context`:The shared editor context containing current selection, focus, and frame state. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null.
`System.InvalidOperationException`:Thrown when the panel is not attached. | -| [`protected virtual void Inno.Editor.Core.EditorPanel.OnAttach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L139) | Runs after the panel is attached to an active extension generation.
`context`:The shared editor context for the active runtime. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorPanel.OnDetach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L153) | Runs before the panel is detached from its active extension generation.
`context`:The shared editor context for the runtime being detached. | This includes failed attachment compensation. Retain unfinished resources when throwing RetirementPendingException and avoid repeating completed cleanup steps on the next attempt. | 具体失败按方法与类型契约处理。 | -| [`protected abstract void Inno.Editor.Core.EditorPanel.OnDraw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L131) | Draws the complete dockable contents of this panel for the current frame.
`context`:The shared editor context containing current selection, focus, and frame state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected virtual void Inno.Editor.Core.EditorPanel.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L183) | Restores readable project state owned by this panel.
`state`:The read-only state parameter for this panel. Missing or incompatible values return the fallback supplied to M:Inno.Editor.Core.EditorState.Get``1(System.String,``0). | This method is called only when M:Inno.Editor.Core.EditorPanel.Capture(Inno.Editor.Core.EditorState) is overridden. It runs once after the panel is attached and before the panel is allowed to capture replacement state. | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorPanel.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L31) | Gets whether the presentation backend may create a scroll range for this panel window. | Full-canvas panels should return false and manage navigation inside their own content instead of allowing the host window to scroll. | 具体失败按方法与类型契约处理。 | -| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorPanel.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L36) | Gets the initial floating size in UI coordinates; zero leaves native auto-sizing in control. Saved layouts take precedence. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorPanel.isOpen { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L41) | Gets or sets whether panel is visible. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public virtual bool Inno.Editor.Core.EditorPanel.useWindowPadding { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L22) | Gets whether the presentation backend should inset this panel body by its standard window padding. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract class Inno.Editor.Core.EditorPanel`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L13) | Base class for editor panel implementations. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorPanelAttribute` - @@ -471,20 +317,13 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorPanelAttribute.EditorPanelAttribute(string id, string title, int order = 0, bool defaultOpen = true, string menuPath = "", bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L36) | Creates a dockable panel registration with stable identity and presentation metadata.
`id`:The stable identity used for Panel-menu routing and reload-state retention.
`title`:The visible dockable-window title.
`order`:The stable panel and generated Panel-menu ordering value.
`defaultOpen`:Whether a newly discovered panel is visible before retained state is available.
`menuPath`:The optional slash-delimited category path under the generated Panel menu.
`separatorBefore`:Whether the generated toggle is preceded by a separator within its final menu category. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when id or title is empty, or when menuPath contains an empty segment. | -| [`public bool Inno.Editor.Core.EditorPanelAttribute.defaultOpen { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L74) | Gets whether a newly discovered panel is open by default. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorPanelAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L59) | Gets the stable panel identifier. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorPanelAttribute.menuPath { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L79) | Gets the optional category path under the generated Panel menu. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public int Inno.Editor.Core.EditorPanelAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L69) | Gets the stable panel ordering value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorPanelAttribute.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L84) | Gets whether the generated panel toggle begins a new visual group. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorPanelAttribute.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L64) | Gets the visible panel title. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorPanelAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L9) | Registers an editor panel for automatic discovery. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorReloadCoordinator` - + + + + + + + @@ -493,20 +332,45 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorReloadCoordinator.EditorReloadCoordinator()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L16) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | 请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public bool Inno.Editor.Core.EditorReloadCoordinator.CanActivateReload(out string? reason)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L66) | Queries whether compilation and activation can proceed without interrupting a running session.
返回:True when all live guards permit generation work.
`reason`:The first blocking reason, or null when activation is allowed. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | 请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public bool Inno.Editor.Core.EditorReloadCoordinator.CanRequestReload(out string? reason)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L55) | Queries whether every registered owner accepts an explicit reload request.
返回:True when all live guards permit a manual request.
`reason`:The first blocking reason, or null when the request is allowed. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | 请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public Inno.Extensibility.Reload.IAssemblyUnloadProbe Inno.Editor.Core.EditorReloadCoordinator.Execute(Inno.Extensibility.Modules.AssemblyReloadSession reload, Inno.Extensibility.Reload.IGenerationChange? externalChange = null)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L113) | Applies one prepared assembly reload together with every registered editor feature migration.
返回:A monitor that observes cooperative unloading of assemblies retired by the committed reload.
`reload`:The prepared assembly reload session to activate and complete.
`externalChange`:Content recovery prepared before type publication, applied before Editor recovery, and restored before old values. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | `System.ArgumentNullException`:Thrown when reload is null.
`System.AggregateException`:Thrown when activation fails and one or more feature or assembly rollback stages also fail.
请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public void Inno.Editor.Core.EditorReloadCoordinator.RefreshDiagnostics()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L128) | Requests every live participant to republish diagnostics derived from its current state. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | 请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public System.IDisposable Inno.Editor.Core.EditorReloadCoordinator.Register(Inno.Editor.Core.IEditorReloadParticipant participant)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L81) | Registers an editor feature that owns generation-bound live state.
返回:A registration lease that strongly retains the participant until the lease is disposed. The coordinator itself retains only a weak reference.
`participant`:The feature participant. The coordinator retains only a weak reference to this instance. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | `System.ArgumentNullException`:Thrown when participant is null.
请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public System.IDisposable Inno.Editor.Core.EditorReloadCoordinator.RegisterGuard(Inno.Editor.Core.IEditorReloadGuard guard)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L34) | Registers a weak admission guard without stopping its owner or creating a generation transaction.
返回:A caller-owned lease retaining the guard until registration is released.
`guard`:The owner whose current state determines reload admission. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | `System.ArgumentNullException`:The guard is null.
请求和执行分别重新检查;事务失败逆序恢复。 | -| [`public sealed class Inno.Editor.Core.EditorReloadCoordinator : Inno.Scripting.Reload.IScriptReloadCoordinator`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L16) | Coordinates weakly registered editor feature migrations around atomic assembly reload sessions. | 注册 lease 由调用者释放;coordinator 仅弱引用参与者与 guard。 | 请求和执行分别重新检查;事务失败逆序恢复。 | - -### `Inno.Editor.Core.EditorRuntime` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -514,83 +378,10 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Core.EditorRuntime.EditorRuntime(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L19) | Creates a runtime for the supplied passive context.
`context`:The shared editor context. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when context is null. | -| [`public abstract void Inno.Editor.Core.EditorRuntime.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L57) | Stops the runtime and releases active extensions. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`protected void Inno.Editor.Core.EditorRuntime.SetFrame(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L48) | Advances frame-scoped data and publishes the latest immutable frame.
`frame`:The frame to publish. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract void Inno.Editor.Core.EditorRuntime.Start()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L32) | Starts the runtime and activates its initial extension generation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract void Inno.Editor.Core.EditorRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L40) | Updates the runtime for one editor frame.
`frame`:The immutable frame state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Core.EditorRuntime.context { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L27) | Gets the shared passive editor context. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract class Inno.Editor.Core.EditorRuntime : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L8) | Defines the presentation-independent lifecycle of an editor runtime. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorState` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Core.EditorState.EditorState()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L19) | Initializes the base contract for a runtime-owned editor state parameter. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public abstract T Inno.Editor.Core.EditorState.Get(string key, T fallback)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L42) | Reads a compatible value or returns the caller-provided fallback.
返回:The stored compatible value, or fallback.
`key`:The stable key local to the owning module or panel.
`fallback`:The value returned when the key is absent, malformed, or incompatible with T. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when key is empty. | -| [`public abstract void Inno.Editor.Core.EditorState.Set(string key, T value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L68) | Writes one neutral value under a stable extension-local key.
`key`:The stable key local to the owning module or panel.
`value`:The value to persist. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when key is empty.
`System.InvalidOperationException`:Thrown when the state parameter was supplied for restoration and is therefore read-only.
`System.NotSupportedException`:Thrown when value cannot be represented by the runtime state serializer. | -| [`public abstract class Inno.Editor.Core.EditorState`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L14) | Provides the serialization-neutral parameter used to capture or restore one editor extension's project state. | The editor supplies this object to the protected state hooks on Inno.Editor.Core.EditorModule and Inno.Editor.Core.EditorPanel. Extensions should write values during capture and read values during restore; storage format and persistence remain private runtime details. | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorStatistic` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorStatistic.EditorStatistic(Inno.Editor.Core.EditorStatisticId id, Inno.Editor.Core.EditorStatisticGroupId groupId, string groupName, string label, string value, int groupOrder = 0, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L117) | Creates one statistic contribution.
`id`:Globally stable statistic identity used for replacement within a frame.
`groupId`:Stable visual group identity.
`groupName`:User-facing group heading.
`label`:User-facing metric label.
`value`:Already formatted user-facing value.
`groupOrder`:Ascending group presentation order.
`order`:Ascending metric order within the group. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when an identifier, group name, label, or value is empty. | -| [`public Inno.Editor.Core.EditorStatisticGroupId Inno.Editor.Core.EditorStatistic.groupId { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L150) | Gets the stable visual group identity. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorStatistic.groupName { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L155) | Gets the user-facing group heading. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public int Inno.Editor.Core.EditorStatistic.groupOrder { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L170) | Gets ascending group presentation order. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.Core.EditorStatisticId Inno.Editor.Core.EditorStatistic.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L145) | Gets the globally stable statistic identity. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorStatistic.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L160) | Gets the user-facing metric label. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public int Inno.Editor.Core.EditorStatistic.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L175) | Gets ascending metric order within the group. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorStatistic.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L165) | Gets the presentation-ready value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.Core.EditorStatistic`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L88) | Describes one immutable, presentation-ready statistic without retaining provider instances or types. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorStatisticGroupId` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorStatisticGroupId.EditorStatisticGroupId(string value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L60) | Creates a globally stable statistic-group identifier.
`value`:Non-empty globally stable identity. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when value is empty. | -| [`public bool Inno.Editor.Core.EditorStatisticGroupId.Equals(Inno.Editor.Core.EditorStatisticGroupId other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override bool Inno.Editor.Core.EditorStatisticGroupId.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override int Inno.Editor.Core.EditorStatisticGroupId.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override string Inno.Editor.Core.EditorStatisticGroupId.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L82) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorStatisticGroupId.operator ==(Inno.Editor.Core.EditorStatisticGroupId left, Inno.Editor.Core.EditorStatisticGroupId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorStatisticGroupId.operator !=(Inno.Editor.Core.EditorStatisticGroupId left, Inno.Editor.Core.EditorStatisticGroupId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorStatisticGroupId.isValid { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L74) | Gets whether this identifier contains a usable value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorStatisticGroupId.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L69) | Gets the globally stable identity. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public readonly record struct Inno.Editor.Core.EditorStatisticGroupId : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Identifies one visual group in the editor statistics presentation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorStatisticId` - @@ -601,81 +392,196 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorStatisticId.EditorStatisticId(string value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L21) | Creates a globally stable statistic identifier.
`value`:Non-empty globally stable identity. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentException`:Thrown when value is empty. | -| [`public bool Inno.Editor.Core.EditorStatisticId.Equals(Inno.Editor.Core.EditorStatisticId other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override bool Inno.Editor.Core.EditorStatisticId.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override int Inno.Editor.Core.EditorStatisticId.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public override string Inno.Editor.Core.EditorStatisticId.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L43) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorStatisticId.operator ==(Inno.Editor.Core.EditorStatisticId left, Inno.Editor.Core.EditorStatisticId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public static bool Inno.Editor.Core.EditorStatisticId.operator !=(Inno.Editor.Core.EditorStatisticId left, Inno.Editor.Core.EditorStatisticId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.Core.EditorStatisticId.isValid { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L35) | Gets whether this identifier contains a usable value. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string Inno.Editor.Core.EditorStatisticId.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L30) | Gets the globally stable identity. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public readonly record struct Inno.Editor.Core.EditorStatisticId : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Identifies one editor statistic across panels, modules, and reloadable extensions. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.EditorStatistics` - + + + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorStatistics.EditorStatistics()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorStatistics.GetSnapshot()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L230) | Gets a deterministic snapshot combining current contributions with the previous completed frame for providers that draw after their consumer.
返回:A detached, sorted snapshot containing at most one value for each stable statistic ID. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.Core.EditorStatistics.Publish(Inno.Editor.Core.EditorStatistic statistic)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L200) | Publishes or replaces one statistic for the current editor frame.
`statistic`:Immutable contribution containing no runtime provider references. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when statistic is null. | -| [`public void Inno.Editor.Core.EditorStatistics.Publish(System.Collections.Generic.IEnumerable statistics)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L216) | Publishes or replaces several statistics for the current editor frame.
`statistics`:Contributions to publish in enumeration order. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when statistics or one of its values is null. | -| [`public sealed class Inno.Editor.Core.EditorStatistics`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | Exchanges frame-scoped, reload-safe statistics between independent editor features and viewers. | Contributions contain only stable identifiers and strings. A contribution is retained for the current frame and one completed-frame handoff so panel draw order cannot lose data. | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.IEditorPanelReloadState` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`System.ReadOnlyMemory Inno.Editor.Core.IEditorPanelReloadState.CaptureReloadState()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L16) | Captures presentation-neutral state without retaining runtime or plugin object references.
返回:An immutable byte payload that can be consumed by the replacement panel generation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`void Inno.Editor.Core.IEditorPanelReloadState.RestoreReloadState(System.ReadOnlyMemory state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L24) | Restores presentation-neutral state captured from the previous panel generation.
`state`:The immutable payload returned by the retiring panel generation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public interface Inno.Editor.Core.IEditorPanelReloadState`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L8) | Optionally migrates presentation-neutral panel state across assembly reloads. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.Core.IEditorReloadGuard` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`string? Inno.Editor.Core.IEditorReloadGuard.blockingReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L21) | Gets the reason admission is blocked, or null when the owner is ready for reload. | 借用不可变契约;状态由 owner 在其线程安全点提供。 | 阻止请求不停止 Play;仍须遵守 generation Pending/Faulted gate。 | -| [`bool Inno.Editor.Core.IEditorReloadGuard.canActivateReload { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L16) | Gets whether compilation and activation may proceed without a live isolated runtime session. | 借用不可变契约;状态由 owner 在其线程安全点提供。 | 阻止请求不停止 Play;仍须遵守 generation Pending/Faulted gate。 | -| [`bool Inno.Editor.Core.IEditorReloadGuard.canRequestReload { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L11) | Gets whether an explicit user reload request may be accepted in the current owner state. | 借用不可变契约;状态由 owner 在其线程安全点提供。 | 阻止请求不停止 Play;仍须遵守 generation Pending/Faulted gate。 | -| [`public interface Inno.Editor.Core.IEditorReloadGuard`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L6) | Describes whether an editor owner has released the state required for a generation change. | 借用不可变契约;状态由 owner 在其线程安全点提供。 | 阻止请求不停止 Play;仍须遵守 generation Pending/Faulted gate。 | - -### `Inno.Editor.Core.IEditorReloadParticipant` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Extensibility.Reload.IGenerationChange Inno.Editor.Core.IEditorReloadParticipant.Capture(Inno.Extensibility.Modules.AssemblyReloadContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L21) | Captures one isolated transaction for the prepared assembly generation.
返回:A transaction that has captured current state but has not modified live editor objects.
`context`:The prepared assembly reload context containing previous and candidate participant state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`void Inno.Editor.Core.IEditorReloadParticipant.RefreshDiagnostics()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L26) | Republishes diagnostics derived from the participant's current live state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public interface Inno.Editor.Core.IEditorReloadParticipant`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L10) | Contributes editor-owned live state to an assembly reload without coupling the reload requester to a feature domain. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | +| [`public abstract class Inno.Editor.Core.EditorModal`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L10) | Defines non-dockable modal editor content. | — | — | +| [`public abstract bool Inno.Editor.Core.EditorModal.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L15) | Gets whether the modal should currently be visible. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorModal.blocksInteraction { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L20) | Gets whether the modal prevents interaction with regular editor views. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorModal.canMove { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L25) | Gets whether the modal window can be moved inside the main viewport. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorModal.canResize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L30) | Gets whether the modal window can be resized by the user. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorModal.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L36) | Gets whether the modal window owns scrolling when its content overflows. Disable this when a child region owns all scrolling. | — | — | +| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorModal.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L42) | Gets the initial modal size in unscaled editor units, or when the runtime should size the modal from its content. | — | — | +| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorModal.minimumSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L48) | Gets the minimum modal size in unscaled editor units, or when no explicit minimum is required. | — | — | +| [`public void Inno.Editor.Core.EditorModal.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L60) | Draws the modal body inside the runtime-managed centered window.
`context`: The shared editor context containing current project and frame state. | — | `System.ArgumentNullException`: Thrown when is . | +| [`protected abstract void Inno.Editor.Core.EditorModal.OnDraw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L72) | Draws the modal body inside the runtime-managed centered window.
`context`: The shared editor context containing current project and frame state. | — | — | +| [`protected Inno.Editor.Core.EditorModal.EditorModal()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModal.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Core.EditorModalAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L9) | Registers a centered editor modal for automatic discovery. | — | — | +| [`public Inno.Editor.Core.EditorModalAttribute.EditorModalAttribute(string id, string title, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L26) | Creates a centered modal registration with a stable identity and draw order.
`id`: The stable identity used to retain modal transition state across refreshes.
`title`: The visible title rendered by the modal host.
`order`: The stable ordering value when multiple modals are visible. | — | `System.ArgumentException`: Thrown when or is empty. | +| [`public string Inno.Editor.Core.EditorModalAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L43) | Gets the stable modal identifier. | — | — | +| [`public string Inno.Editor.Core.EditorModalAttribute.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L48) | Gets the visible modal title. | — | — | +| [`public int Inno.Editor.Core.EditorModalAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModalAttribute.cs#L53) | Gets the stable draw order. | — | — | +| [`public abstract class Inno.Editor.Core.EditorModule : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L13) | Owns optional shared state and lifecycle for one editor feature. Simple panels and actions do not need a module. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorModule.blocksFollowingUpdates { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L28) | Gets whether modules ordered after this module must defer their updates for the current frame. | — | — | +| [`public void Inno.Editor.Core.EditorModule.Start(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L46) | Starts the module after the containing extension generation becomes active.
`context`: The shared editor context for the active runtime. | — | `System.ArgumentNullException`: Thrown when is .
`System.ObjectDisposedException`: Thrown after the module has been disposed.
`System.InvalidOperationException`: Thrown when the module is already started. | +| [`public void Inno.Editor.Core.EditorModule.Update(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L74) | Updates the module once per editor frame before panels and modals are drawn.
`context`: The shared editor context containing the current frame state. | — | `System.ArgumentNullException`: Thrown when is .
`System.ObjectDisposedException`: Thrown after the module has been disposed.
`System.InvalidOperationException`: Thrown when the module is not started. | +| [`public void Inno.Editor.Core.EditorModule.Stop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L102) | Stops the module before the containing extension generation is released.
`context`: The shared editor context for the runtime being stopped. | — | `System.ArgumentNullException`: Thrown when is .
`System.ObjectDisposedException`: Thrown after the module has been disposed.
`Inno.Core.Execution.RetirementPendingException`: Live work still depends on this module. Its owner must retain it and retry Stop before disposal. | +| [`protected virtual void Inno.Editor.Core.EditorModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L133) | Runs after the module generation becomes active and before its first update.
`context`: The shared editor context for the active runtime. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorModule.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L143) | Runs once per editor frame before views are drawn.
`context`: The shared editor context containing the current frame state. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L157) | Runs before the module generation is released and its disposable instances are destroyed.
`context`: The shared editor context for the runtime being stopped. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorModule.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L172) | Captures readable project state owned by this module.
`state`: The writable parameter that receives the complete readable state for this module. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorModule.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L187) | Restores readable project state owned by this module.
`state`: The read-only state parameter for this module. Missing or incompatible values return the fallback supplied to . | — | — | +| [`protected virtual void Inno.Editor.Core.EditorModule.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L195) | Releases resources owned by this module after it has stopped and left the active extension generation. | — | — | +| [`protected Inno.Editor.Core.EditorModule.EditorModule()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModule.cs#L13) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Core.EditorModuleAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L9) | Registers an editor feature module with a stable identity for automatic discovery. | — | — | +| [`public Inno.Editor.Core.EditorModuleAttribute.EditorModuleAttribute(string id, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L24) | Creates a module registration with stable identity and deterministic lifecycle order.
`id`: The globally unique module identifier used for discovery diagnostics and optional persisted module state.
`order`: The ascending order used to start and update the module. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Core.EditorModuleAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L37) | Gets the stable module identifier. | — | — | +| [`public int Inno.Editor.Core.EditorModuleAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorModuleAttribute.cs#L42) | Gets the stable lifecycle order. | — | — | +| [`public abstract class Inno.Editor.Core.EditorPanel`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L13) | Base class for editor panel implementations. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorPanel.useWindowPadding { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L22) | Gets whether the presentation backend should inset this panel body by its standard window padding. | — | — | +| [`public virtual bool Inno.Editor.Core.EditorPanel.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L31) | Gets whether the presentation backend may create a scroll range for this panel window. | — | — | +| [`public virtual System.Numerics.Vector2 Inno.Editor.Core.EditorPanel.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L36) | Gets the initial floating size in UI coordinates; zero leaves native auto-sizing in control. Saved layouts take precedence. | — | — | +| [`public bool Inno.Editor.Core.EditorPanel.isOpen { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L41) | Gets or sets whether panel is visible. | — | — | +| [`public void Inno.Editor.Core.EditorPanel.Attach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L56) | Attaches the panel after its extension generation becomes active.
`context`: The shared editor context for the active runtime. | — | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the panel is already attached. | +| [`public void Inno.Editor.Core.EditorPanel.Detach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L82) | Detaches the panel before its extension generation is released.
`context`: The shared editor context for the runtime being detached. | — | `System.ArgumentNullException`: Thrown when is .
`Inno.Core.Execution.RetirementPendingException`: Panel work is still active. The owner must retain the panel and its dependencies and retry detachment. | +| [`public void Inno.Editor.Core.EditorPanel.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L117) | Draws the complete dockable contents of the panel for the current frame.
`context`: The shared editor context containing current selection, focus, and frame state. | — | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the panel is not attached. | +| [`protected abstract void Inno.Editor.Core.EditorPanel.OnDraw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L131) | Draws the complete dockable contents of this panel for the current frame.
`context`: The shared editor context containing current selection, focus, and frame state. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorPanel.OnAttach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L139) | Runs after the panel is attached to an active extension generation.
`context`: The shared editor context for the active runtime. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorPanel.OnDetach(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L153) | Runs before the panel is detached from its active extension generation.
`context`: The shared editor context for the runtime being detached. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorPanel.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L168) | Captures readable project state owned by this panel.
`state`: The writable parameter that receives the complete readable state for this panel. | — | — | +| [`protected virtual void Inno.Editor.Core.EditorPanel.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L183) | Restores readable project state owned by this panel.
`state`: The read-only state parameter for this panel. Missing or incompatible values return the fallback supplied to . | — | — | +| [`protected Inno.Editor.Core.EditorPanel.EditorPanel()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanel.cs#L13) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Core.EditorPanelAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L9) | Registers an editor panel for automatic discovery. | — | — | +| [`public Inno.Editor.Core.EditorPanelAttribute.EditorPanelAttribute(string id, string title, int order = 0, bool defaultOpen = true, string menuPath = "", bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L36) | Creates a dockable panel registration with stable identity and presentation metadata.
`id`: The stable identity used for Panel-menu routing and reload-state retention.
`title`: The visible dockable-window title.
`order`: The stable panel and generated Panel-menu ordering value.
`defaultOpen`: Whether a newly discovered panel is visible before retained state is available.
`menuPath`: The optional slash-delimited category path under the generated Panel menu.
`separatorBefore`: Whether the generated toggle is preceded by a separator within its final menu category. | — | `System.ArgumentException`: Thrown when or is empty, or when contains an empty segment. | +| [`public string Inno.Editor.Core.EditorPanelAttribute.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L59) | Gets the stable panel identifier. | — | — | +| [`public string Inno.Editor.Core.EditorPanelAttribute.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L64) | Gets the visible panel title. | — | — | +| [`public int Inno.Editor.Core.EditorPanelAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L69) | Gets the stable panel ordering value. | — | — | +| [`public bool Inno.Editor.Core.EditorPanelAttribute.defaultOpen { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L74) | Gets whether a newly discovered panel is open by default. | — | — | +| [`public string Inno.Editor.Core.EditorPanelAttribute.menuPath { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L79) | Gets the optional category path under the generated Panel menu. | — | — | +| [`public bool Inno.Editor.Core.EditorPanelAttribute.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorPanelAttribute.cs#L84) | Gets whether the generated panel toggle begins a new visual group. | — | — | +| [`public static class Inno.Editor.Core.EditorTypeSpecificity`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs#L7) | Ranks assignable editor extension targets by class ancestry, with interface registrations one step away. | — | — | +| [`public static bool Inno.Editor.Core.EditorTypeSpecificity.TryGet(System.Type concreteType, System.Type candidateType, out int distance)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs#L27) | Determines whether a registration accepts a concrete target and returns its specificity distance.
`concreteType`: The target type supplied by the active extension generation.
`candidateType`: The registered class or interface accepted by the extension.
`distance`: Zero for an exact match, ancestry distance for classes, or one for an interface. | True when the registration accepts the target; otherwise false and the maximum distance. | `System.ArgumentNullException`: Either type is null. | +| [`public interface Inno.Editor.Core.IEditorPanelReloadState`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L8) | Optionally migrates presentation-neutral panel state across assembly reloads. | — | — | +| [`System.ReadOnlyMemory Inno.Editor.Core.IEditorPanelReloadState.CaptureReloadState()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L16) | Captures presentation-neutral state without retaining runtime or plugin object references. | An immutable byte payload that can be consumed by the replacement panel generation. | — | +| [`void Inno.Editor.Core.IEditorPanelReloadState.RestoreReloadState(System.ReadOnlyMemory state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L24) | Restores presentation-neutral state captured from the previous panel generation.
`state`: The immutable payload returned by the retiring panel generation. | — | — | +| [`public static class Inno.Editor.Core.EditorDisplayNames`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L7) | Formats readable Editor labels independently from a native UI context or session. | — | — | +| [`public static string Inno.Editor.Core.EditorDisplayNames.Nicify(string name)`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L18) | Converts an identifier into a readable editor label.
`name`: Source identifier. | A label with identifier separators and lower-to-upper transitions spaced; empty when the input is blank. | — | +| [`public sealed class Inno.Editor.Core.EditorReloadCoordinator : Inno.Scripting.Reload.IScriptReloadCoordinator`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L16) | Coordinates weakly registered editor feature migrations around atomic assembly reload sessions. | — | — | +| [`public System.IDisposable Inno.Editor.Core.EditorReloadCoordinator.RegisterGuard(Inno.Editor.Core.IEditorReloadGuard guard)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L34) | Registers a weak admission guard without stopping its owner or creating a generation transaction.
`guard`: The owner whose current state determines reload admission. | A caller-owned lease retaining the guard until registration is released. | `System.ArgumentNullException`: The guard is null. | +| [`public bool Inno.Editor.Core.EditorReloadCoordinator.CanRequestReload(out string? reason)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L55) | Queries whether every registered owner accepts an explicit reload request.
`reason`: The first blocking reason, or null when the request is allowed. | True when all live guards permit a manual request. | — | +| [`public bool Inno.Editor.Core.EditorReloadCoordinator.CanActivateReload(out string? reason)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L66) | Queries whether compilation and activation can proceed without interrupting a running session.
`reason`: The first blocking reason, or null when activation is allowed. | True when all live guards permit generation work. | — | +| [`public System.IDisposable Inno.Editor.Core.EditorReloadCoordinator.Register(Inno.Editor.Core.IEditorReloadParticipant participant)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L81) | Registers an editor feature that owns generation-bound live state.
`participant`: The feature participant. The coordinator retains only a weak reference to this instance. | A registration lease that strongly retains the participant until the lease is disposed. The coordinator itself retains only a weak reference. | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Extensibility.Reload.IAssemblyUnloadProbe Inno.Editor.Core.EditorReloadCoordinator.Execute(Inno.Extensibility.Modules.AssemblyReloadSession reload, Inno.Extensibility.Reload.IGenerationChange? externalChange = null)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L113) | Applies one prepared assembly reload together with every registered editor feature migration.
`reload`: The prepared assembly reload session to activate and complete.
`externalChange`: Content recovery prepared before type publication, applied before Editor recovery, and restored before old values. | A monitor that observes cooperative unloading of assemblies retired by the committed reload. | `System.ArgumentNullException`: Thrown when is .
`System.AggregateException`: Thrown when activation fails and one or more feature or assembly rollback stages also fail. | +| [`public void Inno.Editor.Core.EditorReloadCoordinator.RefreshDiagnostics()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L128) | Requests every live participant to republish diagnostics derived from its current state. | — | — | +| [`public Inno.Editor.Core.EditorReloadCoordinator.EditorReloadCoordinator()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public interface Inno.Editor.Core.IEditorReloadGuard`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L6) | Describes whether an editor owner has released the state required for a generation change. | — | — | +| [`bool Inno.Editor.Core.IEditorReloadGuard.canRequestReload { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L11) | Gets whether an explicit user reload request may be accepted in the current owner state. | — | — | +| [`bool Inno.Editor.Core.IEditorReloadGuard.canActivateReload { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L16) | Gets whether compilation and activation may proceed without a live isolated runtime session. | — | — | +| [`string? Inno.Editor.Core.IEditorReloadGuard.blockingReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadGuard.cs#L21) | Gets the reason admission is blocked, or null when the owner is ready for reload. | — | — | +| [`public interface Inno.Editor.Core.IEditorReloadParticipant`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L10) | Contributes editor-owned live state to an assembly reload without coupling the reload requester to a feature domain. | — | — | +| [`Inno.Extensibility.Reload.IGenerationChange Inno.Editor.Core.IEditorReloadParticipant.Capture(Inno.Extensibility.Modules.AssemblyReloadContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L21) | Captures one isolated transaction for the prepared assembly generation.
`context`: The prepared assembly reload context containing previous and candidate participant state. | A transaction that has captured current state but has not modified live editor objects. | — | +| [`void Inno.Editor.Core.IEditorReloadParticipant.RefreshDiagnostics()`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/IEditorReloadParticipant.cs#L26) | Republishes diagnostics derived from the participant's current live state. | — | — | +| [`public sealed class Inno.Editor.Core.EditorContext`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L13) | Provides passive project and frame state shared by editor extensions. Interaction routing is supplied separately by Inno.Editor.Interactions. | — | — | +| [`public Inno.Editor.Core.EditorContext.EditorContext(string projectDirectory, Inno.Editor.Core.EditorKeyboardPolicy keyboard)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L31) | Creates a passive editor context for one project.
`projectDirectory`: The project root containing Assets and Library.
`keyboard`: The explicit immutable conventions selected by product composition. | — | `System.ArgumentNullException`: The keyboard policy is null.
`System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Core.EditorContext.projectDirectory { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L47) | Gets the normalized project root directory. | — | — | +| [`public Inno.Editor.Core.EditorKeyboardPolicy Inno.Editor.Core.EditorContext.keyboard { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L52) | Gets the immutable keyboard conventions shared by discovery, dispatch and presentation. | — | — | +| [`public Inno.Editor.Core.EditorStatistics Inno.Editor.Core.EditorContext.statistics { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L57) | Gets the frame-scoped statistics exchange used by independent editor features and viewers. | — | — | +| [`public string Inno.Editor.Core.EditorContext.layoutPath { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L63) | Gets the absolute path of the project editor layout document. | — | — | +| [`public string Inno.Editor.Core.EditorContext.imguiLayout { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L69) | Gets the Dear ImGui layout text without editor module or panel state sections. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorContext.GetLayoutSectionNames(string prefix = "")`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L86) | Gets layout section names matching an ordinal prefix.
`prefix`: The prefix to match, or an empty string for every section. | A stable sorted snapshot of matching section names. | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.Core.EditorContext.TryGetLayoutSection(string sectionName, out System.Collections.Generic.IReadOnlyDictionary values)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L104) | Tries to read one independent editor layout section snapshot.
`sectionName`: The section name without its INI header syntax.
`values`: The copied values when the section exists. | when the requested section exists. | `System.ArgumentException`: Thrown when is empty or cannot be represented in the layout. | +| [`public void Inno.Editor.Core.EditorContext.SetLayoutSection(string sectionName, System.Collections.Generic.IEnumerable> values)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L126) | Adds or replaces one human-readable editor layout section in memory.
`sectionName`: The section name without its INI header syntax.
`values`: The complete scalar or JSON-formatted values. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when a section name, key, or value cannot be represented in the layout. | +| [`public bool Inno.Editor.Core.EditorContext.RemoveLayoutSection(string sectionName)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L145) | Removes one editor layout section from the in-memory document.
`sectionName`: The section name without its INI header syntax. | when an existing section was removed. | `System.ArgumentException`: Thrown when is empty or cannot be represented in the layout. | +| [`public void Inno.Editor.Core.EditorContext.SetImGuiLayout(string? value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L154) | Replaces the Dear ImGui layout while retaining editor module and panel state sections.
`value`: The complete layout text returned by Dear ImGui. | — | — | +| [`public bool Inno.Editor.Core.EditorContext.SaveLayoutIfChanged()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L169) | Atomically saves the project editor layout when it changed. | when a changed document was written. | `System.IO.IOException`: Thrown when the layout document cannot be written atomically.
`System.UnauthorizedAccessException`: Thrown when the layout document is inaccessible. | +| [`public void Inno.Editor.Core.EditorContext.SaveLayout()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L181) | Atomically rewrites the complete project editor layout document. | — | `System.IO.IOException`: Thrown when the layout document cannot be written atomically.
`System.UnauthorizedAccessException`: Thrown when the layout document is inaccessible. | +| [`public Inno.Editor.Core.EditorFrame Inno.Editor.Core.EditorContext.frame { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L186) | Gets the latest immutable editor frame snapshot. | — | — | +| [`public bool Inno.Editor.Core.EditorContext.isFocused { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs#L191) | Gets whether any editor viewport currently owns application focus. | — | — | +| [`public readonly record struct Inno.Editor.Core.EditorFrame : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Describes one immutable editor frame.
`deltaTime`: The elapsed frame time in seconds.
`totalTime`: The absolute editor runtime in seconds.
`isFocused`: Whether an editor viewport owns application focus. | — | — | +| [`public Inno.Editor.Core.EditorFrame.EditorFrame(float deltaTime, float totalTime, bool isFocused)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Describes one immutable editor frame.
`deltaTime`: The elapsed frame time in seconds.
`totalTime`: The absolute editor runtime in seconds.
`isFocused`: Whether an editor viewport owns application focus. | — | — | +| [`public float Inno.Editor.Core.EditorFrame.deltaTime { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L16) | The elapsed frame time in seconds. | — | — | +| [`public float Inno.Editor.Core.EditorFrame.totalTime { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L17) | The absolute editor runtime in seconds. | — | — | +| [`public bool Inno.Editor.Core.EditorFrame.isFocused { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L18) | Whether an editor viewport owns application focus. | — | — | +| [`public override string Inno.Editor.Core.EditorFrame.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Core.EditorFrame.operator !=(Inno.Editor.Core.EditorFrame left, Inno.Editor.Core.EditorFrame right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Core.EditorFrame.operator ==(Inno.Editor.Core.EditorFrame left, Inno.Editor.Core.EditorFrame right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Core.EditorFrame.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Core.EditorFrame.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Core.EditorFrame.Equals(Inno.Editor.Core.EditorFrame other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Core.EditorFrame.Deconstruct(out float deltaTime, out float totalTime, out bool isFocused)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorFrame.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Core.EditorKeyboardPolicy`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L9) | Supplies immutable keyboard conventions selected by the Editor product composition. | — | — | +| [`public Inno.Editor.Core.EditorKeyboardPolicy.EditorKeyboardPolicy(Inno.Core.Input.KeyModifier primaryModifier, string superModifierLabel)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L23) | Defines the primary shortcut modifier and the display name of the Super modifier.
`primaryModifier`: One Control, Super or Alt modifier used by symbolic primary shortcuts.
`superModifierLabel`: The nonempty product-specific name displayed for the Super modifier. | — | `System.ArgumentException`: The modifier is not one supported modifier or the label is empty. | +| [`public Inno.Core.Input.KeyModifier Inno.Editor.Core.EditorKeyboardPolicy.primaryModifier { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L37) | Gets the modifier used when resolving symbolic primary shortcuts. | — | — | +| [`public string Inno.Editor.Core.EditorKeyboardPolicy.superModifierLabel { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorKeyboardPolicy.cs#L42) | Gets the product-selected display name of the Super modifier. | — | — | +| [`public abstract class Inno.Editor.Core.EditorRuntime : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L8) | Defines the presentation-independent lifecycle of an editor runtime. | — | — | +| [`protected Inno.Editor.Core.EditorRuntime.EditorRuntime(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L19) | Creates a runtime for the supplied passive context.
`context`: The shared editor context. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Core.EditorRuntime.context { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L27) | Gets the shared passive editor context. | — | — | +| [`public abstract void Inno.Editor.Core.EditorRuntime.Start()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L32) | Starts the runtime and activates its initial extension generation. | — | — | +| [`public abstract void Inno.Editor.Core.EditorRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L40) | Updates the runtime for one editor frame.
`frame`: The immutable frame state. | — | — | +| [`protected void Inno.Editor.Core.EditorRuntime.SetFrame(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L48) | Advances frame-scoped data and publishes the latest immutable frame.
`frame`: The frame to publish. | — | — | +| [`public abstract void Inno.Editor.Core.EditorRuntime.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorRuntime.cs#L57) | Stops the runtime and releases active extensions. | — | — | +| [`public abstract class Inno.Editor.Core.EditorState`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L14) | Provides the serialization-neutral parameter used to capture or restore one editor extension's project state. | — | — | +| [`protected Inno.Editor.Core.EditorState.EditorState()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L19) | Initializes the base contract for a runtime-owned editor state parameter. | — | — | +| [`public abstract T Inno.Editor.Core.EditorState.Get(string key, T fallback)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L42) | Reads a compatible value or returns the caller-provided fallback.
`key`: The stable key local to the owning module or panel.
`fallback`: The value returned when the key is absent, malformed, or incompatible with .
`T`: The neutral value type expected by the caller. | The stored compatible value, or . | `System.ArgumentException`: Thrown when is empty. | +| [`public abstract void Inno.Editor.Core.EditorState.Set(string key, T value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorState.cs#L68) | Writes one neutral value under a stable extension-local key.
`key`: The stable key local to the owning module or panel.
`value`: The value to persist.
`T`: The neutral value type supplied by the caller. | — | `System.ArgumentException`: Thrown when is empty.
`System.InvalidOperationException`: Thrown when the state parameter was supplied for restoration and is therefore read-only.
`System.NotSupportedException`: Thrown when cannot be represented by the runtime state serializer. | +| [`public readonly record struct Inno.Editor.Core.EditorStatisticId : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Identifies one editor statistic across panels, modules, and reloadable extensions. | — | — | +| [`public Inno.Editor.Core.EditorStatisticId.EditorStatisticId(string value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L21) | Creates a globally stable statistic identifier.
`value`: Non-empty globally stable identity. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Core.EditorStatisticId.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L30) | Gets the globally stable identity. | — | — | +| [`public bool Inno.Editor.Core.EditorStatisticId.isValid { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L35) | Gets whether this identifier contains a usable value. | — | — | +| [`public override string Inno.Editor.Core.EditorStatisticId.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L43) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static bool Inno.Editor.Core.EditorStatisticId.operator !=(Inno.Editor.Core.EditorStatisticId left, Inno.Editor.Core.EditorStatisticId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Core.EditorStatisticId.operator ==(Inno.Editor.Core.EditorStatisticId left, Inno.Editor.Core.EditorStatisticId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Core.EditorStatisticId.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Core.EditorStatisticId.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Core.EditorStatisticId.Equals(Inno.Editor.Core.EditorStatisticId other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L10) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly record struct Inno.Editor.Core.EditorStatisticGroupId : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Identifies one visual group in the editor statistics presentation. | — | — | +| [`public Inno.Editor.Core.EditorStatisticGroupId.EditorStatisticGroupId(string value)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L60) | Creates a globally stable statistic-group identifier.
`value`: Non-empty globally stable identity. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Core.EditorStatisticGroupId.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L69) | Gets the globally stable identity. | — | — | +| [`public bool Inno.Editor.Core.EditorStatisticGroupId.isValid { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L74) | Gets whether this identifier contains a usable value. | — | — | +| [`public override string Inno.Editor.Core.EditorStatisticGroupId.ToString()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L82) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static bool Inno.Editor.Core.EditorStatisticGroupId.operator !=(Inno.Editor.Core.EditorStatisticGroupId left, Inno.Editor.Core.EditorStatisticGroupId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Core.EditorStatisticGroupId.operator ==(Inno.Editor.Core.EditorStatisticGroupId left, Inno.Editor.Core.EditorStatisticGroupId right)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Core.EditorStatisticGroupId.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Core.EditorStatisticGroupId.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Core.EditorStatisticGroupId.Equals(Inno.Editor.Core.EditorStatisticGroupId other)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L49) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Core.EditorStatistic`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L88) | Describes one immutable, presentation-ready statistic without retaining provider instances or types. | — | — | +| [`public Inno.Editor.Core.EditorStatistic.EditorStatistic(Inno.Editor.Core.EditorStatisticId id, Inno.Editor.Core.EditorStatisticGroupId groupId, string groupName, string label, string value, int groupOrder = 0, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L117) | Creates one statistic contribution.
`id`: Globally stable statistic identity used for replacement within a frame.
`groupId`: Stable visual group identity.
`groupName`: User-facing group heading.
`label`: User-facing metric label.
`value`: Already formatted user-facing value.
`groupOrder`: Ascending group presentation order.
`order`: Ascending metric order within the group. | — | `System.ArgumentException`: Thrown when an identifier, group name, label, or value is empty. | +| [`public Inno.Editor.Core.EditorStatisticId Inno.Editor.Core.EditorStatistic.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L145) | Gets the globally stable statistic identity. | — | — | +| [`public Inno.Editor.Core.EditorStatisticGroupId Inno.Editor.Core.EditorStatistic.groupId { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L150) | Gets the stable visual group identity. | — | — | +| [`public string Inno.Editor.Core.EditorStatistic.groupName { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L155) | Gets the user-facing group heading. | — | — | +| [`public string Inno.Editor.Core.EditorStatistic.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L160) | Gets the user-facing metric label. | — | — | +| [`public string Inno.Editor.Core.EditorStatistic.value { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L165) | Gets the presentation-ready value. | — | — | +| [`public int Inno.Editor.Core.EditorStatistic.groupOrder { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L170) | Gets ascending group presentation order. | — | — | +| [`public int Inno.Editor.Core.EditorStatistic.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L175) | Gets ascending metric order within the group. | — | — | +| [`public sealed class Inno.Editor.Core.EditorStatistics`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | Exchanges frame-scoped, reload-safe statistics between independent editor features and viewers. | — | — | +| [`public void Inno.Editor.Core.EditorStatistics.Publish(Inno.Editor.Core.EditorStatistic statistic)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L200) | Publishes or replaces one statistic for the current editor frame.
`statistic`: Immutable contribution containing no runtime provider references. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.Core.EditorStatistics.Publish(System.Collections.Generic.IEnumerable statistics)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L216) | Publishes or replaces several statistics for the current editor frame.
`statistics`: Contributions to publish in enumeration order. | — | `System.ArgumentNullException`: Thrown when or one of its values is null. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorStatistics.GetSnapshot()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L230) | Gets a deterministic snapshot combining current contributions with the previous completed frame for providers that draw after their consumer. | A detached, sorted snapshot containing at most one value for each stable statistic ID. | — | +| [`public Inno.Editor.Core.EditorStatistics.EditorStatistics()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):公开引用边界由实际签名核对。 -- [Inno.Scripting.Reload](../scripting/Inno.Scripting.Reload.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Input](../core/Inno.Core.Input.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 公开签名依赖 | +| `Inno.Scripting.Reload` | 公开签名依赖 | +| `Inno.Extensibility.Reload` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Input` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Diagnostics.md b/docs/editor/Inno.Editor.Diagnostics.md index e62b82ef0..f18371fd4 100644 --- a/docs/editor/Inno.Editor.Diagnostics.md +++ b/docs/editor/Inno.Editor.Diagnostics.md @@ -20,9 +20,7 @@ Console timeline 与 collapse group 都按 occurrence sequence 正序排列: ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Diagnostics.EditorConsole` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -35,51 +33,14 @@ Console timeline 与 collapse group 都按 occurrence sequence 正序排列: - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Diagnostics.EditorConsole.EditorConsole(Inno.Core.Logging.LogRouter logRouter, Inno.Core.Diagnostics.DiagnosticHub diagnosticHub, Inno.Editor.PlayMode.IEditorPlayMode playMode)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L45) | Creates a Console service that observes one editor Play Mode controller.
`logRouter`:The host logging router whose entries are presented by this Console.
`diagnosticHub`:The host diagnostic hub whose current reports are presented by this Console.
`playMode`:The Play Mode source used to clear ordinary logs when a new simulation request begins. | — | `System.ArgumentNullException`:Thrown when playMode is null. | -| [`public Inno.Editor.Diagnostics.EditorConsoleSnapshot Inno.Editor.Diagnostics.EditorConsole.Capture()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L158) | Captures an immutable snapshot containing both individual occurrences and global groups.
返回:A consistent snapshot that remains valid while new entries arrive. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Clear()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L178) | Removes retained logs while keeping currently reported issues visible until their owners resolve them. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Clear(Inno.Core.Diagnostics.DiagnosticSource source)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L143) | Removes the current diagnostic report for one producer.
`source`:The producer whose report should be removed. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L192) | Detaches this service and releases its subscriptions. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Receive(Inno.Core.Logging.LogEntry entry)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L108) | Appends one asynchronously dispatched log entry.
`entry`:The immutable entry to append. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Replace(Inno.Core.Diagnostics.DiagnosticReport report)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L124) | Replaces the complete current diagnostic report for one producer.
`report`:The complete report to expose in subsequent snapshots. | — | — | -| [`public void Inno.Editor.Diagnostics.EditorConsole.Start()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L91) | Attaches this service to the process logging, diagnostic, and Play Mode sources. | — | `System.ObjectDisposedException`:Thrown after this service has been disposed. | -| [`public int Inno.Editor.Diagnostics.EditorConsole.capacity { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L58) | Gets or sets the maximum number of ordinary log occurrences retained in memory. | — | — | -| [`public bool Inno.Editor.Diagnostics.EditorConsole.clearOnPlay { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L83) | Gets or sets whether ordinary Console logs are cleared when a new Play Mode request begins. Current diagnostics remain visible because they represent active compiler and subsystem state. | — | — | -| [`public sealed class Inno.Editor.Diagnostics.EditorConsole : Inno.Editor.Diagnostics.IEditorConsole, Inno.Core.Logging.ILogSink, Inno.Core.Diagnostics.IDiagnosticSink, System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L15) | Owns one editor's bounded Console history, current diagnostics, grouping, and Play Mode clearing policy. | — | — | - -### `Inno.Editor.Diagnostics.EditorConsoleEntryKind` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Diagnostics.EditorConsoleEntryKind.Diagnostic = 1`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L16) | The occurrence represents current state from a diagnostic producer. | — | — | -| [`Inno.Editor.Diagnostics.EditorConsoleEntryKind.Log = 0`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L11) | The occurrence was emitted through the runtime logging pipeline. | — | — | -| [`public enum Inno.Editor.Diagnostics.EditorConsoleEntryKind : int`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L6) | Identifies the source protocol represented by one editor Console occurrence. | — | — | - -### `Inno.Editor.Diagnostics.EditorConsoleGroup` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public int Inno.Editor.Diagnostics.EditorConsoleGroup.count { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L38) | Gets the total number of matching occurrences. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleGroup.identity { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L23) | Gets the deterministic content fingerprint used as the presentation identity. | — | — | -| [`public Inno.Editor.Diagnostics.EditorConsoleOccurrence Inno.Editor.Diagnostics.EditorConsoleGroup.latest { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L33) | Gets the most recently received matching occurrence. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleGroup.occurrences { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L28) | Gets every matching occurrence in arrival order. | — | — | -| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleGroup`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L9) | Groups equivalent Console occurrences regardless of whether they arrived consecutively. | — | — | - -### `Inno.Editor.Diagnostics.EditorConsoleOccurrence` - @@ -96,60 +57,69 @@ Console timeline 与 collapse group 都按 occurrence sequence 正序排列: - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.category { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L77) | Gets the producer-defined category. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.code { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L72) | Gets the diagnostic code, or an empty string for uncoded entries. | — | — | -| [`public int Inno.Editor.Diagnostics.EditorConsoleOccurrence.column { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L107) | Gets the one-based source column, or zero when unavailable. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.displayMessage { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L87) | Gets the message text prefixed by its diagnostic code when one is present. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.file { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L97) | Gets the related source path, or an empty string when unavailable. | — | — | -| [`public Inno.Editor.Diagnostics.EditorConsoleEntryKind Inno.Editor.Diagnostics.EditorConsoleOccurrence.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L52) | Gets the source protocol represented by this occurrence. | — | — | -| [`public Inno.Core.Logging.LogLevel Inno.Editor.Diagnostics.EditorConsoleOccurrence.level { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L57) | Gets the normalized Console severity. | — | — | -| [`public int Inno.Editor.Diagnostics.EditorConsoleOccurrence.line { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L102) | Gets the one-based source line, or zero when unavailable. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L82) | Gets the original message text. | — | — | -| [`public long Inno.Editor.Diagnostics.EditorConsoleOccurrence.sequence { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L47) | Gets the process-local monotonic sequence assigned when this occurrence entered the Console. | — | — | -| [`public Inno.Core.Logging.LogSessionId Inno.Editor.Diagnostics.EditorConsoleOccurrence.sessionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L117) | Gets the isolated runtime session that emitted this occurrence. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L62) | Gets the human-readable producer name. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.sourceId { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L67) | Gets the stable diagnostic producer identifier, or an empty string for log entries. | — | — | -| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.stackTrace { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L112) | Gets the captured stack trace, or an empty string when unavailable. | — | — | -| [`public System.DateTime Inno.Editor.Diagnostics.EditorConsoleOccurrence.time { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L92) | Gets the occurrence timestamp. | — | — | -| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleOccurrence`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L10) | Describes one immutable log or diagnostic occurrence in the editor Console timeline. | — | — | - -### `Inno.Editor.Diagnostics.EditorConsoleSnapshot` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleSnapshot.groups { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L34) | Gets global fingerprint groups ordered by their latest occurrence. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleSnapshot.occurrences { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L29) | Gets every current occurrence in chronological arrival order. | — | — | -| [`public long Inno.Editor.Diagnostics.EditorConsoleSnapshot.revision { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L24) | Gets the monotonic revision of the captured Console state. | — | — | -| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleSnapshot`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L9) | Provides one immutable and internally consistent view of editor Console state. | — | — | - -### `Inno.Editor.Diagnostics.IEditorConsole` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`Inno.Editor.Diagnostics.EditorConsoleSnapshot Inno.Editor.Diagnostics.IEditorConsole.Capture()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L25) | Captures an immutable snapshot containing both individual occurrences and global groups.
返回:A consistent snapshot that remains valid while new entries arrive. | — | — | -| [`void Inno.Editor.Diagnostics.IEditorConsole.Clear()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L30) | Removes retained logs while keeping currently reported issues visible until their owners resolve them. | — | — | +| [`public sealed class Inno.Editor.Diagnostics.EditorConsole : Inno.Editor.Diagnostics.IEditorConsole, Inno.Core.Logging.ILogSink, Inno.Core.Diagnostics.IDiagnosticSink, System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L15) | Owns one editor's bounded Console history, current diagnostics, grouping, and Play Mode clearing policy. | — | — | +| [`public Inno.Editor.Diagnostics.EditorConsole.EditorConsole(Inno.Core.Logging.LogRouter logRouter, Inno.Core.Diagnostics.DiagnosticHub diagnosticHub, Inno.Editor.PlayMode.IEditorPlayMode playMode)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L46) | Creates a Console service that observes one editor Play Mode controller.
`logRouter`: The host logging router whose entries are presented by this Console.
`diagnosticHub`: The host diagnostic hub whose current reports are presented by this Console.
`playMode`: The Play Mode source used to clear ordinary logs when a new simulation request begins. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public int Inno.Editor.Diagnostics.EditorConsole.capacity { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L59) | Gets or sets the maximum number of ordinary log occurrences retained in memory. | — | — | +| [`public bool Inno.Editor.Diagnostics.EditorConsole.clearOnPlay { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L84) | Gets or sets whether ordinary Console logs are cleared when a new Play Mode request begins. Current diagnostics remain visible because they represent active compiler and subsystem state. | — | — | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Start()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L92) | Attaches this service to the process logging, diagnostic, and Play Mode sources. | — | `System.ObjectDisposedException`: Thrown after this service has been disposed. | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Receive(Inno.Core.Logging.LogEntry entry)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L120) | Appends one asynchronously dispatched log entry.
`entry`: The immutable entry to append. | — | — | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Replace(Inno.Core.Diagnostics.DiagnosticReport report)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L136) | Replaces the complete current diagnostic report for one producer.
`report`: The complete report to expose in subsequent snapshots. | — | — | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Clear(Inno.Core.Diagnostics.DiagnosticSource source)`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L155) | Removes the current diagnostic report for one producer.
`source`: The producer whose report should be removed. | — | — | +| [`public Inno.Editor.Diagnostics.EditorConsoleSnapshot Inno.Editor.Diagnostics.EditorConsole.Capture()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L170) | Captures an immutable snapshot containing both individual occurrences and global groups. | A consistent snapshot that remains valid while new entries arrive. | — | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Clear()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L190) | Removes retained logs while keeping currently reported issues visible until their owners resolve them. | — | — | +| [`public void Inno.Editor.Diagnostics.EditorConsole.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs#L204) | Detaches this service and releases its subscriptions. | — | — | +| [`public enum Inno.Editor.Diagnostics.EditorConsoleEntryKind : int`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L6) | Identifies the source protocol represented by one editor Console occurrence. | — | — | +| [`Inno.Editor.Diagnostics.EditorConsoleEntryKind.Log = 0`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L11) | The occurrence was emitted through the runtime logging pipeline. | — | — | +| [`Inno.Editor.Diagnostics.EditorConsoleEntryKind.Diagnostic = 1`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleEntryKind.cs#L16) | The occurrence represents current state from a diagnostic producer. | — | — | +| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleGroup`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L9) | Groups equivalent Console occurrences regardless of whether they arrived consecutively. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleGroup.identity { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L23) | Gets the deterministic content fingerprint used as the presentation identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleGroup.occurrences { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L28) | Gets every matching occurrence in arrival order. | — | — | +| [`public Inno.Editor.Diagnostics.EditorConsoleOccurrence Inno.Editor.Diagnostics.EditorConsoleGroup.latest { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L33) | Gets the most recently received matching occurrence. | — | — | +| [`public int Inno.Editor.Diagnostics.EditorConsoleGroup.count { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleGroup.cs#L38) | Gets the total number of matching occurrences. | — | — | +| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleOccurrence`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L10) | Describes one immutable log or diagnostic occurrence in the editor Console timeline. | — | — | +| [`public long Inno.Editor.Diagnostics.EditorConsoleOccurrence.sequence { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L47) | Gets the process-local monotonic sequence assigned when this occurrence entered the Console. | — | — | +| [`public Inno.Editor.Diagnostics.EditorConsoleEntryKind Inno.Editor.Diagnostics.EditorConsoleOccurrence.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L52) | Gets the source protocol represented by this occurrence. | — | — | +| [`public Inno.Core.Logging.LogLevel Inno.Editor.Diagnostics.EditorConsoleOccurrence.level { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L57) | Gets the normalized Console severity. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L62) | Gets the human-readable producer name. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.sourceId { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L67) | Gets the stable diagnostic producer identifier, or an empty string for log entries. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.code { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L72) | Gets the diagnostic code, or an empty string for uncoded entries. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.category { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L77) | Gets the producer-defined category. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L82) | Gets the original message text. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.displayMessage { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L87) | Gets the message text prefixed by its diagnostic code when one is present. | — | — | +| [`public System.DateTime Inno.Editor.Diagnostics.EditorConsoleOccurrence.time { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L92) | Gets the occurrence timestamp. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.file { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L97) | Gets the related source path, or an empty string when unavailable. | — | — | +| [`public int Inno.Editor.Diagnostics.EditorConsoleOccurrence.line { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L102) | Gets the one-based source line, or zero when unavailable. | — | — | +| [`public int Inno.Editor.Diagnostics.EditorConsoleOccurrence.column { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L107) | Gets the one-based source column, or zero when unavailable. | — | — | +| [`public string Inno.Editor.Diagnostics.EditorConsoleOccurrence.stackTrace { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L112) | Gets the captured stack trace, or an empty string when unavailable. | — | — | +| [`public Inno.Core.Logging.LogSessionId Inno.Editor.Diagnostics.EditorConsoleOccurrence.sessionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleOccurrence.cs#L117) | Gets the isolated runtime session that emitted this occurrence. | — | — | +| [`public sealed class Inno.Editor.Diagnostics.EditorConsoleSnapshot`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L9) | Provides one immutable and internally consistent view of editor Console state. | — | — | +| [`public long Inno.Editor.Diagnostics.EditorConsoleSnapshot.revision { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L24) | Gets the monotonic revision of the captured Console state. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleSnapshot.occurrences { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L29) | Gets every current occurrence in chronological arrival order. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Diagnostics.EditorConsoleSnapshot.groups { get; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsoleSnapshot.cs#L34) | Gets global fingerprint groups ordered by their latest occurrence. | — | — | +| [`public interface Inno.Editor.Diagnostics.IEditorConsole`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L6) | Exposes read-only editor Console snapshots and explicit retention controls to editor features. | — | — | | [`int Inno.Editor.Diagnostics.IEditorConsole.capacity { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L11) | Gets or sets the maximum number of ordinary log occurrences retained in memory. | — | — | | [`bool Inno.Editor.Diagnostics.IEditorConsole.clearOnPlay { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L17) | Gets or sets whether ordinary Console logs are cleared when a new Play Mode request begins. Current diagnostics remain visible because they represent active compiler and subsystem state. | — | — | -| [`public interface Inno.Editor.Diagnostics.IEditorConsole`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L6) | Exposes read-only editor Console snapshots and explicit retention controls to editor features. | — | — | +| [`Inno.Editor.Diagnostics.EditorConsoleSnapshot Inno.Editor.Diagnostics.IEditorConsole.Capture()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L25) | Captures an immutable snapshot containing both individual occurrences and global groups. | A consistent snapshot that remains valid while new entries arrive. | — | +| [`void Inno.Editor.Diagnostics.IEditorConsole.Clear()`](../../src/composition/editor/framework/Inno.Editor.Diagnostics/IEditorConsole.cs#L30) | Removes retained logs while keeping currently reported issues visible until their owners resolve them. | — | — | ## 项目依赖 -- [Inno.Editor.PlayMode](Inno.Editor.PlayMode.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.PlayMode` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Exporting.md b/docs/editor/Inno.Editor.Exporting.md index a5ecc6d76..ed4a3391b 100644 --- a/docs/editor/Inno.Editor.Exporting.md +++ b/docs/editor/Inno.Editor.Exporting.md @@ -32,21 +32,58 @@ Editor/CLI 已完成这个协调;源码工作区中的 Project 用户不需要 - [Inno.Build](../build/Inno.Build.md):公开构建请求、结果与目标边界。 - [Inno.Build.Cli](../build/Inno.Build.Cli.md):使用同一 Build Pipeline 的无界面入口。 -## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +## 异步服务与界面分离 + +EditorExportService 是无界面的导出 owner:StartGame/StartPlugin、Cancel、progress、错误和任务 drain 全部复用 Build pipeline 与 LifetimeScope。Workbench 拥有输入表单与阻塞 modal。服务不引用 ImGui、Panel 或 hosting;关闭时先取消并等待任务释放资源,不能让隐藏窗口丢弃活动导出。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public sealed class Inno.Editor.Exporting.EditorExportService : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L20) | Owns asynchronous export, progress, cancellation and retirement independently from presentation. | — | — | +| [`public bool Inno.Editor.Exporting.EditorExportService.isPluginBusy { get; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L42) | Gets whether a Plugin source export still owns build resources. | — | — | +| [`public bool Inno.Editor.Exporting.EditorExportService.isGameBusy { get; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L46) | Gets whether a game export or its Support Pack preparation is active. | — | — | +| [`public string Inno.Editor.Exporting.EditorExportService.status { get; private set; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L50) | Gets the latest owner-thread progress message. | — | — | +| [`public string Inno.Editor.Exporting.EditorExportService.error { get; private set; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L54) | Gets the last failure or cancellation diagnostic, or an empty string. | — | — | +| [`public float Inno.Editor.Exporting.EditorExportService.gameProgress { get; private set; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L58) | Gets monotonic progress for the current game build, from zero through one. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Exporting.EditorExportService.availableGameTargets { get; }`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L62) | Gets the targets registered by the active Build distribution. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Exporting.EditorExportService.GetManagedDeployments(Inno.Build.BuildTargetId target)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L73) | Gets deployment implementations registered for a target.
`target`: The explicitly chosen build target. | The registered deployment choices; no host fallback is introduced. | — | +| [`public string Inno.Editor.Exporting.EditorExportService.GetGameTargetDisplayName(Inno.Build.BuildTargetId target)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L83) | Gets a registered target label or a diagnostic for an unavailable target.
`target`: The target to describe. | A presentation label; never used as a routing identity. | — | +| [`public void Inno.Editor.Exporting.EditorExportService.StartPlugin(Inno.Build.PluginBuildRequest request)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L96) | Starts a source-package export using the existing Build pipeline.
`request`: The complete, explicitly selected package request. | — | `System.InvalidOperationException`: Another export still owns the service. | +| [`public void Inno.Editor.Exporting.EditorExportService.StartGame(Inno.Build.GameBuildRequest request)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L115) | Prepares the selected Support Pack before capturing and building the game.
`request`: The complete game request; no form or window state is retained. | — | `System.InvalidOperationException`: Another export still owns the service. | +| [`public void Inno.Editor.Exporting.EditorExportService.Cancel()`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L137) | Requests cancellation without releasing resources before their owned work drains. | — | — | +| [`protected override void Inno.Editor.Exporting.EditorExportService.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L157) | Advances compilation tickets, build progress, and completed export tasks.
`context`: The active Editor context for the current frame. | — | — | +| [`protected override void Inno.Editor.Exporting.EditorExportService.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L177) | Cancels and drains active export work before the Editor generation is stopped.
`context`: The active Editor context being stopped. | — | — | +| [`protected override void Inno.Editor.Exporting.EditorExportService.OnDispose()`](../../src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs#L185) | Drains export cleanup before releasing cancellation resources owned by this module. | — | — | ## 项目依赖 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build](../build/Inno.Build.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Managed](../build/Inno.Build.Managed.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Build.Managed` | 公开签名依赖 | +| `Inno.Build` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Graph.ImGui.md b/docs/editor/Inno.Editor.Graph.ImGui.md new file mode 100644 index 000000000..434ce42d8 --- /dev/null +++ b/docs/editor/Inno.Editor.Graph.ImGui.md @@ -0,0 +1,58 @@ +# Inno.Editor.Graph.ImGui + +[Editor 索引](README.md) · [Wiki 首页](../README.md) · [UI 架构](../architecture/EDITOR_UI_ARCHITECTURE.md) + +## 职责与边界 + +通用 Graph 画布呈现。依赖中立 Graph/Graphs 与 ImGui;禁止引用 Rendering、Shader 或具体 Panel。 + +## 依赖与初始化 + +产品通过 `EditorProduct.props` 显式部署此程序集。由当前 TypeRegistry/Editor Registry 发现声明并在候选成功后切换;不扫描目录、不建立独立注册表或全局状态。 + +## API 与扩展点 + +GraphCanvasView 组合当前 GraphCanvasState、画布原点和尺寸,提供 ToScreen、ToGraph、DrawGrid、DrawConnection 和 HitConnection。它不修改 graph,也不拥有 History、选择或资源。 + +## 工作流 + +每次绘制按当前 viewport 创建值对象;同一连接曲线用于绘制与命中测试。屏幕坐标只在当前帧有效。 + +## 错误、生命周期与热重载 + +控件只在当前 EditorUiSession 的 owner thread、作用域与原生上下文中使用。跨帧只保留稳定 ID 和中立状态;Missing、只读和未赋值状态保持区别。手势失败保留 History 所有权和诊断;扩展在所属 generation 退休,不保留插件 Type、delegate 或实例。 + +所有公开签名的当前映射见本页 API 清单;内部实现不构成稳定扩展契约。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasView`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L11) | Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasView.GraphCanvasView(Inno.Editor.Graph.GraphCanvasState state, System.Numerics.Vector2 origin, System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L31) | Creates a frame-local graph presenter over navigation owned by its document.
`state`: The existing neutral navigation and selection state.
`origin`: The screen-space canvas origin.
`size`: The finite nonnegative canvas extent. | — | `System.ArgumentOutOfRangeException`: The canvas geometry is invalid. | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasView.ToScreen(Inno.Core.Graphs.GraphPosition point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L55) | Projects a graph-space location into the current screen rectangle.
`point`: The domain-independent graph coordinate. | The corresponding screen coordinate. | — | +| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasView.ToGraph(System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L65) | Converts a screen location back into graph coordinates.
`point`: The screen coordinate to project. | The graph coordinate under the pointer. | — | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawGrid(uint color, float spacing = 32)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L78) | Draws a bounded graph-space grid through the current UI drawing context.
`color`: The packed presentation color.
`spacing`: Positive graph-space grid spacing. | — | `System.ArgumentOutOfRangeException`: Spacing is nonpositive or nonfinite. | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawConnection(System.Numerics.Vector2 output, System.Numerics.Vector2 input, uint color, float thickness = 2)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L106) | Draws a horizontal-tangent connection using the same curve used for hit testing.
`output`: The output endpoint in screen space.
`input`: The input endpoint in screen space.
`color`: The packed connection color.
`thickness`: The positive screen-space stroke width. | — | — | +| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasView.HitConnection(System.Numerics.Vector2 point, System.Numerics.Vector2 output, System.Numerics.Vector2 input, float radius = 6)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L137) | Checks pointer distance against the displayed cubic connection without any UI or document access.
`point`: The screen-space pointer location.
`output`: The displayed output endpoint.
`input`: The displayed input endpoint.
`radius`: Nonnegative screen-space acceptance radius. | True when the pointer is within the curve's hit area, including its endpoints. | `System.ArgumentOutOfRangeException`: Radius is negative or nonfinite. | + +## 项目依赖 + +| 工程 | 边界 | +| --- | --- | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Core.Graphs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Editor.Graph` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Graph.md b/docs/editor/Inno.Editor.Graph.md index 39544fe2d..18142fc55 100644 --- a/docs/editor/Inno.Editor.Graph.md +++ b/docs/editor/Inno.Editor.Graph.md @@ -31,9 +31,7 @@ GraphDocument 是可编辑创作模型,不是假称不可变的运行快照。 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Graph.GraphCanvasState` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -52,41 +50,9 @@ GraphDocument 是可编辑创作模型,不是假称不可变的运行快照。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Graph.GraphCanvasState.GraphCanvasState()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L12) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.BeginConnection(Inno.Core.Graphs.GraphEndpoint endpoint, Inno.Core.Graphs.GraphPortDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L164) | Begins a connection drag from one endpoint.
`endpoint`:Stable endpoint.
`direction`:Direction of the endpoint within its node interface. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.BeginConnection(Inno.Core.Graphs.GraphEndpoint output)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L178) | Begins a connection drag from one output endpoint.
`output`:Stable output endpoint. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.CancelConnection()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L183) | Cancels any active connection drag. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.ClearSelection()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L147) | Clears all transient node and edge selection. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.PanBy(float deltaX, float deltaY)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L82) | Moves the canvas origin by screen-space pixels.
`deltaX`:Horizontal screen-space delta.
`deltaY`:Vertical screen-space delta. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.SelectNodes(System.Collections.Generic.IEnumerable nodes)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L121) | Replaces node selection.
`nodes`:Complete selected-node set. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.SetViewport(Inno.Core.Graphs.GraphPosition pan, float zoom)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L58) | Restores persistent canvas navigation state with bounded zoom.
`pan`:Graph-space pan offset.
`zoom`:Requested zoom in the inclusive range 0.1 to 4. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.ToggleNode(Inno.Core.Graphs.GraphNodeId nodeId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L135) | Toggles one node while preserving the remaining selection.
`nodeId`:Node to toggle. | — | — | -| [`public void Inno.Editor.Graph.GraphCanvasState.ZoomAt(float factor, float pivotX, float pivotY)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L99) | Changes zoom while preserving the graph point beneath a screen-space pivot.
`factor`:Positive multiplicative zoom factor.
`pivotX`:Screen-space pivot X.
`pivotY`:Screen-space pivot Y. | — | — | -| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.GraphCanvasState.pan { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L22) | Gets the graph-space origin mapped to canvas screen origin. | — | — | -| [`public Inno.Core.Graphs.GraphEndpoint? Inno.Editor.Graph.GraphCanvasState.pendingConnection { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L42) | Gets the endpoint currently being connected, or null. | — | — | -| [`public Inno.Core.Graphs.GraphPortDirection? Inno.Editor.Graph.GraphCanvasState.pendingConnectionDirection { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L47) | Gets the direction of Inno.Editor.Graph.GraphCanvasState.pendingConnection. | — | — | -| [`public System.Collections.Generic.IReadOnlyCollection Inno.Editor.Graph.GraphCanvasState.selectedEdges { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L37) | Gets selected edge identities. | — | — | -| [`public System.Collections.Generic.IReadOnlyCollection Inno.Editor.Graph.GraphCanvasState.selectedNodes { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L32) | Gets selected node identities. | — | — | -| [`public float Inno.Editor.Graph.GraphCanvasState.zoom { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L27) | Gets the current graph-to-screen scale. | — | — | -| [`public sealed class Inno.Editor.Graph.GraphCanvasState`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L12) | Stores transient pan, zoom, selection and connection interaction state without ImGui dependencies. | — | — | - -### `Inno.Editor.Graph.GraphClipboardData` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public int Inno.Editor.Graph.GraphClipboardData.edgeCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L28) | Gets the copied connection count whose endpoints both belong to the fragment. | — | — | -| [`public int Inno.Editor.Graph.GraphClipboardData.nodeCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L23) | Gets the copied node count. | — | — | -| [`public sealed class Inno.Editor.Graph.GraphClipboardData`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L14) | Contains a neutral copied node fragment suitable for graph clipboard operations. | — | — | - -### `Inno.Editor.Graph.GraphDocumentController` - @@ -104,29 +70,6 @@ GraphDocument 是可编辑创作模型,不是假称不可变的运行快照。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Core.Graphs.GraphNodeId Inno.Editor.Graph.GraphDocumentController.AddNode(string definitionId, Inno.Core.Graphs.GraphPosition position, System.Collections.Generic.IReadOnlyDictionary? values = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L122) | Adds a node with a generated stable identity.
返回:The new stable node identity.
`definitionId`:Stable node definition ID.
`position`:Initial graph-space position.
`values`:Optional neutral property values. | — | — | -| [`public Inno.Core.Graphs.GraphEdgeId Inno.Editor.Graph.GraphDocumentController.Connect(Inno.Core.Graphs.GraphEndpoint output, Inno.Core.Graphs.GraphEndpoint input)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L217) | Creates or reconnects one input endpoint to an output endpoint.
返回:The generated connection identity.
`output`:Source endpoint.
`input`:Destination endpoint. | — | — | -| [`public Inno.Editor.Graph.GraphClipboardData Inno.Editor.Graph.GraphDocumentController.Copy(System.Collections.Generic.IEnumerable nodeIds)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L331) | Copies selected nodes and only their internal connections.
返回:A detached neutral clipboard fragment.
`nodeIds`:Nodes to copy. | — | — | -| [`public bool Inno.Editor.Graph.GraphDocumentController.Disconnect(Inno.Core.Graphs.GraphEdgeId edgeId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L254) | Removes one stable connection.
返回:true after a connection was removed.
`edgeId`:Connection to remove. | — | — | -| [`public void Inno.Editor.Graph.GraphDocumentController.MarkSaved()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L82) | Marks the current revision as saved without creating a data History entry. | — | — | -| [`public void Inno.Editor.Graph.GraphDocumentController.MoveNodes(System.Collections.Generic.IReadOnlyDictionary positions)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L180) | Moves one or more nodes as one completed gesture, independently undoable from earlier drags.
`positions`:Complete destination positions keyed by node identity. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.GraphDocumentController.Paste(Inno.Editor.Graph.GraphClipboardData clipboard, Inno.Core.Graphs.GraphPosition offset, System.Action>? remapNode = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L365) | Pastes a clipboard fragment with new stable identities and a graph-space offset.
返回:New node identities in clipboard document order.
`clipboard`:Detached neutral fragment.
`offset`:Offset added to every copied node position.
`remapNode`:Optional synchronous domain remapping of neutral node values after identity allocation. The callback is never stored in History. | — | — | -| [`public void Inno.Editor.Graph.GraphDocumentController.RemoveNodes(System.Collections.Generic.IEnumerable nodeIds)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L151) | Removes nodes and all incident connections as one structural edit.
`nodeIds`:Nodes to remove. | — | — | -| [`public bool Inno.Editor.Graph.GraphDocumentController.RemoveNodeValue(Inno.Core.Graphs.GraphNodeId nodeId, string propertyId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L307) | Removes one neutral node property as a structural edit.
返回:true when the property existed.
`nodeId`:Owning node.
`propertyId`:Stable property ID. | — | — | -| [`public void Inno.Editor.Graph.GraphDocumentController.ReplaceDocument(Inno.Core.Graphs.GraphDocument replacement, string historyName, string? gestureId = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L96) | Replaces the complete neutral document as one atomic, undoable authoring operation.
`replacement`:Complete replacement document copied by the operation.
`historyName`:Concise artist-facing history entry name.
`gestureId`:Unique active gesture identity for continuous samples, or null for an independent edit. | — | — | -| [`public void Inno.Editor.Graph.GraphDocumentController.SetNodeValue(Inno.Core.Graphs.GraphNodeId nodeId, string propertyId, Inno.Core.Graphs.GraphSerializedValue value, string? gestureId = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L280) | Creates or replaces one neutral node property; only samples carrying the same explicit gesture identity merge.
`nodeId`:Owning node.
`propertyId`:Stable property ID.
`value`:New neutral value.
`gestureId`:Unique current text/numeric gesture identity, or null for an independent edit. | — | — | -| [`public Inno.Core.Graphs.GraphDocument Inno.Editor.Graph.GraphDocumentController.document { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L67) | Gets the mutable neutral document used by the current asset generation. | — | — | -| [`public System.Guid Inno.Editor.Graph.GraphDocumentController.documentId { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L57) | Gets the stable project-relative document identity. | — | — | -| [`public bool Inno.Editor.Graph.GraphDocumentController.isAvailable { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L62) | Gets whether the current document identity and source are both available. | — | — | -| [`public bool Inno.Editor.Graph.GraphDocumentController.isDirty { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L77) | Gets whether content changed after its last explicit saved marker. | — | — | -| [`public ulong Inno.Editor.Graph.GraphDocumentController.revision { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L72) | Gets the monotonic in-session content revision. | — | — | -| [`public sealed class Inno.Editor.Graph.GraphDocumentController`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L36) | Applies neutral graph edits atomically and records every data mutation in reload-safe Editor History. | — | — | - -### `Inno.Editor.Graph.GraphEditorModule` - @@ -137,27 +80,66 @@ GraphDocument 是可编辑创作模型,不是假称不可变的运行快照。 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public bool Inno.Editor.Graph.GraphEditorModule.CloseDocument(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L89) | Closes a document explicitly; its neutral History entries remain unavailable until the identity is reopened.
返回:True when a live document was closed.
`documentId`:The persistent document identity to release. | — | — | -| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L208) | Unregisters every document identity so issued controllers cannot resolve retired sessions. | — | — | -| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L191) | Initializes this feature when its owning runtime becomes active.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L199) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public Inno.Editor.Graph.GraphDocumentController Inno.Editor.Graph.GraphEditorModule.OpenDocument(System.Guid documentId, Inno.Core.Graphs.GraphDocument document, Inno.Editor.Interactions.IEditorHistory history)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L57) | Opens or joins an active graph document session.
返回:A presentation-independent document controller.
`documentId`:Persistent document identity; use the Asset identity for asset-backed graphs.
`document`:Mutable neutral document owned by the current asset generation.
`history`:Shared editor history used for every data mutation. | — | `System.InvalidOperationException`:The bounded document owner is full. | -| [`public void Inno.Editor.Graph.GraphEditorModule.RebindDocument(System.Guid documentId, Inno.Core.Graphs.GraphDocument document)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L156) | Rebinds an active session to a newly imported current-generation document.
`documentId`:Stable document identity.
`document`:New current-generation document instance. | — | `System.InvalidOperationException`:Thrown when the document has no active session. | -| [`public void Inno.Editor.Graph.GraphEditorModule.SetAvailability(System.Guid documentId, bool available)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L135) | Marks a temporarily Missing source without discarding its graph or advancing its History stack.
`documentId`:The persistent identity of an open document.
`available`:Whether its source can be resolved in the current generation. | — | — | -| [`public bool Inno.Editor.Graph.GraphEditorModule.TryOpenDocument(System.Guid documentId, Inno.Editor.Interactions.IEditorHistory history, out Inno.Editor.Graph.GraphDocumentController? controller)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L115) | Resolves an already open document without replacing its authored contents.
返回:True when the document is still owned by this module.
`documentId`:The persistent document identity.
`history`:The shared history owner for the returned controller.
`controller`:The joined controller, or null when the document is closed. | — | — | +| [`public sealed class Inno.Editor.Graph.GraphCanvasState`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L12) | Stores transient pan, zoom, selection and connection interaction state without ImGui dependencies. | — | — | +| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.GraphCanvasState.pan { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L22) | Gets the graph-space origin mapped to canvas screen origin. | — | — | +| [`public float Inno.Editor.Graph.GraphCanvasState.zoom { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L27) | Gets the current graph-to-screen scale. | — | — | +| [`public System.Collections.Generic.IReadOnlyCollection Inno.Editor.Graph.GraphCanvasState.selectedNodes { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L32) | Gets selected node identities. | — | — | +| [`public System.Collections.Generic.IReadOnlyCollection Inno.Editor.Graph.GraphCanvasState.selectedEdges { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L37) | Gets selected edge identities. | — | — | +| [`public Inno.Core.Graphs.GraphEndpoint? Inno.Editor.Graph.GraphCanvasState.pendingConnection { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L42) | Gets the endpoint currently being connected, or . | — | — | +| [`public Inno.Core.Graphs.GraphPortDirection? Inno.Editor.Graph.GraphCanvasState.pendingConnectionDirection { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L47) | Gets the direction of . | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.SetViewport(Inno.Core.Graphs.GraphPosition pan, float zoom)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L58) | Restores persistent canvas navigation state with bounded zoom.
`pan`: Graph-space pan offset.
`zoom`: Requested zoom in the inclusive range 0.1 to 4. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.PanBy(float deltaX, float deltaY)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L82) | Moves the canvas origin by screen-space pixels.
`deltaX`: Horizontal screen-space delta.
`deltaY`: Vertical screen-space delta. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.ZoomAt(float factor, float pivotX, float pivotY)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L99) | Changes zoom while preserving the graph point beneath a screen-space pivot.
`factor`: Positive multiplicative zoom factor.
`pivotX`: Screen-space pivot X.
`pivotY`: Screen-space pivot Y. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.SelectNodes(System.Collections.Generic.IEnumerable nodes)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L121) | Replaces node selection.
`nodes`: Complete selected-node set. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.ToggleNode(Inno.Core.Graphs.GraphNodeId nodeId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L135) | Toggles one node while preserving the remaining selection.
`nodeId`: Node to toggle. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.ClearSelection()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L147) | Clears all transient node and edge selection. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.BeginConnection(Inno.Core.Graphs.GraphEndpoint endpoint, Inno.Core.Graphs.GraphPortDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L164) | Begins a connection drag from one endpoint.
`endpoint`: Stable endpoint.
`direction`: Direction of the endpoint within its node interface. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.BeginConnection(Inno.Core.Graphs.GraphEndpoint output)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L178) | Begins a connection drag from one output endpoint.
`output`: Stable output endpoint. | — | — | +| [`public void Inno.Editor.Graph.GraphCanvasState.CancelConnection()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L183) | Cancels any active connection drag. | — | — | +| [`public Inno.Editor.Graph.GraphCanvasState.GraphCanvasState()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphCanvasState.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Graph.GraphClipboardData`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L14) | Contains a neutral copied node fragment suitable for graph clipboard operations. | — | — | +| [`public int Inno.Editor.Graph.GraphClipboardData.nodeCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L23) | Gets the copied node count. | — | — | +| [`public int Inno.Editor.Graph.GraphClipboardData.edgeCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L28) | Gets the copied connection count whose endpoints both belong to the fragment. | — | — | +| [`public sealed class Inno.Editor.Graph.GraphDocumentController`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L36) | Applies neutral graph edits atomically and records every data mutation in reload-safe Editor History. | — | — | +| [`public System.Guid Inno.Editor.Graph.GraphDocumentController.documentId { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L57) | Gets the stable project-relative document identity. | — | — | +| [`public bool Inno.Editor.Graph.GraphDocumentController.isAvailable { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L62) | Gets whether the current document identity and source are both available. | — | — | +| [`public Inno.Core.Graphs.GraphDocument Inno.Editor.Graph.GraphDocumentController.document { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L67) | Gets the mutable neutral document used by the current asset generation. | — | — | +| [`public ulong Inno.Editor.Graph.GraphDocumentController.revision { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L72) | Gets the monotonic in-session content revision. | — | — | +| [`public bool Inno.Editor.Graph.GraphDocumentController.isDirty { get; }`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L77) | Gets whether content changed after its last explicit saved marker. | — | — | +| [`public void Inno.Editor.Graph.GraphDocumentController.MarkSaved()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L82) | Marks the current revision as saved without creating a data History entry. | — | — | +| [`public void Inno.Editor.Graph.GraphDocumentController.ReplaceDocument(Inno.Core.Graphs.GraphDocument replacement, string historyName, string? gestureId = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L96) | Replaces the complete neutral document as one atomic, undoable authoring operation.
`replacement`: Complete replacement document copied by the operation.
`historyName`: Concise artist-facing history entry name.
`gestureId`: Unique active gesture identity for continuous samples, or null for an independent edit. | — | — | +| [`public Inno.Core.Graphs.GraphNodeId Inno.Editor.Graph.GraphDocumentController.AddNode(string definitionId, Inno.Core.Graphs.GraphPosition position, System.Collections.Generic.IReadOnlyDictionary? values = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L122) | Adds a node with a generated stable identity.
`definitionId`: Stable node definition ID.
`position`: Initial graph-space position.
`values`: Optional neutral property values. | The new stable node identity. | — | +| [`public void Inno.Editor.Graph.GraphDocumentController.RemoveNodes(System.Collections.Generic.IEnumerable nodeIds)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L151) | Removes nodes and all incident connections as one structural edit.
`nodeIds`: Nodes to remove. | — | — | +| [`public void Inno.Editor.Graph.GraphDocumentController.MoveNodes(System.Collections.Generic.IReadOnlyDictionary positions)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L180) | Moves one or more nodes as one completed gesture, independently undoable from earlier drags.
`positions`: Complete destination positions keyed by node identity. | — | — | +| [`public Inno.Core.Graphs.GraphEdgeId Inno.Editor.Graph.GraphDocumentController.Connect(Inno.Core.Graphs.GraphEndpoint output, Inno.Core.Graphs.GraphEndpoint input)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L217) | Creates or reconnects one input endpoint to an output endpoint.
`output`: Source endpoint.
`input`: Destination endpoint. | The generated connection identity. | — | +| [`public bool Inno.Editor.Graph.GraphDocumentController.Disconnect(Inno.Core.Graphs.GraphEdgeId edgeId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L254) | Removes one stable connection.
`edgeId`: Connection to remove. | after a connection was removed. | — | +| [`public void Inno.Editor.Graph.GraphDocumentController.SetNodeValue(Inno.Core.Graphs.GraphNodeId nodeId, string propertyId, Inno.Core.Graphs.GraphSerializedValue value, string? gestureId = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L280) | Creates or replaces one neutral node property; only samples carrying the same explicit gesture identity merge.
`nodeId`: Owning node.
`propertyId`: Stable property ID.
`value`: New neutral value.
`gestureId`: Unique current text/numeric gesture identity, or null for an independent edit. | — | — | +| [`public bool Inno.Editor.Graph.GraphDocumentController.RemoveNodeValue(Inno.Core.Graphs.GraphNodeId nodeId, string propertyId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L307) | Removes one neutral node property as a structural edit.
`nodeId`: Owning node.
`propertyId`: Stable property ID. | when the property existed. | — | +| [`public Inno.Editor.Graph.GraphClipboardData Inno.Editor.Graph.GraphDocumentController.Copy(System.Collections.Generic.IEnumerable nodeIds)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L331) | Copies selected nodes and only their internal connections.
`nodeIds`: Nodes to copy. | A detached neutral clipboard fragment. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.GraphDocumentController.Paste(Inno.Editor.Graph.GraphClipboardData clipboard, Inno.Core.Graphs.GraphPosition offset, System.Action>? remapNode = null)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphDocumentController.cs#L365) | Pastes a clipboard fragment with new stable identities and a graph-space offset.
`clipboard`: Detached neutral fragment.
`offset`: Offset added to every copied node position.
`remapNode`: Optional synchronous domain remapping of neutral node values after identity allocation. The callback is never stored in History. | New node identities in clipboard document order. | — | | [`public sealed class Inno.Editor.Graph.GraphEditorModule : Inno.Editor.Core.EditorModule, Inno.Editor.Core.IEditorReloadParticipant`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L19) | Owns active neutral graph document sessions resolved by reload-safe Editor History. | — | — | +| [`public Inno.Editor.Graph.GraphDocumentController Inno.Editor.Graph.GraphEditorModule.OpenDocument(System.Guid documentId, Inno.Core.Graphs.GraphDocument document, Inno.Editor.Interactions.IEditorHistory history)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L57) | Opens or joins an active graph document session.
`documentId`: Persistent document identity; use the Asset identity for asset-backed graphs.
`document`: Mutable neutral document owned by the current asset generation.
`history`: Shared editor history used for every data mutation. | A presentation-independent document controller. | `System.InvalidOperationException`: The bounded document owner is full. | +| [`public bool Inno.Editor.Graph.GraphEditorModule.CloseDocument(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L89) | Closes a document explicitly; its neutral History entries remain unavailable until the identity is reopened.
`documentId`: The persistent document identity to release. | True when a live document was closed. | — | +| [`public bool Inno.Editor.Graph.GraphEditorModule.TryOpenDocument(System.Guid documentId, Inno.Editor.Interactions.IEditorHistory history, out Inno.Editor.Graph.GraphDocumentController? controller)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L115) | Resolves an already open document without replacing its authored contents.
`documentId`: The persistent document identity.
`history`: The shared history owner for the returned controller.
`controller`: The joined controller, or null when the document is closed. | True when the document is still owned by this module. | — | +| [`public void Inno.Editor.Graph.GraphEditorModule.SetAvailability(System.Guid documentId, bool available)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L135) | Marks a temporarily Missing source without discarding its graph or advancing its History stack.
`documentId`: The persistent identity of an open document.
`available`: Whether its source can be resolved in the current generation. | — | — | +| [`public void Inno.Editor.Graph.GraphEditorModule.RebindDocument(System.Guid documentId, Inno.Core.Graphs.GraphDocument document)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L155) | Rebinds an active session to a newly imported current-generation document.
`documentId`: Stable document identity.
`document`: New current-generation document instance. | — | `System.InvalidOperationException`: Thrown when the document has no active session. | +| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L190) | Initializes this feature when its owning runtime becomes active.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L198) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Graph.GraphEditorModule.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Graph/GraphEditorModule.cs#L207) | Unregisters every document identity so issued controllers cannot resolve retired sessions. | — | — | ## 项目依赖 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):实现依赖,PrivateAssets="compile"。 -- [Inno.References](../references/Inno.References.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Graphs](../core/Inno.Core.Graphs.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.References` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Core.Graphs` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Hosting.md b/docs/editor/Inno.Editor.Hosting.md index f487faa2d..ef25d2083 100644 --- a/docs/editor/Inno.Editor.Hosting.md +++ b/docs/editor/Inno.Editor.Hosting.md @@ -35,39 +35,16 @@ Project 创作 IO 属于创作服务;OS 用户位置由平台产品提供。 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Hosting.EditorApplication` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Editor.Hosting.EditorApplication.RunAsync(Inno.Editor.Hosting.EditorLaunchOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorApplication.cs#L40) | Validates composition, owns the created Editor host and retires resources on exit, cancellation or failure.
返回:Zero after orderly completion; startup, execution and retirement failures propagate.
`options`:Borrowed catalogs, factories and product configuration; created resources transfer to the host.
`cancellationToken`:Cancels before creation or requests orderly owner-thread shutdown. | — | `System.ArgumentNullException`:Options or a required dependency is null.
`System.ArgumentException`:A required location or product selection is invalid.
`System.NotSupportedException`:A selected backend lacks its matching runtime or authoring provider.
`System.OperationCanceledException`:Startup or execution was canceled after resource retirement.
`System.InvalidOperationException`:Product startup or resource retirement fails. | -| [`public static class Inno.Editor.Hosting.EditorApplication`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorApplication.cs#L11) | Runs the shared Editor product with explicitly supplied platform and publication composition. | — | — | - -### `Inno.Editor.Hosting.EditorCommandLineOptions` - - - + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public bool Inno.Editor.Hosting.EditorCommandLineOptions.windowVisible { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L27) | 是否显示真实平台窗口;默认 true,false 保留同一 Editor 初始化、帧循环与退出。 | 启动配置不可变;产品组合将值传给 PlatformWindowOptions。 | 参数解析失败发生在创建设备前;隐藏运行不验证 GUI 或焦点。 | -| [`public static Inno.Editor.Hosting.EditorCommandLineOptions Inno.Editor.Hosting.EditorCommandLineOptions.Parse(string[] arguments)`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L41) | Parses the common Editor command line without selecting a platform.
返回:The immutable project startup configuration.
`arguments`:The project path followed by optional name/value pairs. | — | `System.ArgumentException`:The project or an option is missing, repeated or invalid. | -| [`public Inno.Rendering.GraphicsApi? Inno.Editor.Hosting.EditorCommandLineOptions.graphicsApi { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L27) | Gets an explicit renderer preference, or null for backend policy. | — | — | -| [`public string Inno.Editor.Hosting.EditorCommandLineOptions.projectDirectory { get; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L17) | Gets the caller-selected authoring project. | — | — | -| [`public int? Inno.Editor.Hosting.EditorCommandLineOptions.smokeFrameLimit { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L22) | Gets the optional bounded verification frame count. | — | — | -| [`public sealed class Inno.Editor.Hosting.EditorCommandLineOptions`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L10) | Parses the shared Editor project and verification workflow before platform composition starts. | — | — | - -### `Inno.Editor.Hosting.EditorLaunchOptions` - @@ -86,72 +63,82 @@ Project 创作 IO 属于创作服务;OS 用户位置由平台产品提供。 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Hosting.EditorLaunchOptions.EditorLaunchOptions()`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L17) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | +| [`public static class Inno.Editor.Hosting.EditorApplication`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorApplication.cs#L11) | Runs the shared Editor product with explicitly supplied platform and publication composition. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Editor.Hosting.EditorApplication.RunAsync(Inno.Editor.Hosting.EditorLaunchOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorApplication.cs#L40) | Validates composition, owns the created Editor host and retires resources on exit, cancellation or failure.
`options`: Borrowed catalogs, factories and product configuration; created resources transfer to the host.
`cancellationToken`: Cancels before creation or requests orderly owner-thread shutdown. | Zero after orderly completion; startup, execution and retirement failures propagate. | `System.ArgumentNullException`: Options or a required dependency is null.
`System.ArgumentException`: A required location or product selection is invalid.
`System.NotSupportedException`: A selected backend lacks its matching runtime or authoring provider.
`System.OperationCanceledException`: Startup or execution was canceled after resource retirement.
`System.InvalidOperationException`: Product startup or resource retirement fails. | +| [`public sealed class Inno.Editor.Hosting.EditorCommandLineOptions`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L10) | Parses the shared Editor project and verification workflow before platform composition starts. | — | — | +| [`public string Inno.Editor.Hosting.EditorCommandLineOptions.projectDirectory { get; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L17) | Gets the caller-selected authoring project. | — | — | +| [`public int? Inno.Editor.Hosting.EditorCommandLineOptions.smokeFrameLimit { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L22) | Gets the optional bounded verification frame count. | — | — | +| [`public bool Inno.Editor.Hosting.EditorCommandLineOptions.windowVisible { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L27) | Gets whether the platform window is shown; hidden execution retains the same Editor lifecycle. | — | — | +| [`public Inno.Rendering.GraphicsApi? Inno.Editor.Hosting.EditorCommandLineOptions.graphicsApi { get; private init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L32) | Gets an explicit renderer preference, or null for backend policy. | — | — | +| [`public static Inno.Editor.Hosting.EditorCommandLineOptions Inno.Editor.Hosting.EditorCommandLineOptions.Parse(string[] arguments)`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorCommandLineOptions.cs#L46) | Parses the common Editor command line without selecting a platform.
`arguments`: The project path followed by optional name/value pairs. | The immutable project startup configuration. | `System.ArgumentException`: The project or an option is missing, repeated or invalid. | +| [`public sealed class Inno.Editor.Hosting.EditorLaunchOptions`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L17) | Supplies product-owned composition to the shared Editor lifecycle without selecting a platform or backend. | — | — | +| [`public required string Inno.Editor.Hosting.EditorLaunchOptions.projectDirectory { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L22) | Gets the current authoring project location; shared authoring services own its project IO. | — | — | +| [`public required Inno.Editor.Core.EditorKeyboardPolicy Inno.Editor.Hosting.EditorLaunchOptions.keyboard { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L27) | Gets explicit keyboard conventions selected by the Editor product. | — | — | | [`public required Inno.Adapter.Presentation.IAuthoringAdapterCatalog Inno.Editor.Hosting.EditorLaunchOptions.adapterCatalog { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L32) | Gets the borrowed immutable runtime, authoring and presentation provider catalogs. | — | — | +| [`public required Inno.Shell.ShellOptions Inno.Editor.Hosting.EditorLaunchOptions.shell { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L37) | Gets explicit backend, primary-window and rendering policy selected by the product. | — | — | +| [`public required Inno.Adapter.Presentation.PresentationBackendId Inno.Editor.Hosting.EditorLaunchOptions.presentation { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L42) | Gets the explicit presentation implementation selected by the product. | — | — | +| [`public required Inno.Shell.IShellFrameDriver Inno.Editor.Hosting.EditorLaunchOptions.frameDriver { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L47) | Gets the borrowed owner-thread scheduling policy. | — | — | +| [`public required Inno.Build.Composition.BuildDistribution Inno.Editor.Hosting.EditorLaunchOptions.distribution { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L52) | Gets the borrowed immutable publication distribution. | — | — | +| [`public required Inno.Build.BuildTargetId Inno.Editor.Hosting.EditorLaunchOptions.defaultBuildTarget { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L57) | Gets the explicit default game target for new project build settings. | — | — | | [`public required Inno.Build.Composition.BuildCompositionContext Inno.Editor.Hosting.EditorLaunchOptions.buildContext { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L62) | Gets the explicitly selected SDK and build execution host. | — | — | +| [`public required string Inno.Editor.Hosting.EditorLaunchOptions.supportPackRoot { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L67) | Gets the explicit installed Support Pack location provided by product composition. | — | — | | [`public required System.Func Inno.Editor.Hosting.EditorLaunchOptions.createEngineHost { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L72) | Gets the factory transferring a newly created metadata-configured engine to the Editor owner. | — | — | -| [`public System.Func? Inno.Editor.Hosting.EditorLaunchOptions.createHostLogSink { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L87) | Gets an optional factory transferring one host log sink to the Editor lifetime. | — | — | | [`public required System.Func Inno.Editor.Hosting.EditorLaunchOptions.createScriptModuleSource { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L77) | Gets the factory transferring a candidate script module source to the existing reload transaction. | — | — | | [`public System.Func? Inno.Editor.Hosting.EditorLaunchOptions.createSessionLogSink { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L82) | Gets an optional factory transferring each edit or play log sink to its owning session. | — | — | -| [`public required Inno.Build.BuildTargetId Inno.Editor.Hosting.EditorLaunchOptions.defaultBuildTarget { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L57) | Gets the explicit default game target for new project build settings. | — | — | -| [`public required Inno.Build.Composition.BuildDistribution Inno.Editor.Hosting.EditorLaunchOptions.distribution { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L52) | Gets the borrowed immutable publication distribution. | — | — | -| [`public required Inno.Shell.IShellFrameDriver Inno.Editor.Hosting.EditorLaunchOptions.frameDriver { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L47) | Gets the borrowed owner-thread scheduling policy. | — | — | -| [`public required Inno.Editor.Core.EditorKeyboardPolicy Inno.Editor.Hosting.EditorLaunchOptions.keyboard { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L27) | Gets explicit keyboard conventions selected by the Editor product. | — | — | -| [`public required Inno.Adapter.Presentation.PresentationBackendId Inno.Editor.Hosting.EditorLaunchOptions.presentation { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L42) | Gets the explicit presentation implementation selected by the product. | — | — | -| [`public required string Inno.Editor.Hosting.EditorLaunchOptions.projectDirectory { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L22) | Gets the current authoring project location; shared authoring services own its project IO. | — | — | -| [`public required Inno.Shell.ShellOptions Inno.Editor.Hosting.EditorLaunchOptions.shell { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L37) | Gets explicit backend, primary-window and rendering policy selected by the product. | — | — | +| [`public System.Func? Inno.Editor.Hosting.EditorLaunchOptions.createHostLogSink { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L87) | Gets an optional factory transferring one host log sink to the Editor lifetime. | — | — | | [`public int? Inno.Editor.Hosting.EditorLaunchOptions.smokeFrameLimit { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L92) | Gets an optional positive frame limit for bounded product verification. | — | — | -| [`public required string Inno.Editor.Hosting.EditorLaunchOptions.supportPackRoot { get; init; }`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L67) | Gets the explicit installed Support Pack location provided by product composition. | — | — | -| [`public sealed class Inno.Editor.Hosting.EditorLaunchOptions`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L17) | Supplies product-owned composition to the shared Editor lifecycle without selecting a platform or backend. | — | — | +| [`public Inno.Editor.Hosting.EditorLaunchOptions.EditorLaunchOptions()`](../../src/composition/editor/hosting/Inno.Editor.Hosting/EditorLaunchOptions.cs#L17) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Engine.Default](../runtime/Inno.Engine.Default.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter](../runtime/Inno.Adapter.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Audio](../audio/Inno.Adapter.Audio.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Animation.Assets](../animation/Inno.Animation.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Animation.Runtime](../animation/Inno.Animation.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Audio](Inno.Editor.Audio.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.PlayMode](Inno.Editor.PlayMode.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Rendering](Inno.Editor.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Audio.Assets](../audio/Inno.Audio.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Audio.Runtime](../audio/Inno.Audio.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Text.Assets](../text/Inno.Text.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.UI.Assets](../ui/Inno.UI.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.UI.Runtime](../ui/Inno.UI.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Plugins.Authoring](../plugins/Inno.Plugins.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Events](../core/Inno.Core.Events.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Layers](../core/Inno.Core.Layers.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Input.Runtime](../input/Inno.Input.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene](../scene/Inno.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene.Assets](../scene/Inno.Scene.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Platform](../platform/Inno.Platform.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets](../rendering/Inno.Rendering.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Runtime](../rendering/Inno.Rendering.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Storage.Runtime](../storage/Inno.Storage.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Managed](../build/Inno.Build.Managed.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Runtime.Contracts](../runtime/Inno.Runtime.Contracts.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets.Authoring](../rendering/Inno.Rendering.Assets.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Build.Composition](../build/Inno.Build.Composition.md):公开引用边界由实际签名核对。 -- [Inno.Shell](../runtime/Inno.Shell.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Presentation](../platform/Inno.Adapter.Presentation.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):公开引用边界由实际签名核对。 -- [Inno.Build](../build/Inno.Build.md):公开引用边界由实际签名核对。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):公开引用边界由实际签名核对。 -- [Inno.Scripting.Compiler](../scripting/Inno.Scripting.Compiler.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Engine.Default` | 实现依赖;不传递 compile surface | +| `Inno.Adapter` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Audio` | 实现依赖;不传递 compile surface | +| `Inno.Animation.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Animation.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Audio` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Audio.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Audio.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Text.Assets` | 实现依赖;不传递 compile surface | +| `Inno.UI.Assets` | 实现依赖;不传递 compile surface | +| `Inno.UI.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Plugins.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Events` | 实现依赖;不传递 compile surface | +| `Inno.Core.Layers` | 实现依赖;不传递 compile surface | +| `Inno.Input.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Platform` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Storage.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Build.Managed` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Runtime.Contracts` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Build.Composition` | 公开签名依赖 | +| `Inno.Shell` | 公开签名依赖 | +| `Inno.Adapter.Presentation` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Runtime` | 公开签名依赖 | +| `Inno.Build` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Extensibility.Modules` | 公开签名依赖 | +| `Inno.Scripting.Compiler` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.ImGui.md b/docs/editor/Inno.Editor.ImGui.md index 0f447825a..420257e10 100644 --- a/docs/editor/Inno.Editor.ImGui.md +++ b/docs/editor/Inno.Editor.ImGui.md @@ -13,7 +13,7 @@ ## 共享 Inspector 与窗口呈现 -`ImGuiWidget.SectionLayout(Action drawContent)` 为一段内容建立可复用的 fieldset scope;scope 内连续调用 `bool SectionHeader(string title, string? description = null, Action? drawLeadingControl = null)` 时,标题嵌入上边框,直到下一个标题或 scope 结束的内容都被同一边框完整包裹。展开的内部 content fieldset 使用轻微圆角;窗口、Panel、顶层 Header 和其他大容器保持直角。返回值决定当前 section 正文是否应绘制;标题文字本身通过共享的 `ClickableText` widget 切换展开状态,不显示额外的加减号或整行 hover 背景。标题与 Stats 的共享 `CollectionSectionHeader` 使用同一 `sectionHeaderPadding`,因此左侧缩进不会由各 Panel 自行估算。折叠态保留中断式横线,并在整条线的左右端各绘制一根以横线为中心的短竖帽,形成 `⊢ … ⊣` 轮廓;竖帽与横线共享颜色、粗细和交点,不形成向下的残留边框。折叠状态由 ImGui 按当前 Inspector scope 的稳定顺序保存,标题或 Local/World 文案变化不会重置状态。`EnsureSection` 为没有 `[Header]` 的默认属性自动建立 `Properties` fieldset,`isSectionContentVisible` 则让 serialized-property pipeline 在任意 section 折叠后持续跳过内容,直到下一个 Header。可选 leading control 用于 Local/World 这类属于分组语义本身的开关;scope 外的 `SectionHeader` 仍保持普通分隔标题并返回 `true`。`HeaderSurface(id, drawContent, spanWindowPadding)` 是 Inspector target 与 Shader Editor 顶部共用的直角 header 容器;`CollectionSectionHeader` 是 Stats 等 collection surface 的直角分组条。`HelpBox(string text, string icon, Vector4 color)` 绘制有边框、状态图标与侧边语义色的内部提示卡片。`DrawItemTooltip` 使用父 viewport work area、真实内宽和缩放 padding 测量,靠边自动翻向并约束位置。File Browser、Hierarchy 与 Stats 共用暗色 collectionRow/collectionRowAlternate,交替条纹、hover、选择仍保持区分。 +`EditorUiSession.current.widgets.SectionLayout(Action drawContent)` 为一段内容建立可复用的 fieldset scope;scope 内连续调用 `bool SectionHeader(string title, string? description = null, Action? drawLeadingControl = null)` 时,标题嵌入上边框,直到下一个标题或 scope 结束的内容都被同一边框完整包裹。展开的内部 content fieldset 使用轻微圆角;窗口、Panel、顶层 Header 和其他大容器保持直角。返回值决定当前 section 正文是否应绘制;标题文字本身通过共享的 `ClickableText` widget 切换展开状态,不显示额外的加减号或整行 hover 背景。标题与 Stats 的共享 `CollectionSectionHeader` 使用同一 `sectionHeaderPadding`,因此左侧缩进不会由各 Panel 自行估算。折叠态保留中断式横线,并在整条线的左右端各绘制一根以横线为中心的短竖帽,形成 `⊢ … ⊣` 轮廓;竖帽与横线共享颜色、粗细和交点,不形成向下的残留边框。折叠状态由 ImGui 按当前 Inspector scope 的稳定顺序保存,标题或 Local/World 文案变化不会重置状态。`EnsureSection` 为没有 `[Header]` 的默认属性自动建立 `Properties` fieldset,`isSectionContentVisible` 则让 serialized-property pipeline 在任意 section 折叠后持续跳过内容,直到下一个 Header。可选 leading control 用于 Local/World 这类属于分组语义本身的开关;scope 外的 `SectionHeader` 仍保持普通分隔标题并返回 `true`。`HeaderSurface(id, drawContent, spanWindowPadding)` 是 Inspector target 与 Shader Editor 顶部共用的直角 header 容器;`CollectionSectionHeader` 是 Stats 等 collection surface 的直角分组条。`HelpBox(string text, string icon, Vector4 color)` 绘制有边框、状态图标与侧边语义色的内部提示卡片。`DrawItemTooltip` 使用父 viewport work area、真实内宽和缩放 padding 测量,靠边自动翻向并约束位置。File Browser、Hierarchy 与 Stats 共用暗色 collectionRow/collectionRowAlternate,交替条纹、hover、选择仍保持区分。 浮动 Panel 在标题右侧显示统一关闭按钮;docked Panel 保持每个 dock node 一个关闭入口。Docked close glyph 以 ImGui 实际 `TabBar.BarRect` 的几何中心定位,不用字体高度或 DockNode 顶点推测,因此不同缩放与顶栏布局下仍严格垂直居中。窗口标题不继承输入控件的 FrameBorderSize,避免额外分隔线;这与透明接缝是两个不同的问题。窗口、Child、Tab 和 component header 的外轮廓使用直角;按钮、输入框、菜单、tooltip、HelpBox 与 Inspector content fieldset 等内部交互/内容表面仍使用语义圆角。所有按钮仍走现有 Panel close/dirty 确认生命周期。 @@ -69,9 +69,9 @@ Palette 与 Style Metrics 并列位于 `Styling`,runtime host 与三个表现 所有主题颜色集中在 `EditorPalette`:原生 ImGui col、Inspector、Hierarchy、Asset Browser、Logging、轴颜色与 drag target 都不在 Panel 中声明。换主题只需替换这一个 palette surface。 -所有跨 Panel 的像素布局、padding、spacing、rounding、列比例和最小尺寸集中在 `ImGuiWidget.style`(`EditorStyleMetrics`)。Panel 可以读取语义名,例如 `panelTabFramePadding`、`sectionHeaderPadding`、`inspectorSectionPadding`、`inspectorSectionRounding`、`inspectorCollapsedSectionCapLength`、`propertyMetadataSpacing`、`assetListNameSeparatorPosition`、`inspectorCardSpacing`、`hierarchyItemSpacing`、`hierarchyRenameMinimumWidth` 与 `settingsFieldPadding`,不应新增散落的固定像素。Inspector fieldset 的 outline 同样来自 `EditorPalette.inspectorSectionBorder`。 +所有跨 Panel 的像素布局、padding、spacing、rounding、列比例和最小尺寸集中在 `EditorUiSession.current.widgets.style`(`EditorStyleMetrics`)。Panel 可以读取语义名,例如 `panelTabFramePadding`、`sectionHeaderPadding`、`inspectorSectionPadding`、`inspectorSectionRounding`、`inspectorCollapsedSectionCapLength`、`propertyMetadataSpacing`、`assetListNameSeparatorPosition`、`inspectorCardSpacing`、`hierarchyItemSpacing`、`hierarchyRenameMinimumWidth` 与 `settingsFieldPadding`,不应新增散落的固定像素。Inspector fieldset 的 outline 同样来自 `EditorPalette.inspectorSectionBorder`。 -`ImGuiWidget.SetupStyle()` 把 layout metrics 和 `EditorPalette` 应用到原生 ImGui style;运行期间 zoom 改变时,runtime 只在倍率发生变化后重新应用一次 native style。普通窗口绘制时,`ResizeGrip`、`ResizeGripHovered` 与 `ResizeGripActive` 使用透明色,因此可缩放窗口仍保留边缘/角落命中能力,但不会显示右下角三角形。Dear ImGui 在更新 Dock tree splitter 时会把 separator hover/active 临时映射到 resize-grip hover/active;`ImGuiEditorRuntime` 只在 `DockSpaceOverViewport` 调用范围内恢复这两个 accent color,保证 Panel 间连接线的 hover/drag feedback 可见,同时不恢复窗口三角形。 +`EditorUiSession.current.widgets.SetupStyle()` 把 layout metrics 和 `EditorPalette` 应用到原生 ImGui style;运行期间 zoom 改变时,runtime 只在倍率发生变化后重新应用一次 native style。普通窗口绘制时,`ResizeGrip`、`ResizeGripHovered` 与 `ResizeGripActive` 使用透明色,因此可缩放窗口仍保留边缘/角落命中能力,但不会显示右下角三角形。Dear ImGui 在更新 Dock tree splitter 时会把 separator hover/active 临时映射到 resize-grip hover/active;`ImGuiEditorRuntime` 只在 `DockSpaceOverViewport` 调用范围内恢复这两个 accent color,保证 Panel 间连接线的 hover/drag feedback 可见,同时不恢复窗口三角形。 主 dockspace 在窗口尺寸变化时按 ImGui 当前节点数据逐层更新子节点的 `SizeRef`,中央 node 不再独占新增加的空间。只有两个子节点的实际尺寸之和等于父节点的可用尺寸时,才用实际尺寸推导比例;刚载入 layout 时的子节点 `Size` 可能为零或错误地等于父尺寸,这时改用 ImGui 保存的 `SizeRef`,避免比例在启动首帧逐次漂移。宿主按当前 ImGui viewport ID 推导 dockspace ID,在第一次提交 dockspace 前读取已加载的节点并等比更新;拖动 splitter 的实际结果成为下一次 resize 的比例,最小尺寸仍由原生 `WindowMinSize` 和 separator 限制。比例不另存一份,布局的唯一持久来源仍是 `editor.ini` 的 `[Docking][Data]`。 @@ -115,6 +115,7 @@ Editor ImGui context 默认启用 Inno overlay scrollbar 扩展。纵横滚动 ```csharp using System; +using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; static bool DrawCardHeader( @@ -126,7 +127,7 @@ static bool DrawCardHeader( Action drawTrailingControl, Action drawContextMenu ) { - return ImGuiWidget.CollapsingCard(id, title, + return EditorUiSession.current.widgets.CollapsingCard(id, title, drawLeadingControl: drawLeadingControl, drawTrailingControl: drawTrailingControl, defaultOpen: true, dimmed: !enabled, trailingControlWidth: trailingControlWidth, @@ -153,6 +154,7 @@ Component(包含 Transform)与 GameSystem 的排序拖拽也绑定在这个 ```csharp using System; using Inno.Editor.ImGui; +using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; static void DrawOpenCardBody( @@ -165,7 +167,7 @@ static void DrawOpenCardBody( return; try { - ImGuiWidget.CardBody(id, drawContent: drawContent, dimmed: !enabled); + EditorUiSession.current.widgets.CardBody(id, drawContent: drawContent, dimmed: !enabled); } finally { @@ -204,24 +206,782 @@ Scripting facade 同时导出 `TreeNodeDrawContext`、`TreeNodeOptions`、`TreeN [`Inno.Editor.ImGui.Tests`](../../tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs) 使用真实 ImGui context 和公开 Widget API,通过鼠标事件打开弹层并测量稳定后的布局。覆盖三档缩放下的 2:3 列、长标签换行、短列表无滚动、长列表高度边界、菜单搜索填满内容宽度,以及重叠窗口仅前景控件接收点击。测试宿主必须部署与当前绑定一致的 native 目录;测试不依赖桌面解锁或截图推测内部布局。 -## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 -### `Inno.Editor.ImGui.EditorDragDropRenderer` +## Session 与原生边界 - - - +每个 ImGuiEditorRuntime 拥有一个 EditorUiSession。widgets 与 menus 的缓存、搜索、树布局及手势状态均按 session/owner 管理;Panel 关闭或 generation 退休时清理。静态 current 只访问同步绘制 scope,跨线程、跨 context 或失序释放明确失败。 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static bool Inno.Editor.ImGui.EditorDragDropRenderer.Source(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDragData data, System.Action? drawPreview = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L33) | Publishes managed drag data for the most recently submitted ImGui item.
返回:true while the item is an active drag source.
`interaction`:The interaction area and target that produced the drag source.
`data`:The managed drag data published by the source.
`drawPreview`:An optional callback that draws the native drag preview. | — | — | -| [`public static Inno.Editor.ImGui.EditorDropWidgetResult Inno.Editor.ImGui.EditorDragDropRenderer.Target(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L57) | Evaluates and, on delivery, accepts a managed drag on the most recently submitted ImGui item.
返回:The native preview state together with the managed compatibility and delivery results.
`interaction`:The interaction area and managed drop target.
`placement`:The requested position relative to the target. | — | — | -| [`public static class Inno.Editor.ImGui.EditorDragDropRenderer`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L14) | Bridges managed editor drag sessions to the native ImGui payload API. | — | — | +普通 Panel、Inspection 和插件使用 pointer-free facade 及引擎自己的 options。EditorDrawList 仅借用当前帧和窗口,切换窗口、下一帧、离开 frame 或更换 context 后失效。字体用 ImGuiFontScope 成对恢复。TransformGizmo 封装 native gizmo,仅暴露中立矩阵与操作结果。 + +InputText 的 UTF-8 临时内存按大小使用 stack/共享 ArrayPool,finally 归还并清空;未改变文本不解码,不引入私有池。性能结论以验收采样为准。 -### `Inno.Editor.ImGui.EditorDropWidgetResult` +```csharp +using InnoEditor.ImGui; + +static void DrawHelp() +{ + EditorUiSession.current.widgets.Hint("Changes use the shared Editor History."); +} +``` +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -234,62 +994,48 @@ Scripting facade 同时导出 `TreeNodeDrawContext`、`TreeNodeOptions`、`TreeN - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.ImGui.EditorDropWidgetResult.EditorDropWidgetResult(bool isPreviewing, Inno.Editor.Interactions.EditorDropStatus status, Inno.Editor.Interactions.EditorDropResult result)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L101) | Creates a combined native-preview and managed-drop result.
`isPreviewing`:Whether a compatible native payload is currently hovering the target.
`status`:The compatibility and visual state returned by the managed drop router.
`result`:The result produced when the payload was delivered. | — | — | -| [`public bool Inno.Editor.ImGui.EditorDropWidgetResult.Equals(Inno.Editor.ImGui.EditorDropWidgetResult other)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.ImGui.EditorDropWidgetResult.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.ImGui.EditorDropWidgetResult.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.ImGui.EditorDropWidgetResult.operator ==(Inno.Editor.ImGui.EditorDropWidgetResult left, Inno.Editor.ImGui.EditorDropWidgetResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.ImGui.EditorDropWidgetResult.operator !=(Inno.Editor.ImGui.EditorDropWidgetResult left, Inno.Editor.ImGui.EditorDropWidgetResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.ImGui.EditorDropWidgetResult.ToString()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.ImGui.EditorDropWidgetResult.isPreviewing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L114) | Gets whether a compatible native payload is hovering the target. | — | — | -| [`public static Inno.Editor.ImGui.EditorDropWidgetResult Inno.Editor.ImGui.EditorDropWidgetResult.none { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L129) | Gets an inactive drop target result. | — | — | -| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.ImGui.EditorDropWidgetResult.result { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L124) | Gets the delivered drop result. | — | — | -| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.ImGui.EditorDropWidgetResult.status { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L119) | Gets the managed drop compatibility status. | — | — | -| [`public readonly record struct Inno.Editor.ImGui.EditorDropWidgetResult : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L87) | Reports the preview and delivery state of an ImGui editor drop target. | — | — | - -### `Inno.Editor.ImGui.EditorGameInputCapture` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.ImGui.EditorGameInputCapture.EditorGameInputCapture(System.Func resolveWindow)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L35) | Creates a capture policy using a presentation viewport to platform window resolver.
`resolveWindow`:Returns a live window ID for a viewport, or null when absent. | — | `System.ArgumentNullException`:Thrown when the resolver is null. | -| [`public void Inno.Editor.ImGui.EditorGameInputCapture.BeginFrame()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L43) | Starts a presentation pass in which the visible Game View must report its current bounds. | — | — | -| [`public uint Inno.Editor.ImGui.EditorGameInputCapture.CompleteFrame()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L89) | Ends a presentation pass and identifies input that must be released after focus loss.
返回:The former platform window ID to reset, or zero if no reset is needed. | — | — | -| [`public bool Inno.Editor.ImGui.EditorGameInputCapture.Report(uint viewportId, System.Numerics.Vector2 origin, System.Numerics.Vector2 size, bool focused, bool hovered)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L66) | Reports the Game View image in coordinates local to its platform window.
返回:true when the Game View and its platform window both own focus.
`viewportId`:The presentation viewport containing the Game View.
`origin`:The image's upper-left corner relative to that window.
`size`:The visible image size in logical pixels.
`focused`:Whether the Game View is selected and its platform window has focus.
`hovered`:Whether the image is inside the frontmost hovered ImGui window. | — | — | -| [`public Inno.Core.Events.Event? Inno.Editor.ImGui.EditorGameInputCapture.Route(Inno.Core.Events.Event evnt)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L126) | Filters an engine event through the current Game View focus and pointer bounds.
返回:A routed event or a synthetic focus reset, or null when the Game View must not receive input.
`evnt`:The original backend-neutral platform event. | A globally consumed key, button, focus or close release is not replayed to the game. It returns a new focus-reset event so previously held input is released before simulation. Consumed pointer movement still updates hit-test bounds without reaching the game backend. | `System.ArgumentNullException`:Thrown when the event is null. | -| [`public sealed class Inno.Editor.ImGui.EditorGameInputCapture`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L13) | Routes platform input to the Play session only while the visible Game View owns focus. | — | — | - -### `Inno.Editor.ImGui.EditorMenuRenderer` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static bool Inno.Editor.ImGui.EditorMenuRenderer.ContextMenu(string id, Inno.Editor.Interactions.EditorInteraction scopeInteraction, Inno.Editor.Interactions.EditorInteraction itemInteraction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L80) | Draws one context popup composed from an item interaction and its containing-scope interaction.
返回:true while the context popup is open and at least one command group was drawn.
`id`:Stable popup identifier in the current ImGui ID scope.
`scopeInteraction`:Container interaction whose commands are shown first, such as creation commands for a directory.
`itemInteraction`:Selected-item interaction whose commands are shown after the container commands. | — | — | -| [`public static bool Inno.Editor.ImGui.EditorMenuRenderer.ContextMenu(string id, Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L38) | Draws a resolved right-click menu for the most recently submitted ImGui item.
返回:true while the context popup is open and its items were drawn.
`id`:The stable popup identifier in the current ImGui ID scope.
`interaction`:The interaction area and optional operation target. | — | — | -| [`public static void Inno.Editor.ImGui.EditorMenuRenderer.DrawItems(Inno.Editor.Interactions.EditorInteraction interaction, System.Collections.Generic.IReadOnlyList items)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L322) | Recursively draws resolved menu nodes into the currently open popup or menu.
`interaction`:The interaction used to resolve shortcuts and enqueue selected actions.
`items`:The immutable menu nodes to draw in display order. | — | — | -| [`public static bool Inno.Editor.ImGui.EditorMenuRenderer.DrawSearchItems(Inno.Editor.Interactions.EditorInteraction interaction, System.Collections.Generic.IReadOnlyList items, string search)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L379) | Draws matching leaf commands as a flat searchable list.
返回:true when a command was selected.
`interaction`:The interaction used to enqueue selected actions.
`items`:The immutable menu tree whose leaves should be searched.
`search`:The case-insensitive text matched against each slash-delimited leaf path. | — | — | -| [`public static void Inno.Editor.ImGui.EditorMenuRenderer.MainMenu(Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L216) | Draws the complete editor main menu bar for the supplied menu context.
`interaction`:The main-menu interaction area. | — | — | -| [`public static bool Inno.Editor.ImGui.EditorMenuRenderer.WindowContextMenu(string id, Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L126) | Draws a resolved right-click menu when the current ImGui window's unoccupied background is clicked.
返回:true while the background context popup is open and its items were drawn.
`id`:The stable popup identifier in the current ImGui ID scope.
`interaction`:The interaction area and directory target for the background operation. | — | — | -| [`public static class Inno.Editor.ImGui.EditorMenuRenderer`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L17) | Renders immutable editor menu models through ImGui. | — | — | - -### `Inno.Editor.ImGui.EditorPalette` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -402,124 +1148,6 @@ Scripting facade 同时导出 `TreeNodeDrawContext`、`TreeNodeOptions`、`TreeN - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityEmphasized = 0.82`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L61) | Gets emphasized opacity for borders and interactive grips. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityFaint = 0.04`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L26) | Gets faint opacity for quiet guides and separators. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityMedium = 0.45`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L46) | Gets medium opacity for secondary content and overlays. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityMuted = 0.28`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L41) | Gets muted opacity for shadows and subdued outlines. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityNearOpaque = 0.92`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L66) | Gets near-opaque opacity for active overlays. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityNone = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L16) | Gets zero opacity for invisible surfaces. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityOpaque = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L76) | Gets full opacity. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityPopup = 0.985`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L71) | Gets popup opacity, retaining only enough transparency for depth. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityProminent = 0.7`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L56) | Gets prominent opacity for navigation emphasis. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacitySoft = 0.2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L36) | Gets soft opacity for tinted container surfaces. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityStrong = 0.55`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L51) | Gets strong opacity for accents that remain translucent. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacitySubtle = 0.12`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L31) | Gets subtle opacity for selection washes and low-emphasis overlays. | — | — | -| [`public const float Inno.Editor.ImGui.EditorPalette.opacityTrace = 0.005`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L21) | Gets trace opacity for barely perceptible alternating surfaces. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetActive(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L458) | Gets an active treatment derived from a base theme color.
返回:The base color blended toward the palette text color using the standard active amount.
`color`:The color consumed by get active; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetHovered(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L447) | Gets a hover treatment derived from a base theme color.
返回:The base color blended toward the palette text color using the standard hover amount.
`color`:The color consumed by get hovered; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedBorder(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L575) | Gets the expanded log card border derived from a severity color.
返回:The standard expanded-border base blended with the severity color.
`severityColor`:The palette color representing the log severity. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedCard(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L564) | Gets the expanded log card background derived from a severity color.
返回:The standard expanded-card base blended with the severity color.
`severityColor`:The palette color representing the log severity. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogSeparator(System.Numerics.Vector4 cardColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L586) | Gets the separator color used inside an expanded log card.
返回:The card color blended toward the palette text color.
`cardColor`:The resolved background color of the expanded card. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.Lerp(System.Numerics.Vector4 from, System.Numerics.Vector4 to, float amount)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L603) | Linearly interpolates two palette colors using a clamped amount.
返回:The component-wise interpolated color.
`from`:The color returned when amount is zero.
`to`:The color returned when amount is one.
`amount`:The interpolation amount, clamped to the inclusive zero-to-one range. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.WithOpacity(System.Numerics.Vector4 color, float opacity)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L686) | Returns a theme color with a caller-selected opacity, normally one of the shared opacity levels.
返回:The validated vector4 that represents the completed operation.
`color`:Base RGB color.
`opacity`:Replacement alpha in the inclusive zero-to-one range. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L216) | Gets the standard accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L226) | Gets the active accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L221) | Gets the hovered accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetAccent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L436) | Gets the opaque asset browser accent. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L411) | Gets the asset browser border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorderSoft { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L416) | Gets the soft asset browser border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBreadcrumbText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L431) | Gets the subdued text color used by asset browser breadcrumb paths. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L406) | Gets the asset browser field background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L421) | Gets the asset browser text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetTextMuted { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L426) | Gets the muted asset browser text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisW { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L493) | Gets W axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisX { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L478) | Gets X axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisY { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L483) | Gets Y axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisZ { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L488) | Gets Z axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.border { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L161) | Gets the standard border color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.borderShadow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L166) | Gets the standard border shadow color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L381) | Gets the deepest collection background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L386) | Gets the primary collection row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L391) | Gets the alternate collection row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.compactControlHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L376) | Gets compact control hover color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.dragDropTarget { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L281) | Gets the standard drag target color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.error { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L96) | Gets the standard error color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frame { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L171) | Gets the standard frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L181) | Gets the active frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L176) | Gets the hovered frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchyInactiveText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L468) | Gets inactive hierarchy text. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchySceneRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L463) | Gets scene row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBody { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L326) | Gets inspector card body background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBodyBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L331) | Gets inspector card body border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisabledText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L366) | Gets disabled inspector card text. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisclosureHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L371) | Gets inspector disclosure hover background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L301) | Gets inspector card header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorLayerLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L321) | Gets the subdued background of Layer labels in Inspector target headers. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorSectionBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L336) | Gets the outline color of framed Inspector sections. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTagLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L316) | Gets the subdued background of Tag labels in Inspector target headers. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L306) | Gets the persistent Inspector target header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeaderBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L311) | Gets the persistent Inspector target header border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L528) | Gets collapsed log card border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedCard { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L523) | Gets collapsed log card background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logDebug { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L498) | Gets debug log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logError { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L513) | Gets error log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L533) | Gets the base expanded log card background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBorderBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L538) | Gets the base expanded log card border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logFatal { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L518) | Gets fatal log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logInfo { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L503) | Gets informational log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggle { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L543) | Gets log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L553) | Gets active log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L548) | Gets hovered log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logWarning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L508) | Gets warning log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L136) | Gets the background used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItem { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L141) | Gets the resting background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L151) | Gets the active background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L146) | Gets the hovered background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuSeparator { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L156) | Gets the separator color used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L131) | Gets text color used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.modalDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L296) | Gets modal dim background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L291) | Gets navigation dim background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationHighlight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L286) | Gets navigation highlight color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.popupBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L126) | Gets the editor popup background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L201) | Gets the standard scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L211) | Gets the active scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L206) | Gets the hovered scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L396) | Gets the translucent primary background of a complete Settings field. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsFieldAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L401) | Gets the translucent alternate background of a complete Settings field. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderInputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L111) | Gets the orange heading surface shared by Shader input nodes. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L106) | Gets the neutral Shader node heading surface. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderOutputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L116) | Gets the purple heading surface shared by Shader output nodes. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeBoolean { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L351) | Gets the accent used by Boolean Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeInteger { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L346) | Gets the accent used by integer Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeNumeric { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L341) | Gets the accent used by numeric Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeOther { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L361) | Gets the accent used by unclassified Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeResource { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L356) | Gets the accent used by Shader resource type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L231) | Gets the standard tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L246) | Gets the dimmed tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmedSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L251) | Gets the selected dimmed tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L236) | Gets the hovered tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderLight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L271) | Gets the light table border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderStrong { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L266) | Gets the strong table border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L261) | Gets the table header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L276) | Gets the alternate table row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L241) | Gets the selected tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelectedOverline { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L256) | Gets the selected tab overline color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.text { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L86) | Gets the primary text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.textDisabled { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L91) | Gets disabled text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.title { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L186) | Gets the inactive title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L191) | Gets the active title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleCollapsed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L196) | Gets the collapsed title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.transparent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L81) | Gets fully transparent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.treeGuide { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L473) | Gets hierarchy tree guide color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.warning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L101) | Gets the standard warning color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.windowBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L121) | Gets the editor window background. | — | — | -| [`public static class Inno.Editor.ImGui.EditorPalette`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L11) | Defines the complete editor color palette in one theme surface. | — | — | - -### `Inno.Editor.ImGui.EditorStyleMetrics` - @@ -637,562 +1265,1505 @@ Scripting facade 同时导出 `TreeNodeDrawContext`、`TreeNodeOptions`、`TreeN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MAX_ZOOM = 1.5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L19) | Defines the largest supported editor UI zoom. | — | — | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MIN_ZOOM = 0.75`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L14) | Defines the smallest supported editor UI zoom. | — | — | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_ZOOM_STEP = 0.1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L24) | Defines one keyboard or menu zoom increment. | — | — | -| [`public Inno.Editor.ImGui.EditorStyleMetrics.EditorStyleMetrics()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ResetZoom()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L101) | Restores the baseline editor UI zoom.
返回:true when the zoom changed. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetCompactMode(bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L48) | Switches between comfortable and compact editor density.
返回:true when the density changed.
`value`:true for compact density. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetZoom(float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L68) | Sets the editor UI zoom after clamping it to the supported range.
返回:true when the effective zoom changed.
`value`:The requested UI zoom multiplier. | — | `System.ArgumentOutOfRangeException`:Thrown when value is not finite. | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomIn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L85) | Increases editor UI zoom by one bounded increment.
返回:true when the zoom changed. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomOut()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L93) | Decreases editor UI zoom by one bounded increment.
返回:true when the zoom changed. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L287) | Gets asset breadcrumb bar height. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L562) | Gets spacing around breadcrumb separators. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L237) | Gets asset browser cell padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetFrameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L247) | Gets asset browser frame rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L297) | Gets asset grid cell padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridDefaultScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L302) | Gets default asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridFixedCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L537) | Gets fixed padding added to calculated asset grid cells. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L552) | Gets the horizontal inset that constrains an asset grid icon inside its card. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L557) | Gets the vertical spacing between an asset grid icon and its label. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L547) | Gets the top inset reserved above an asset grid icon. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelBottomPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L527) | Gets bottom padding for asset grid labels. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L522) | Gets horizontal padding for asset grid labels. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelLineSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L532) | Gets the additional vertical spacing between asset grid label lines. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMaximumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L312) | Gets maximum asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumCellSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L292) | Gets minimum asset grid cell size. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L307) | Gets minimum asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridScaleBias { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L542) | Gets the scale bias added to asset grid icons. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L242) | Gets asset browser item spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListContentHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L517) | Gets the horizontal inset between an asset list separator and column content. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListMinimumColumnRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L507) | Gets the minimum normalized width reserved for each asset list column. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListNameSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L497) | Gets the default normalized position of the asset list name/type separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L317) | Gets spacing between asset list rows. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListSeparatorHitWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L512) | Gets the horizontal hit width of an asset list column separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListTypeSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L502) | Gets the default normalized position of the asset list type/source separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetPaneMinimumVisibleWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L277) | Gets the minimum visible width retained for either asset browser pane while its splitter is dragged. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetSplitterMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L282) | Gets the minimum draggable asset browser splitter width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L492) | Gets spacing between asset browser toolbar sections. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L487) | Gets the regular asset browser toolbar spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarTightSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L482) | Gets the tight asset browser toolbar spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L232) | Gets asset browser window padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L572) | Gets the minimum width of an axis prefix. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixWidthRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L577) | Gets the axis prefix share of the complete control. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisValueMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L567) | Gets the minimum width of one axis value field. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.borderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L126) | Gets standard border thickness. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.breadcrumbFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L192) | Gets breadcrumb frame padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L222) | Gets standard table cell padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.columnMinimumSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L257) | Gets minimum column spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.compactCheckboxSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L417) | Gets the compact checkbox size used by card headers. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L177) | Gets compact frame padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L207) | Gets compact vertical item spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disabledAlpha { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L111) | Gets disabled content opacity. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disclosureButtonInset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L387) | Gets inspector disclosure inset. | — | — | +| [`public static class Inno.Editor.ImGui.ImGui`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs#L9) | Provides pointer-free controls within the active Editor UI session. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.InvisibleButton(string label, System.Numerics.Vector2 size, Inno.Editor.ImGui.ImGuiButtonFlags flags = Inno.Editor.ImGui.ImGuiButtonFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs#L26) | Submits an explicitly drawn control interaction rectangle.
`label`: The visible label and optional stable ID suffix.
`size`: The explicit two-dimensional extent.
`flags`: The explicit control and interaction options. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.MenuItem(string label, string shortcut = "", bool selected = false, bool enabled = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs#L53) | Draws a menu item with explicit availability and checked state.
`label`: The visible label and optional stable ID suffix.
`shortcut`: A display label only; execution remains with the Action router.
`selected`: Whether the item is currently selected.
`enabled`: Whether the control can accept input. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.SliderInt(string label, ref int value, int minimum, int maximum, string format = "%d", Inno.Editor.ImGui.ImGuiSliderFlags flags = Inno.Editor.ImGui.ImGuiSliderFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs#L87) | Edits an integer within explicit inclusive bounds.
`label`: The visible label and optional stable ID suffix.
`value`: The caller-owned value updated only after an accepted edit.
`minimum`: The inclusive lower bound or upper-left corner.
`maximum`: The inclusive upper bound or lower-right corner.
`format`: The numeric display format.
`flags`: The explicit control and interaction options. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.DragInt(string label, ref int value, float speed = 1, int minimum = 0, int maximum = 0, string format = "%d", Inno.Editor.ImGui.ImGuiSliderFlags flags = Inno.Editor.ImGui.ImGuiSliderFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs#L126) | Edits an integer by pointer drag with explicit speed and optional bounds.
`label`: The visible label and optional stable ID suffix.
`value`: The caller-owned value updated only after an accepted edit.
`speed`: The pointer movement multiplier.
`minimum`: The inclusive lower bound or upper-left corner.
`maximum`: The inclusive upper bound or lower-right corner.
`format`: The numeric display format.
`flags`: The explicit control and interaction options. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static uint Inno.Editor.ImGui.ImGui.GetStyleColor(Inno.Editor.ImGui.ImGuiCol color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs#L20) | Gets the current style color including the active alpha.
`color`: The packed RGBA color or semantic style slot. | A copied value from the active presentation context. | — | +| [`public static uint Inno.Editor.ImGui.ImGui.GetControlId(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs#L35) | Derives a control identity from the current window and ID stack.
`label`: The visible label and optional stable ID suffix. | A copied value from the active presentation context. | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushClipRect(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, bool intersect)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs#L53) | Clips subsequent controls and drawing to a screen rectangle.
`minimum`: The inclusive lower bound or upper-left corner.
`maximum`: The inclusive upper bound or lower-right corner.
`intersect`: Whether to intersect the enclosing clip. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopClipRect()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs#L65) | Restores the enclosing control clip after a matching push. | — | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMousePos()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L17) | Gets the current pointer position in screen-space UI units. | A copied value from the active presentation context. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseClicked(Inno.Editor.ImGui.ImGuiMouseButton button, bool repeat = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L35) | Checks a pointer-button press in the current frame.
`button`: The pointer button to inspect.
`repeat`: Whether a held key or button may produce repeats. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseReleased(Inno.Editor.ImGui.ImGuiMouseButton button)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L52) | Checks a pointer-button release in the current frame.
`button`: The pointer button to inspect. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseDoubleClicked(Inno.Editor.ImGui.ImGuiMouseButton button)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L67) | Checks the backend-classified double-click event.
`button`: The pointer button to inspect. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsKeyDown(Inno.Editor.ImGui.ImGuiKey key)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L82) | Checks whether a presentation key remains held.
`key`: The presentation key to inspect. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsKeyPressed(Inno.Editor.ImGui.ImGuiKey key, bool repeat = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L100) | Checks a presentation-key press with explicit repeat policy.
`key`: The presentation key to inspect.
`repeat`: Whether a held key or button may produce repeats. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static void Inno.Editor.ImGui.ImGui.ResetMouseDragDelta(Inno.Editor.ImGui.ImGuiMouseButton button)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L114) | Resets the consumed pointer-drag distance.
`button`: The pointer button to inspect. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetMouseCursor(Inno.Editor.ImGui.ImGuiMouseCursor cursor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L126) | Requests the semantic cursor for the hovered control.
`cursor`: The semantic pointer shape. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseHoveringRect(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, bool clip = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L147) | Checks pointer containment in a screen rectangle with optional window clipping.
`minimum`: The inclusive lower bound or upper-left corner.
`maximum`: The inclusive upper bound or lower-right corner.
`clip`: Whether the current clipping rectangle participates. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsWindowHovered(Inno.Editor.ImGui.ImGuiHoveredFlags flags = Inno.Editor.ImGui.ImGuiHoveredFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L165) | Checks window hover under the declared interaction policy.
`flags`: The explicit control and interaction options. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsWindowFocused(Inno.Editor.ImGui.ImGuiFocusedFlags flags = Inno.Editor.ImGui.ImGuiFocusedFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L180) | Checks keyboard focus within the declared window scope.
`flags`: The explicit control and interaction options. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsAnyItemHovered()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L192) | Checks whether a submitted control currently owns hover. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsAnyItemActive()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L204) | Checks whether a submitted control owns an active interaction. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemActivated()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L216) | Checks the first frame of the last control interaction. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemDeactivatedAfterEdit()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L228) | Checks the completion frame of an edited control. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetKeyboardFocusHere(int offset = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L240) | Requests focus for a relative control position.
`offset`: The relative focus offset from the next submitted control. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetItemDefaultFocus()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L249) | Chooses the last item as the default selection in an appearing popup. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetClipboardText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L261) | Copies literal text through the host clipboard adapter.
`text`: Literal text; no formatting interpretation is applied. | — | — | +| [`public static int Inno.Editor.ImGui.ImGui.GetFrameCount()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs#L273) | Gets the current native frame identity for transient diagnostics. | A copied value from the active presentation context. | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetCursorScreenPosition(System.Numerics.Vector2 position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L17) | Places the next control at a screen-space location.
`position`: The requested coordinate in UI units. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosX(float position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L29) | Changes only the local horizontal layout cursor.
`position`: The requested coordinate in UI units. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosY(float position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L41) | Changes only the local vertical layout cursor.
`position`: The requested coordinate in UI units. | — | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetCursorPosX()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L53) | Gets the local horizontal layout cursor. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetCursorPosY()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L65) | Gets the local vertical layout cursor. | A copied value from the active presentation context. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetWindowPos()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L77) | Gets the current window screen-space origin. | A copied value from the active presentation context. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetWindowSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L89) | Gets the current window size in UI units. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetFontSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L101) | Gets the active font size in UI units. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetFrameHeight()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L113) | Gets the standard control height including frame padding. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetTextLineHeight()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L125) | Gets the active font line height without spacing. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetFrameHeightWithSpacing()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L137) | Gets the standard control height and following vertical spacing. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetWindowDpiScale()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L149) | Gets the native viewport scale for the current window. | A copied value from the active presentation context. | — | +| [`public static void Inno.Editor.ImGui.ImGui.AlignTextToFramePadding()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L158) | Aligns the next text baseline with framed controls. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetNextItemAllowOverlap()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L167) | Allows explicitly layered controls to share the next item bounds. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetNextWindowSize(System.Numerics.Vector2 size, Inno.Editor.ImGui.ImGuiCond condition = Inno.Editor.ImGui.ImGuiCond.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L182) | Requests a size for the next window under an explicit layout condition.
`size`: The explicit two-dimensional extent.
`condition`: The condition controlling when the request is applied. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetNextWindowContentSize(System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L196) | Declares virtual content size for the next scrollable window.
`size`: The explicit two-dimensional extent. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Indent(float amount = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L208) | Indents subsequent controls by an explicit amount or the standard step.
`amount`: The requested layout distance. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Unindent(float amount = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L220) | Restores a matching indentation step.
`amount`: The requested layout distance. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushTextWrapPos(float position = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L232) | Restricts subsequent text wrapping to a local horizontal boundary.
`position`: The requested coordinate in UI units. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopTextWrapPos()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L241) | Restores the enclosing text-wrap boundary. | — | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetScrollY()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L253) | Gets the current vertical scroll position. | A copied value from the active presentation context. | — | +| [`public static float Inno.Editor.ImGui.ImGui.GetScrollMaxY()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L265) | Gets the current vertical scroll range. | A copied value from the active presentation context. | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetScrollHereY(float ratio = 0.5)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs#L277) | Scrolls the most recently submitted control into the requested relative position.
`ratio`: The relative alignment from zero through one. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.TableSetColumnIndex(int index)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs#L20) | Selects a declared table column for subsequent controls.
`index`: The zero-based item index. | True when the documented control event or condition is present; otherwise false. | — | +| [`public static void Inno.Editor.ImGui.ImGui.TableSetBgColor(Inno.Editor.ImGui.ImGuiTableBgTarget target, uint color, int column = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs#L38) | Sets a table row or cell background through the current table scope.
`target`: The table background role.
`color`: The packed RGBA color or semantic style slot.
`column`: The target column, or -1 for the current row. | — | — | +| [`public static Inno.Editor.ImGui.EditorUiLayout Inno.Editor.ImGui.ImGui.GetLayout()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L16) | Copies the current UI layout metrics. | Value metrics valid independently from the native style pointer. | — | +| [`public static Inno.Editor.ImGui.EditorUiInput Inno.Editor.ImGui.ImGui.GetInput()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L28) | Copies the input facts for this frame. | A neutral value snapshot; no pointer or callback is retained. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.ImGui.GetStyleColorValue(Inno.Editor.ImGui.ImGuiCol color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L43) | Copies an unmodulated style color for compound presentation.
`color`: The semantic style color slot. | The copied linear vector of UI color components. | — | +| [`public static (uint id, System.Numerics.Vector2 position) Inno.Editor.ImGui.ImGui.GetViewport()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L55) | Gets the logical viewport identity and copied position for the current window. | The viewport ID and its screen-space origin. | — | +| [`public static (float minimumX, float maximumX, float rowMinimum, float rowMaximum) Inno.Editor.ImGui.ImGui.GetTableBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L68) | Gets the table and current row bounds needed by row overlays. | The table's horizontal bounds and current row's vertical bounds. | — | +| [`public static (float minimumX, float maximumX, System.Numerics.Vector2 workMaximum, System.Numerics.Vector2 padding) Inno.Editor.ImGui.ImGui.GetWindowBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L83) | Gets copied inner and work bounds for the current window. | Screen-space horizontal bounds and work maximum. | — | +| [`public static Inno.Editor.ImGui.EditorDrawList Inno.Editor.ImGui.ImGui.GetDrawList()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L96) | Borrows drawing commands for the current window and frame without exposing native memory. | A value that rejects use after a window, frame, context or session change. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.HasDragPayload(string protocol)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L106) | Checks the active payload's logical protocol without exposing its native buffer.
`protocol`: The exact drag protocol name. | True when a drag of that protocol is currently active. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text, float scale)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L128) | Measures literal text at a relative font scale.
`text`: The literal text to measure.
`scale`: Scale relative to the current font. | The copied width and height in UI pixels. | — | +| [`public static void Inno.Editor.ImGui.ImGui.Text(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L20) | Draws unformatted text.
`text`: Literal text to draw; percent characters are displayed without format-string interpretation. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.Button(string label, System.Numerics.Vector2 size = default(System.Numerics.Vector2))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L38) | Draws a standard button.
`label`: The visible label and optional ImGui identity suffix.
`size`: The requested size, or zero components for automatic sizing. | when the button is activated. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.SmallButton(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L55) | Draws a compact button.
`label`: The visible label and optional ImGui identity suffix. | when the button is activated. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.Checkbox(string label, ref bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L73) | Draws and edits a Boolean value.
`label`: The control label.
`value`: The checked state, updated when the user toggles the control. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.InputText(string label, ref string value, int capacity = 1024, Inno.Editor.ImGui.ImGuiInputTextFlags flags = Inno.Editor.ImGui.ImGuiInputTextFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L102) | Draws and edits a bounded UTF-8 text value.
`label`: The control label.
`value`: The managed string to display and update.
`capacity`: The positive maximum UTF-8 buffer capacity, including the terminator.
`flags`: Text editing behavior. | when the control reports an edit or submit event. | `System.ArgumentOutOfRangeException`: Thrown when is not positive. | +| [`public static bool Inno.Editor.ImGui.ImGui.InputTextWithHint(string label, string hint, ref string value, int capacity = 1024, Inno.Editor.ImGui.ImGuiInputTextFlags flags = Inno.Editor.ImGui.ImGuiInputTextFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L136) | Draws and edits a bounded UTF-8 text value with placeholder text.
`label`: The control label.
`hint`: The placeholder shown while the value is empty.
`value`: The managed string to display and update.
`capacity`: The positive maximum UTF-8 buffer capacity, including the terminator.
`flags`: Text editing behavior. | when the control reports an edit or submit event. | `System.ArgumentOutOfRangeException`: Thrown when is not positive. | +| [`public static bool Inno.Editor.ImGui.ImGui.InputInt(string label, ref int value, int step = 1, int fastStep = 100, Inno.Editor.ImGui.ImGuiInputTextFlags flags = Inno.Editor.ImGui.ImGuiInputTextFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L168) | Draws and edits an integer value.
`label`: The control label.
`value`: The integer displayed by the control and replaced after an accepted edit.
`step`: Increment used by the ordinary step button.
`fastStep`: Increment used by the accelerated step button.
`flags`: Text editing options for the numeric field. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.InputFloat(string label, ref float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L191) | Draws and edits a floating-point value.
`label`: The control label.
`value`: The number displayed with three fractional digits and replaced after an accepted edit. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.DragFloat(string label, ref float value, float speed = 1, float minimum = 0, float maximum = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L220) | Draws a floating-point drag control.
`label`: The control label.
`value`: The value to display and update.
`speed`: The drag speed.
`minimum`: The inclusive minimum, or zero with a zero maximum for no clamping.
`maximum`: The inclusive maximum, or zero with a zero minimum for no clamping. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.SliderFloat(string label, ref float value, float minimum, float maximum, Inno.Editor.ImGui.ImGuiSliderFlags flags = Inno.Editor.ImGui.ImGuiSliderFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L252) | Draws a bounded floating-point slider.
`label`: The control label.
`value`: The value to display and update.
`minimum`: The inclusive minimum.
`maximum`: The inclusive maximum.
`flags`: Slider behavior. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.ColorEdit4(string label, ref System.Numerics.Vector4 value, Inno.Editor.ImGui.ImGuiColorEditFlags flags = Inno.Editor.ImGui.ImGuiColorEditFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L278) | Draws and edits a four-component color.
`label`: The control label.
`value`: The RGBA value to display and update.
`flags`: Color editing behavior. | when the value changes. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.ColorEditLinear4(string label, ref System.Numerics.Vector4 value, Inno.Editor.ImGui.ImGuiColorEditFlags flags = Inno.Editor.ImGui.ImGuiColorEditFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L308) | Displays a linear RGBA color through a display-sRGB picker and returns edits in linear space.
`label`: The stable identity and visible label of the color control.
`value`: Linear RGB and alpha; only changed RGB channels are decoded, while alpha is never transfer-encoded.
`flags`: Dear ImGui color-edit flags; display modes interpret sRGB values and input storage must be RGB. | True if the display color changed and contains decoded linear RGB. | `System.ArgumentException`: HSV input storage conflicts with the linear-RGB value contract. | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginChild(string id, System.Numerics.Vector2 size = default(System.Numerics.Vector2), Inno.Editor.ImGui.ImGuiChildFlags flags = Inno.Editor.ImGui.ImGuiChildFlags.None, Inno.Editor.ImGui.ImGuiWindowFlags windowFlags = Inno.Editor.ImGui.ImGuiWindowFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L346) | Begins a child region.
`id`: The child identity.
`size`: The requested child size.
`flags`: Child-region behavior.
`windowFlags`: Scroll and window policies for the child. | when child contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndChild()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L359) | Ends the current child region. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginTable(string id, int columns, Inno.Editor.ImGui.ImGuiTableFlags flags = Inno.Editor.ImGui.ImGuiTableFlags.None, System.Numerics.Vector2 size = default(System.Numerics.Vector2), float innerWidth = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L386) | Begins a table.
`id`: The table identity.
`columns`: The positive column count.
`flags`: Table behavior.
`size`: The requested outer size.
`innerWidth`: Optional inner scrolling width; zero uses the outer size. | when table contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndTable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L400) | Ends the current table. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.TableSetupColumn(string label, Inno.Editor.ImGui.ImGuiTableColumnFlags flags = Inno.Editor.ImGui.ImGuiTableColumnFlags.None, float widthOrWeight = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L418) | Declares one table column.
`label`: The column label.
`flags`: Column behavior.
`widthOrWeight`: The initial fixed width or stretch weight. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.TableHeadersRow()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L430) | Draws the table header row from configured column labels. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.TableNextRow(Inno.Editor.ImGui.ImGuiTableRowFlags flags = Inno.Editor.ImGui.ImGuiTableRowFlags.None, float minimumHeight = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L445) | Advances to the next table row.
`flags`: Row presentation flags.
`minimumHeight`: Minimum row height in UI pixels. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.TableNextColumn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L459) | Advances to the next table column. | when the column is visible. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginCombo(string label, string preview, Inno.Editor.ImGui.ImGuiComboFlags flags = Inno.Editor.ImGui.ImGuiComboFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L480) | Begins a combo popup.
`label`: The control label.
`preview`: The preview text.
`flags`: Combo behavior. | when combo contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndCombo()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L492) | Ends the current combo popup. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.Selectable(string label, bool selected = false, Inno.Editor.ImGui.ImGuiSelectableFlags flags = Inno.Editor.ImGui.ImGuiSelectableFlags.None, System.Numerics.Vector2 size = default(System.Numerics.Vector2))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L516) | Draws one selectable item.
`label`: The visible label and identity.
`selected`: Whether the item is currently selected.
`flags`: Selectable behavior.
`size`: The interaction bounds, or zero for the ordinary content size. | when the item is activated. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.CollapsingHeader(string label, Inno.Editor.ImGui.ImGuiTreeNodeFlags flags = Inno.Editor.ImGui.ImGuiTreeNodeFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L538) | Draws a collapsible section header.
`label`: The visible label and identity.
`flags`: Tree-node behavior. | when the section is open. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.TreeNode(string label, Inno.Editor.ImGui.ImGuiTreeNodeFlags flags = Inno.Editor.ImGui.ImGuiTreeNodeFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L558) | Draws a tree node and pushes its tree scope when it is open.
`label`: The visible label and identity.
`flags`: Tree-node behavior. | when child contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.TreePop()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L569) | Ends the current open tree-node scope. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabBar(string id, Inno.Editor.ImGui.ImGuiTabBarFlags flags = Inno.Editor.ImGui.ImGuiTabBarFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L587) | Begins a tab bar.
`id`: The tab bar identity.
`flags`: Tab-bar behavior. | when tab contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndTabBar()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L598) | Ends the current tab bar. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabItem(string label, Inno.Editor.ImGui.ImGuiTabItemFlags flags = Inno.Editor.ImGui.ImGuiTabItemFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L616) | Begins one tab item.
`label`: The tab label and identity.
`flags`: Tab-item behavior. | when tab contents should be submitted. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabItem(string label, ref bool isOpen, Inno.Editor.ImGui.ImGuiTabItemFlags flags = Inno.Editor.ImGui.ImGuiTabItemFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L639) | Begins one closeable tab item.
`label`: The tab label and identity.
`isOpen`: Receives whether the tab remains open.
`flags`: Tab-item behavior. | when tab contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndTabItem()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L650) | Ends the current tab item. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.OpenPopup(string id, Inno.Editor.ImGui.ImGuiPopupFlags flags = Inno.Editor.ImGui.ImGuiPopupFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L665) | Opens a named popup.
`id`: The popup identity.
`flags`: Popup opening behavior. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.BeginPopup(string id, Inno.Editor.ImGui.ImGuiWindowFlags flags = Inno.Editor.ImGui.ImGuiWindowFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L685) | Begins a named popup.
`id`: The popup identity.
`flags`: Popup window behavior. | when popup contents should be submitted. | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L696) | Ends the current popup. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.CloseCurrentPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L705) | Closes the current popup. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.BeginDisabled(bool disabled = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L717) | Begins a disabled block.
`disabled`: Whether controls in the block are disabled. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndDisabled()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L726) | Ends the current disabled block. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.BeginGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L735) | Begins an item group. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L744) | Ends the current item group. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushId(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L756) | Pushes a string identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushId(int id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L768) | Pushes an integer identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopId()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L777) | Pops one identity from the ImGui ID stack. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleColor(Inno.Editor.ImGui.ImGuiCol color, System.Numerics.Vector4 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L792) | Pushes one color override.
`color`: The style color slot.
`value`: The linear RGBA value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopStyleColor(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L806) | Pops color overrides.
`count`: The positive number of overrides to pop. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L821) | Pushes one scalar style override.
`style`: The style variable.
`value`: The scalar value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, System.Numerics.Vector2 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L838) | Pushes one two-component style override.
`style`: The style variable.
`value`: The vector value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopStyleVar(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L852) | Pops style-variable overrides.
`count`: The positive number of overrides to pop. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SameLine(float offset = 0, float spacing = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L867) | Places the next item on the same line.
`offset`: The horizontal offset from the line start, or zero for automatic placement.
`spacing`: The explicit spacing, or a negative value for the current style spacing. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Separator()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L878) | Draws a horizontal separator. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SeparatorText(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L890) | Draws a labeled horizontal separator.
`label`: The label text validated by the separator text operation. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Spacing()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L899) | Adds one standard vertical spacing unit. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Dummy(System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L911) | Advances layout by an invisible size.
`size`: The size consumed by dummy; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetNextItemWidth(float width)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L923) | Sets the width of the next item.
`width`: The width in logical units or pixels required by this operation. | — | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetContentRegionAvailable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L935) | Gets the remaining content size in the current region. | The remaining local width and height. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L947) | Gets the current local cursor position. | The local cursor position. | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosition(System.Numerics.Vector2 position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L959) | Sets the current local cursor position.
`position`: The position consumed by set cursor position; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorScreenPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L971) | Gets the current screen-space cursor position. | The screen-space cursor position. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMinimum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L983) | Gets the minimum screen-space corner of the previous item. | The item minimum corner. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMaximum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L995) | Gets the maximum screen-space corner of the previous item. | The item maximum corner. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1010) | Measures text using the current font.
`text`: The text text validated by the measure text operation. | The measured width and height. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemHovered(Inno.Editor.ImGui.ImGuiHoveredFlags flags = Inno.Editor.ImGui.ImGuiHoveredFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1025) | Gets whether the previous item is hovered.
`flags`: Hovered query behavior. | when the query succeeds. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemActive()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1037) | Gets whether the previous item is active. | while the item owns an active interaction. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemClicked(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1052) | Gets whether the previous item was clicked with a mouse button.
`button`: The mouse button to query. | when the item was clicked. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseButtonDown(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1067) | Gets whether a mouse button is held in the current Editor UI frame, including input consumed by UI controls.
`button`: The UI mouse button to query. | True while the button is held; callers must also check their item's active or hovered state before editing. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMouseDragDelta(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left, float threshold = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1086) | Gets accumulated pointer displacement from the current UI drag's starting position for timeline and canvas controls.
`button`: The button that owns the drag.
`threshold`: Movement required to start dragging, or a negative value to use the Editor's configured threshold. | Displacement in UI coordinates, or zero before the drag threshold is crossed or when no drag is available. The value is cumulative; it must be applied to a captured starting value, not added again each frame. | — | +| [`public static uint Inno.Editor.ImGui.ImGui.ToPackedColor(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1103) | Converts a floating-point RGBA color to Dear ImGui packed color order.
`color`: The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. | The packed color value. | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1124) | Adds a line to the current window draw list.
`start`: The screen-space start point.
`end`: The screen-space end point.
`color`: The packed ImGui color.
`thickness`: The line thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1152) | Adds a rectangle outline to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius.
`thickness`: The outline thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1178) | Adds a filled rectangle to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawCubicCurve(System.Numerics.Vector2 start, System.Numerics.Vector2 firstControl, System.Numerics.Vector2 secondControl, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1209) | Draws a cubic curve without exposing a native draw list or borrowed pointer.
`start`: The first screen-space endpoint.
`firstControl`: The control point beside the first endpoint.
`secondControl`: The control point beside the final endpoint.
`end`: The final screen-space endpoint.
`color`: The packed presentation color.
`thickness`: The positive stroke width. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1234) | Adds text to the current window draw list.
`position`: The screen-space text origin.
`color`: The packed ImGui color.
`text`: The text to draw. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiButtonFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L8) | Defines the supported button flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonLeft = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L17) | React on left mouse button (default) | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonRight = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L21) | React on right mouse button | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonMiddle = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L25) | React on center mouse button | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonMask = Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonLeft \| Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonRight \| Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonMiddle`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L29) | Selects the mouse button mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiButtonFlags.EnableNav = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L33) | InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiChildFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L8) | Defines the supported child flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.Borders = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L17) | Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.AlwaysUseWindowPadding = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L21) | Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.ResizeX = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L25) | Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.ResizeY = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L29) | Allow resize from bottom border (layout direction). " | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.AutoResizeX = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L33) | Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.AutoResizeY = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L37) | Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.AlwaysAutoResize = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L41) | Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.FrameStyle = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L45) | Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. | — | — | +| [`Inno.Editor.ImGui.ImGuiChildFlags.NavFlattened = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs#L49) | Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiCol : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L7) | Defines the supported col choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Text = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L12) | Selects the text presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TextDisabled = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L16) | Selects the text disabled presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.WindowBg = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L20) | Selects the window bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ChildBg = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L24) | Selects the child bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.PopupBg = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L28) | Selects the popup bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Border = 5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L32) | Selects the border presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.BorderShadow = 6`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L36) | Selects the border shadow presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.FrameBg = 7`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L40) | Selects the frame bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.FrameBgHovered = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L44) | Selects the frame bg hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.FrameBgActive = 9`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L48) | Selects the frame bg active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TitleBg = 10`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L52) | Selects the title bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TitleBgActive = 11`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L56) | Selects the title bg active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TitleBgCollapsed = 12`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L60) | Selects the title bg collapsed presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.MenuBarBg = 13`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L64) | Selects the menu bar bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ScrollbarBg = 14`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L68) | Selects the scrollbar bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ScrollbarGrab = 15`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L72) | Selects the scrollbar grab presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ScrollbarGrabHovered = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L76) | Selects the scrollbar grab hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ScrollbarGrabActive = 17`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L80) | Selects the scrollbar grab active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.CheckMark = 18`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L84) | Selects the check mark presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.SliderGrab = 19`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L88) | Selects the slider grab presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.SliderGrabActive = 20`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L92) | Selects the slider grab active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Button = 21`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L96) | Selects the button presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ButtonHovered = 22`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L100) | Selects the button hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ButtonActive = 23`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L104) | Selects the button active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Header = 24`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L108) | Selects the header presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.HeaderHovered = 25`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L112) | Selects the header hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.HeaderActive = 26`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L116) | Selects the header active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Separator = 27`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L120) | Selects the separator presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.SeparatorHovered = 28`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L124) | Selects the separator hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.SeparatorActive = 29`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L128) | Selects the separator active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ResizeGrip = 30`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L132) | Selects the resize grip presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ResizeGripHovered = 31`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L136) | Selects the resize grip hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ResizeGripActive = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L140) | Selects the resize grip active presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.InputTextCursor = 33`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L144) | Selects the input text cursor presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabHovered = 34`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L148) | Selects the tab hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Tab = 35`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L152) | Selects the tab presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabSelected = 36`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L156) | Selects the tab selected presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabSelectedOverline = 37`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L160) | Selects the tab selected overline presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabDimmed = 38`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L164) | Selects the tab dimmed presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabDimmedSelected = 39`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L168) | Selects the tab dimmed selected presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TabDimmedSelectedOverline = 40`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L172) | Selects the tab dimmed selected overline presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.DockingPreview = 41`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L176) | Selects the docking preview presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.DockingEmptyBg = 42`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L180) | Selects the docking empty bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.PlotLines = 43`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L184) | Selects the plot lines presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.PlotLinesHovered = 44`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L188) | Selects the plot lines hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.PlotHistogram = 45`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L192) | Selects the plot histogram presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.PlotHistogramHovered = 46`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L196) | Selects the plot histogram hovered presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TableHeaderBg = 47`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L200) | Selects the table header bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TableBorderStrong = 48`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L204) | Selects the table border strong presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TableBorderLight = 49`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L208) | Selects the table border light presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TableRowBg = 50`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L212) | Selects the table row bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TableRowBgAlt = 51`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L216) | Selects the table row bg alt presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TextLink = 52`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L220) | Selects the text link presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TextSelectedBg = 53`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L224) | Selects the text selected bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.TreeLines = 54`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L228) | Selects the tree lines presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.DragDropTarget = 55`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L232) | Selects the drag drop target presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.DragDropTargetBg = 56`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L236) | Selects the drag drop target bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.UnsavedMarker = 57`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L240) | Selects the unsaved marker presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.NavCursor = 58`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L244) | Selects the nav cursor presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.NavWindowingHighlight = 59`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L248) | Selects the nav windowing highlight presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.NavWindowingDimBg = 60`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L252) | Selects the nav windowing dim bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.ModalWindowDimBg = 61`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L256) | Selects the modal window dim bg presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiCol.Count = 62`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs#L260) | Marks the exclusive end of the valid option range. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiColorEditFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L8) | Defines the supported color edit flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoAlpha = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L17) | // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoPicker = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L21) | // ColorEdit: disable picker when clicking on color square. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoOptions = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L25) | // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoSmallPreview = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L29) | // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs) | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoInputs = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L33) | // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square). | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoTooltip = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L37) | // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoLabel = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L41) | // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoSidePreview = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L45) | // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoDragDrop = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L49) | // ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoBorder = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L53) | // ColorButton: disable border (which is enforced by default) | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.NoColorMarkers = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L57) | // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaOpaque = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L61) | // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaNoBg = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L65) | // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaPreviewHalf = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L69) | // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaBar = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L73) | // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.Hdr = 524288`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L77) | Selects the hdr presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayRgb = 1048576`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L81) | Selects the display rgb presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayHsv = 2097152`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L85) | Selects the display hsv presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayHex = 4194304`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L89) | // " | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.Uint8 = 8388608`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L93) | // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.Float = 16777216`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L97) | // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.PickerHueBar = 33554432`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L101) | // ColorPicker: bar for Hue, rectangle for Sat/Value. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.PickerHueWheel = 67108864`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L105) | // ColorPicker: wheel for Hue, triangle for Sat/Value. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.InputRgb = 134217728`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L109) | Selects the input rgb presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.InputHsv = 268435456`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L113) | Selects the input hsv presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DefaultOptions = Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayRgb \| Inno.Editor.ImGui.ImGuiColorEditFlags.Uint8 \| Inno.Editor.ImGui.ImGuiColorEditFlags.PickerHueBar \| Inno.Editor.ImGui.ImGuiColorEditFlags.InputRgb`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L117) | Selects the default options presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaMask = Inno.Editor.ImGui.ImGuiColorEditFlags.NoAlpha \| Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaOpaque \| Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaNoBg \| Inno.Editor.ImGui.ImGuiColorEditFlags.AlphaPreviewHalf`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L121) | Selects the alpha mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayMask = Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayRgb \| Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayHsv \| Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayHex`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L125) | Selects the display mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.DataTypeMask = Inno.Editor.ImGui.ImGuiColorEditFlags.Uint8 \| Inno.Editor.ImGui.ImGuiColorEditFlags.Float`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L129) | Selects the data type mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.PickerMask = Inno.Editor.ImGui.ImGuiColorEditFlags.PickerHueBar \| Inno.Editor.ImGui.ImGuiColorEditFlags.PickerHueWheel`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L133) | Selects the picker mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiColorEditFlags.InputMask = Inno.Editor.ImGui.ImGuiColorEditFlags.InputRgb \| Inno.Editor.ImGui.ImGuiColorEditFlags.InputHsv`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs#L137) | Selects the input mask presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiComboFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L8) | Defines the supported combo flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.PopupAlignLeft = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L17) | Align the popup toward the left by default | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.HeightSmall = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L21) | Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.HeightRegular = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L25) | Max ~8 items visible (default) | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.HeightLarge = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L29) | Max ~20 items visible | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.HeightLargest = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L33) | As many fitting items as possible | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.NoArrowButton = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L37) | Display on the preview box without the square arrow button | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.NoPreview = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L41) | Display only a square arrow button | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.WidthFitPreview = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L45) | Width dynamically calculated from preview contents | — | — | +| [`Inno.Editor.ImGui.ImGuiComboFlags.HeightMask = Inno.Editor.ImGui.ImGuiComboFlags.HeightSmall \| Inno.Editor.ImGui.ImGuiComboFlags.HeightRegular \| Inno.Editor.ImGui.ImGuiComboFlags.HeightLarge \| Inno.Editor.ImGui.ImGuiComboFlags.HeightLargest`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs#L49) | Selects the height mask presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiCond : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L8) | Defines the supported cond choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiCond.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L13) | No condition (always set the variable), same as _Always | — | — | +| [`Inno.Editor.ImGui.ImGuiCond.Always = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L17) | No condition (always set the variable), same as _None | — | — | +| [`Inno.Editor.ImGui.ImGuiCond.Once = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L21) | Set the variable once per runtime session (only the first call will succeed) | — | — | +| [`Inno.Editor.ImGui.ImGuiCond.FirstUseEver = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L25) | Set the variable if the object/window has no persistently saved data (no entry in .ini file) | — | — | +| [`Inno.Editor.ImGui.ImGuiCond.Appearing = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs#L29) | Set the variable if the object/window is appearing after being hidden/inactive (or the first time) | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiFocusedFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L8) | Defines the supported focused flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.ChildWindows = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L17) | Return true if any children of the window is focused | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.RootWindow = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L21) | Test from root window (top most parent of the current hierarchy) | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.AnyWindow = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L25) | Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.NoPopupHierarchy = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L29) | Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.DockHierarchy = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L33) | Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) | — | — | +| [`Inno.Editor.ImGui.ImGuiFocusedFlags.RootAndChildWindows = Inno.Editor.ImGui.ImGuiFocusedFlags.ChildWindows \| Inno.Editor.ImGui.ImGuiFocusedFlags.RootWindow`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs#L37) | Selects the root and child windows presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiHoveredFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L8) | Defines the supported hovered flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L13) | Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.ChildWindows = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L17) | IsWindowHovered() only: Return true if any children of the window is hovered | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.RootWindow = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L21) | IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AnyWindow = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L25) | IsWindowHovered() only: Return true if any window is hovered | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.NoPopupHierarchy = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L29) | IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.DockHierarchy = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L33) | IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByPopup = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L37) | Return true even if a popup window is normally blocking access to this item/window | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByActiveItem = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L41) | Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByItem = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L45) | IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByWindow = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L49) | IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenDisabled = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L53) | IsItemHovered() only: Return true even if the item is disabled | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.NoNavOverride = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L57) | IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlapped = Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByItem \| Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByWindow`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L61) | Selects the allow when overlapped presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.RectOnly = Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByPopup \| Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByActiveItem \| Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenOverlapped`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L65) | Selects the rect only presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.RootAndChildWindows = Inno.Editor.ImGui.ImGuiHoveredFlags.ChildWindows \| Inno.Editor.ImGui.ImGuiHoveredFlags.RootWindow`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L69) | Selects the root and child windows presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.ForTooltip = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L73) | Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.Stationary = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L77) | Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.DelayNone = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L81) | IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.DelayShort = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L85) | IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.DelayNormal = 65536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L89) | IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). | — | — | +| [`Inno.Editor.ImGui.ImGuiHoveredFlags.NoSharedDelay = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs#L93) | IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiInputTextFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L8) | Defines the supported input text flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CharsDecimal = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L17) | Allow 0123456789.+-*/ | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CharsHexadecimal = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L21) | Allow 0123456789ABCDEFabcdef | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CharsScientific = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L25) | Allow 0123456789.+-*/eE (Scientific notation input) | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CharsUppercase = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L29) | Turn a..z into A..Z | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CharsNoBlank = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L33) | Filter out spaces, tabs | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.AllowTabInput = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L37) | Pressing TAB input a '\t' character into the text field | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.EnterReturnsTrue = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L41) | Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.EscapeClearsAll = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L45) | Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CtrlEnterForNewLine = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L49) | In multi-line mode, validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.ReadOnly = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L53) | Read-only mode | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.Password = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L57) | Password mode, display all characters as '*', disable copy | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.AlwaysOverwrite = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L61) | Overwrite mode | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.AutoSelectAll = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L65) | Select entire text when first taking mouse focus | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.ParseEmptyRefVal = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L69) | InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.DisplayEmptyRefVal = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L73) | InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.NoHorizontalScroll = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L77) | Disable following the cursor horizontally | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.NoUndoRedo = 65536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L81) | Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.ElideLeft = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L85) | When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackCompletion = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L89) | Callback on pressing TAB (for completion handling) | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackHistory = 524288`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L93) | Callback on pressing Up/Down arrows (for history handling) | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackAlways = 1048576`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L97) | Callback on each iteration. User code may query cursor position, modify text buffer. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackCharFilter = 2097152`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L101) | Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackResize = 4194304`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L105) | Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.CallbackEdit = 8388608`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L109) | Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. | — | — | +| [`Inno.Editor.ImGui.ImGuiInputTextFlags.WordWrap = 16777216`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs#L113) | InputTextMultiline(): word-wrap lines that are too long. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiKey : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L7) | Defines the supported key choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L12) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.NamedKeyBegin = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L16) | Identifies the NamedKeyBegin presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Tab = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L20) | == ImGuiKey_NamedKey_BEGIN | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftArrow = 513`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L24) | Identifies the LeftArrow presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightArrow = 514`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L28) | Identifies the RightArrow presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.UpArrow = 515`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L32) | Identifies the UpArrow presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.DownArrow = 516`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L36) | Identifies the DownArrow presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.PageUp = 517`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L40) | Identifies the PageUp presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.PageDown = 518`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L44) | Identifies the PageDown presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Home = 519`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L48) | Identifies the Home presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.End = 520`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L52) | Identifies the End presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Insert = 521`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L56) | Identifies the Insert presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Delete = 522`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L60) | Identifies the Delete presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Backspace = 523`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L64) | Identifies the Backspace presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Space = 524`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L68) | Identifies the Space presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Enter = 525`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L72) | Identifies the Enter presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Escape = 526`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L76) | Identifies the Escape presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftCtrl = 527`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L80) | Identifies the LeftCtrl presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftShift = 528`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L84) | Identifies the LeftShift presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftAlt = 529`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L88) | Identifies the LeftAlt presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftSuper = 530`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L92) | Identifies the LeftSuper presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightCtrl = 531`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L96) | Identifies the RightCtrl presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightShift = 532`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L100) | Identifies the RightShift presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightAlt = 533`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L104) | Identifies the RightAlt presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightSuper = 534`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L108) | Identifies the RightSuper presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Menu = 535`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L112) | Identifies the Menu presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._0 = 536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L116) | Identifies the _0 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._1 = 537`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L120) | Identifies the _1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._2 = 538`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L124) | Identifies the _2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._3 = 539`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L128) | Identifies the _3 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._4 = 540`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L132) | Identifies the _4 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._5 = 541`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L136) | Identifies the _5 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._6 = 542`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L140) | Identifies the _6 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._7 = 543`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L144) | Identifies the _7 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._8 = 544`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L148) | Identifies the _8 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey._9 = 545`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L152) | Identifies the _9 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.A = 546`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L156) | Identifies the A presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.B = 547`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L160) | Identifies the B presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.C = 548`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L164) | Identifies the C presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.D = 549`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L168) | Identifies the D presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.E = 550`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L172) | Identifies the E presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F = 551`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L176) | Identifies the F presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.G = 552`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L180) | Identifies the G presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.H = 553`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L184) | Identifies the H presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.I = 554`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L188) | Identifies the I presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.J = 555`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L192) | Identifies the J presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.K = 556`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L196) | Identifies the K presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.L = 557`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L200) | Identifies the L presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.M = 558`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L204) | Identifies the M presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.N = 559`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L208) | Identifies the N presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.O = 560`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L212) | Identifies the O presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.P = 561`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L216) | Identifies the P presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Q = 562`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L220) | Identifies the Q presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.R = 563`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L224) | Identifies the R presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.S = 564`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L228) | Identifies the S presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.T = 565`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L232) | Identifies the T presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.U = 566`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L236) | Identifies the U presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.V = 567`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L240) | Identifies the V presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.W = 568`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L244) | Identifies the W presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.X = 569`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L248) | Identifies the X presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Y = 570`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L252) | Identifies the Y presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Z = 571`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L256) | Identifies the Z presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F1 = 572`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L260) | Identifies the F1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F2 = 573`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L264) | Identifies the F2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F3 = 574`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L268) | Identifies the F3 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F4 = 575`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L272) | Identifies the F4 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F5 = 576`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L276) | Identifies the F5 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F6 = 577`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L280) | Identifies the F6 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F7 = 578`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L284) | Identifies the F7 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F8 = 579`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L288) | Identifies the F8 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F9 = 580`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L292) | Identifies the F9 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F10 = 581`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L296) | Identifies the F10 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F11 = 582`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L300) | Identifies the F11 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F12 = 583`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L304) | Identifies the F12 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F13 = 584`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L308) | Identifies the F13 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F14 = 585`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L312) | Identifies the F14 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F15 = 586`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L316) | Identifies the F15 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F16 = 587`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L320) | Identifies the F16 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F17 = 588`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L324) | Identifies the F17 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F18 = 589`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L328) | Identifies the F18 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F19 = 590`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L332) | Identifies the F19 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F20 = 591`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L336) | Identifies the F20 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F21 = 592`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L340) | Identifies the F21 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F22 = 593`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L344) | Identifies the F22 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F23 = 594`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L348) | Identifies the F23 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.F24 = 595`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L352) | Identifies the F24 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Apostrophe = 596`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L356) | Identifies the Apostrophe presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Comma = 597`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L360) | Identifies the Comma presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Minus = 598`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L364) | Identifies the Minus presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Period = 599`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L368) | Identifies the Period presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Slash = 600`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L372) | Identifies the Slash presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Semicolon = 601`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L376) | Identifies the Semicolon presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Equal = 602`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L380) | Identifies the Equal presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.LeftBracket = 603`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L384) | Identifies the LeftBracket presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Backslash = 604`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L388) | Identifies the Backslash presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.RightBracket = 605`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L392) | Identifies the RightBracket presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GraveAccent = 606`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L396) | Identifies the GraveAccent presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.CapsLock = 607`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L400) | Identifies the CapsLock presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ScrollLock = 608`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L404) | Identifies the ScrollLock presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.NumLock = 609`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L408) | Identifies the NumLock presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.PrintScreen = 610`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L412) | Identifies the PrintScreen presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Pause = 611`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L416) | Identifies the Pause presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad0 = 612`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L420) | Identifies the Keypad0 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad1 = 613`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L424) | Identifies the Keypad1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad2 = 614`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L428) | Identifies the Keypad2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad3 = 615`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L432) | Identifies the Keypad3 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad4 = 616`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L436) | Identifies the Keypad4 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad5 = 617`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L440) | Identifies the Keypad5 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad6 = 618`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L444) | Identifies the Keypad6 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad7 = 619`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L448) | Identifies the Keypad7 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad8 = 620`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L452) | Identifies the Keypad8 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Keypad9 = 621`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L456) | Identifies the Keypad9 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadDecimal = 622`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L460) | Identifies the KeypadDecimal presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadDivide = 623`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L464) | Identifies the KeypadDivide presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadMultiply = 624`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L468) | Identifies the KeypadMultiply presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadSubtract = 625`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L472) | Identifies the KeypadSubtract presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadAdd = 626`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L476) | Identifies the KeypadAdd presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadEnter = 627`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L480) | Identifies the KeypadEnter presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.KeypadEqual = 628`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L484) | Identifies the KeypadEqual presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.AppBack = 629`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L488) | Identifies the AppBack presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.AppForward = 630`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L492) | Identifies the AppForward presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.Oem102 = 631`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L496) | Identifies the Oem102 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadStart = 632`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L500) | Identifies the GamepadStart presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadBack = 633`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L504) | Identifies the GamepadBack presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadFaceLeft = 634`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L508) | Identifies the GamepadFaceLeft presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadFaceRight = 635`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L512) | Identifies the GamepadFaceRight presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadFaceUp = 636`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L516) | Identifies the GamepadFaceUp presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadFaceDown = 637`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L520) | Identifies the GamepadFaceDown presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadDpadLeft = 638`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L524) | Identifies the GamepadDpadLeft presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadDpadRight = 639`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L528) | Identifies the GamepadDpadRight presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadDpadUp = 640`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L532) | Identifies the GamepadDpadUp presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadDpadDown = 641`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L536) | Identifies the GamepadDpadDown presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadL1 = 642`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L540) | Identifies the GamepadL1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadR1 = 643`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L544) | Identifies the GamepadR1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadL2 = 644`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L548) | Identifies the GamepadL2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadR2 = 645`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L552) | Identifies the GamepadR2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadL3 = 646`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L556) | Identifies the GamepadL3 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadR3 = 647`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L560) | Identifies the GamepadR3 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadLStickLeft = 648`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L564) | Identifies the GamepadLStickLeft presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadLStickRight = 649`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L568) | Identifies the GamepadLStickRight presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadLStickUp = 650`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L572) | Identifies the GamepadLStickUp presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadLStickDown = 651`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L576) | Identifies the GamepadLStickDown presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadRStickLeft = 652`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L580) | Identifies the GamepadRStickLeft presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadRStickRight = 653`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L584) | Identifies the GamepadRStickRight presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadRStickUp = 654`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L588) | Identifies the GamepadRStickUp presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.GamepadRStickDown = 655`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L592) | Identifies the GamepadRStickDown presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseLeft = 656`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L596) | Identifies the MouseLeft presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseRight = 657`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L600) | Identifies the MouseRight presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseMiddle = 658`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L604) | Identifies the MouseMiddle presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseX1 = 659`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L608) | Identifies the MouseX1 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseX2 = 660`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L612) | Identifies the MouseX2 presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseWheelX = 661`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L616) | Identifies the MouseWheelX presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.MouseWheelY = 662`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L620) | Identifies the MouseWheelY presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ReservedForModCtrl = 663`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L624) | Identifies the ReservedForModCtrl presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ReservedForModShift = 664`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L628) | Identifies the ReservedForModShift presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ReservedForModAlt = 665`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L632) | Identifies the ReservedForModAlt presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ReservedForModSuper = 666`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L636) | Identifies the ReservedForModSuper presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.NamedKeyEnd = 667`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L640) | Identifies the NamedKeyEnd presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.NamedKeyCount = 155`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L644) | Identifies the NamedKeyCount presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModNone = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L648) | Identifies the ImGuiModNone presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModCtrl = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L652) | Identifies the ImGuiModCtrl presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModShift = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L656) | Identifies the ImGuiModShift presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModAlt = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L660) | Identifies the ImGuiModAlt presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModSuper = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L664) | Identifies the ImGuiModSuper presentation key or modifier. | — | — | +| [`Inno.Editor.ImGui.ImGuiKey.ImGuiModMask = 61440`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs#L668) | Identifies the ImGuiModMask presentation key or modifier. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiMouseButton : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs#L7) | Defines the supported mouse button choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseButton.Left = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs#L12) | Selects the left presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseButton.Right = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs#L16) | Selects the right presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseButton.Middle = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs#L20) | Selects the middle presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseButton.Count = 5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs#L24) | Marks the exclusive end of the valid option range. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiMouseCursor : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L7) | Defines the supported mouse cursor choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.None = -1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L12) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.Arrow = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L16) | Selects the arrow presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.TextInput = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L20) | Selects the text input presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.ResizeAll = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L24) | Selects the resize all presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.ResizeNs = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L28) | Selects the resize ns presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.ResizeEw = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L32) | Selects the resize ew presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.ResizeNesw = 5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L36) | Selects the resize nesw presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.ResizeNwse = 6`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L40) | Selects the resize nwse presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.Hand = 7`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L44) | Selects the hand presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.Wait = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L48) | Selects the wait presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.Progress = 9`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L52) | Selects the progress presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.NotAllowed = 10`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L56) | Selects the not allowed presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiMouseCursor.Count = 11`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs#L60) | Marks the exclusive end of the valid option range. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiPopupFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L8) | Defines the supported popup flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonLeft = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L17) | For BeginPopupContext*(): open on Left Mouse release. Only one button allowed! | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonRight = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L21) | For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default) | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonMiddle = Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonLeft \| Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonRight`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L25) | For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed! | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.NoReopen = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L29) | For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation) | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.NoOpenOverExistingPopup = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L33) | For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.NoOpenOverItems = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L37) | For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.AnyPopupId = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L41) | For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.AnyPopupLevel = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L45) | For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.AnyPopup = Inno.Editor.ImGui.ImGuiPopupFlags.AnyPopupId \| Inno.Editor.ImGui.ImGuiPopupFlags.AnyPopupLevel`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L49) | Selects the any popup presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonShift = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L53) | Selects the mouse button shift presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonMask = Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonLeft \| Inno.Editor.ImGui.ImGuiPopupFlags.MouseButtonRight`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L57) | Selects the mouse button mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiPopupFlags.InvalidMask = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs#L61) | Selects the invalid mask presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiSelectableFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L8) | Defines the supported selectable flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.NoAutoClosePopups = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L17) | Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups) | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.SpanAllColumns = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L21) | Frame will span all columns of its container table (text will still fit in current column) | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.AllowDoubleClick = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L25) | Generate press events on double clicks too | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.Disabled = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L29) | Cannot be selected, display grayed out text | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.AllowOverlap = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L33) | (WIP) Hit testing to allow subsequent widgets to overlap this one | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.Highlight = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L37) | Make the item be displayed as if it is hovered | — | — | +| [`Inno.Editor.ImGui.ImGuiSelectableFlags.SelectOnNav = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs#L41) | Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiSliderFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L8) | Defines the supported slider flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.Logarithmic = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L17) | Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.NoRoundToFormat = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L21) | Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.NoInput = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L25) | Disable Ctrl+Click or Enter key allowing to input text directly into the widget. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.WrapAround = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L29) | Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.ClampOnInput = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L33) | Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.ClampZeroRange = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L37) | Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.NoSpeedTweaks = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L41) | Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.ColorMarkers = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L45) | DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.AlwaysClamp = Inno.Editor.ImGui.ImGuiSliderFlags.ClampOnInput \| Inno.Editor.ImGui.ImGuiSliderFlags.ClampZeroRange`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L49) | Selects the always clamp presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiSliderFlags.InvalidMask = 1879048207`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs#L53) | Selects the invalid mask presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiStyleVar : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L7) | Defines the supported style var choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.Alpha = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L12) | Selects the alpha presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.DisabledAlpha = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L16) | Selects the disabled alpha presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.WindowPadding = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L20) | Selects the window padding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.WindowRounding = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L24) | Selects the window rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.WindowBorderSize = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L28) | Selects the window border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.WindowMinSize = 5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L32) | Selects the window min size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.WindowTitleAlign = 6`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L36) | Selects the window title align presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ChildRounding = 7`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L40) | Selects the child rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ChildBorderSize = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L44) | Selects the child border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.PopupRounding = 9`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L48) | Selects the popup rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.PopupBorderSize = 10`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L52) | Selects the popup border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.FramePadding = 11`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L56) | Selects the frame padding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.FrameRounding = 12`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L60) | Selects the frame rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.FrameBorderSize = 13`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L64) | Selects the frame border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ItemSpacing = 14`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L68) | Selects the item spacing presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ItemInnerSpacing = 15`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L72) | Selects the item inner spacing presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.IndentSpacing = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L76) | Selects the indent spacing presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.CellPadding = 17`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L80) | Selects the cell padding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ScrollbarSize = 18`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L84) | Selects the scrollbar size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ScrollbarRounding = 19`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L88) | Selects the scrollbar rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ScrollbarPadding = 20`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L92) | Selects the scrollbar padding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.GrabMinSize = 21`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L96) | Selects the grab min size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.GrabRounding = 22`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L100) | Selects the grab rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ImageRounding = 23`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L104) | Selects the image rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ImageBorderSize = 24`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L108) | Selects the image border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabRounding = 25`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L112) | Selects the tab rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabBorderSize = 26`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L116) | Selects the tab border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabMinWidthBase = 27`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L120) | Selects the tab min width base presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabMinWidthShrink = 28`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L124) | Selects the tab min width shrink presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabBarBorderSize = 29`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L128) | Selects the tab bar border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TabBarOverlineSize = 30`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L132) | Selects the tab bar overline size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TableAngledHeadersAngle = 31`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L136) | Selects the table angled headers angle presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TableAngledHeadersTextAlign = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L140) | Selects the table angled headers text align presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TreeLinesSize = 33`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L144) | Selects the tree lines size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.TreeLinesRounding = 34`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L148) | Selects the tree lines rounding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.ButtonTextAlign = 35`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L152) | Selects the button text align presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.SelectableTextAlign = 36`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L156) | Selects the selectable text align presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.SeparatorTextBorderSize = 37`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L160) | Selects the separator text border size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.SeparatorTextAlign = 38`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L164) | Selects the separator text align presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.SeparatorTextPadding = 39`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L168) | Selects the separator text padding presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.DockingSeparatorSize = 40`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L172) | Selects the docking separator size presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiStyleVar.Count = 41`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs#L176) | Marks the exclusive end of the valid option range. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTabBarFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L8) | Defines the supported tab bar flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.Reorderable = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L17) | Allow manually dragging tabs to re-order them + New tabs are appended at the end of list | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.AutoSelectNewTabs = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L21) | Automatically select new tabs when they appear | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.TabListPopupButton = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L25) | Disable buttons to open the tab list popup | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.NoCloseWithMiddleMouseButton = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L29) | Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() & & IsMouseClicked(2)) *p_open = false. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.NoTabListScrollingButtons = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L33) | Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.NoTooltip = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L37) | Disable tooltips when hovering a tab | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.DrawSelectedOverline = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L41) | Draw selected overline markers over selected tab | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyMixed = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L45) | Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyShrink = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L49) | Shrink down tabs when they don't fit | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyScroll = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L53) | Enable scrolling buttons when tabs don't fit | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyMask = Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyDefault \| Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyShrink \| Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyScroll`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L57) | Selects the fitting policy mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabBarFlags.FittingPolicyDefault = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs#L61) | Selects the fitting policy default presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTabItemFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L8) | Defines the supported tab item flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.UnsavedDocument = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L17) | Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.SetSelected = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L21) | Trigger flag to programmatically make the tab selected when calling BeginTabItem() | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.NoCloseWithMiddleMouseButton = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L25) | Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() & & IsMouseClicked(2)) *p_open = false. | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.NoPushId = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L29) | Don't call PushID()/PopID() on BeginTabItem()/EndTabItem() | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.NoTooltip = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L33) | Disable tooltip for the given tab | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.NoReorder = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L37) | Disable reordering this tab or having another tab cross over this tab | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.Leading = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L41) | Enforce the tab position to the left of the tab bar (after the tab list popup button) | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.Trailing = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L45) | Enforce the tab position to the right of the tab bar (before the scrolling buttons) | — | — | +| [`Inno.Editor.ImGui.ImGuiTabItemFlags.NoAssumedClosure = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs#L49) | Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTableBgTarget : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs#L7) | Defines the supported table bg target choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableBgTarget.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs#L12) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableBgTarget.RowBg0 = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs#L16) | Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableBgTarget.RowBg1 = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs#L20) | Set row background color 1 (generally used for selection marking) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableBgTarget.CellBg = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs#L24) | Set cell background color (top-most color) | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTableColumnFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L8) | Defines the supported table column flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.Disabled = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L17) | Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.DefaultHide = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L21) | Default as a hidden/disabled column. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.DefaultSort = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L25) | Default as a sorting column. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.WidthStretch = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L29) | Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.WidthFixed = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L33) | Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoResize = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L37) | Disable manual resizing. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoReorder = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L41) | Disable manual reordering this column, this will also prevent other columns from crossing over this column. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoHide = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L45) | Disable ability to hide/disable this column. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoClip = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L49) | Disable clipping for this column (all NoClip columns will render in a same draw command). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoSort = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L53) | Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoSortAscending = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L57) | Disable ability to sort in the ascending direction. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoSortDescending = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L61) | Disable ability to sort in the descending direction. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoHeaderLabel = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L65) | TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoHeaderWidth = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L69) | Disable header text width contribution to automatic column width. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.PreferSortAscending = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L73) | Make the initial sort direction Ascending when first sorting on this column (default). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.PreferSortDescending = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L77) | Make the initial sort direction Descending when first sorting on this column. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IndentEnable = 65536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L81) | Use current Indent value when entering cell (default for column 0). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IndentDisable = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L85) | Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.AngledHeader = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L89) | TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IsEnabled = 16777216`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L93) | Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IsVisible = 33554432`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L97) | Status: is visible == is enabled AND not clipped by scrolling. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IsSorted = 67108864`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L101) | Status: is currently part of the sort specs | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IsHovered = 134217728`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L105) | Status: is hovered by mouse | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.WidthMask = Inno.Editor.ImGui.ImGuiTableColumnFlags.WidthStretch \| Inno.Editor.ImGui.ImGuiTableColumnFlags.WidthFixed`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L109) | Selects the width mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.IndentMask = Inno.Editor.ImGui.ImGuiTableColumnFlags.IndentEnable \| Inno.Editor.ImGui.ImGuiTableColumnFlags.IndentDisable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L113) | Selects the indent mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.StatusMask = Inno.Editor.ImGui.ImGuiTableColumnFlags.IsEnabled \| Inno.Editor.ImGui.ImGuiTableColumnFlags.IsVisible \| Inno.Editor.ImGui.ImGuiTableColumnFlags.IsSorted \| Inno.Editor.ImGui.ImGuiTableColumnFlags.IsHovered`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L117) | Selects the status mask presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableColumnFlags.NoDirectResize = 1073741824`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs#L121) | Selects the no direct resize presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTableFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L8) | Defines the supported table flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.Resizable = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L17) | Enable resizing columns. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.Reorderable = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L21) | Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.Hideable = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L25) | Enable hiding/disabling columns in context menu. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.Sortable = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L29) | Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoSavedSettings = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L33) | Disable persisting columns order, width, visibility and sort settings in the .ini file. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.ContextMenuInBody = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L37) | Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.RowBg = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L41) | Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerH = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L45) | Draw horizontal borders between rows. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterH = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L49) | Draw horizontal borders at the top and bottom. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerV = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L53) | Draw vertical borders between columns. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterV = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L57) | Draw vertical borders on the left and right sides. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersH = Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerH \| Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterH`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L61) | Draw horizontal borders. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersV = Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerV \| Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterV`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L65) | Draw vertical borders. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersInner = Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerH \| Inno.Editor.ImGui.ImGuiTableFlags.BordersInnerV`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L69) | Draw inner borders. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.BordersOuter = Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterH \| Inno.Editor.ImGui.ImGuiTableFlags.BordersOuterV`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L73) | Draw outer borders. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.Borders = Inno.Editor.ImGui.ImGuiTableFlags.BordersH \| Inno.Editor.ImGui.ImGuiTableFlags.BordersV`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L77) | Draw all borders. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoBordersInBody = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L81) | Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoBordersInBodyUntilResize = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L85) | Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SizingFixedFit = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L89) | Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SizingFixedSame = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L93) | Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SizingStretchProp = Inno.Editor.ImGui.ImGuiTableFlags.SizingFixedFit \| Inno.Editor.ImGui.ImGuiTableFlags.SizingFixedSame`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L97) | Columns default to _WidthStretch with default weights proportional to each columns contents widths. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SizingStretchSame = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L101) | Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoHostExtendX = 65536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L105) | Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoHostExtendY = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L109) | Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoKeepColumnsVisible = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L113) | Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.PreciseWidths = 524288`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L117) | Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoClip = 1048576`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L121) | Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.PadOuterX = 2097152`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L125) | Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoPadOuterX = 4194304`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L129) | Default if BordersOuterV is off. Disable outermost padding. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.NoPadInnerX = 8388608`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L133) | Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.ScrollX = 16777216`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L137) | Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.ScrollY = 33554432`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L141) | Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SortMulti = 67108864`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L145) | Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SortTristate = 134217728`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L149) | Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.HighlightHoveredColumn = 268435456`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L153) | Highlight column headers when hovered (may evolve into a fuller highlight) | — | — | +| [`Inno.Editor.ImGui.ImGuiTableFlags.SizingMask = Inno.Editor.ImGui.ImGuiTableFlags.SizingStretchProp \| Inno.Editor.ImGui.ImGuiTableFlags.SizingStretchSame`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs#L157) | Selects the sizing mask presentation behavior. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTableRowFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs#L8) | Defines the supported table row flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableRowFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTableRowFlags.Headers = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs#L17) | Identify header row (set default background color + width of its contents accounted differently for auto column width) | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiTreeNodeFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L8) | Defines the supported tree node flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.Selected = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L17) | Draw as selected | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.Framed = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L21) | Draw frame with background (e.g. for CollapsingHeader) | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.AllowOverlap = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L25) | Hit testing to allow subsequent widgets to overlap this one | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.NoTreePushOnOpen = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L29) | Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.NoAutoOpenOnLog = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L33) | Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.DefaultOpen = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L37) | Default node to be open | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.OpenOnDoubleClick = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L41) | Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.OpenOnArrow = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L45) | Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.Leaf = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L49) | No collapsing, no arrow (use as a convenience for leaf nodes). | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.Bullet = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L53) | Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.FramePadding = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L57) | Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.SpanAvailWidth = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L61) | Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.SpanFullWidth = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L65) | Extend hit box to the left-most and right-most edges (cover the indent area). | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.SpanLabelWidth = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L69) | Narrow hit box + narrow hovering highlight, will only cover the label text. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.SpanAllColumns = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L73) | Frame will span all columns of its container table (label will still fit in current column) | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.LabelSpanAllColumns = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L77) | Label will span all columns of its container table | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.NavLeftJumpsToParent = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L81) | Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.CollapsingHeader = Inno.Editor.ImGui.ImGuiTreeNodeFlags.Framed \| Inno.Editor.ImGui.ImGuiTreeNodeFlags.NoTreePushOnOpen \| Inno.Editor.ImGui.ImGuiTreeNodeFlags.NoAutoOpenOnLog`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L85) | Selects the collapsing header presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.DrawLinesNone = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L89) | No lines drawn | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.DrawLinesFull = 524288`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L93) | Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees). | — | — | +| [`Inno.Editor.ImGui.ImGuiTreeNodeFlags.DrawLinesToNodes = 1048576`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs#L97) | Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees). | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiWindowFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L8) | Defines the supported window flags choices at the Editor presentation boundary. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoTitleBar = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L17) | Disable title-bar | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoResize = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L21) | Disable user resizing with the lower-right grip | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoMove = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L25) | Disable user moving the window | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoScrollbar = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L29) | Disable scrollbars (window can still scroll with mouse or programmatically) | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoScrollWithMouse = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L33) | Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoCollapse = 32`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L37) | Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.AlwaysAutoResize = 64`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L41) | Resize every window to its content every frame | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoBackground = 128`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L45) | Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoSavedSettings = 256`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L49) | Never load/save settings in .ini file | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoMouseInputs = 512`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L53) | Disable catching mouse, hovering test with pass through. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.MenuBar = 1024`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L57) | Has a menu-bar | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.HorizontalScrollbar = 2048`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L61) | Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoFocusOnAppearing = 4096`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L65) | Disable taking focus when transitioning from hidden to visible state | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoBringToFrontOnFocus = 8192`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L69) | Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.AlwaysVerticalScrollbar = 16384`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L73) | Always show vertical scrollbar (even if ContentSize.y < Size.y) | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.AlwaysHorizontalScrollbar = 32768`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L77) | Always show horizontal scrollbar (even if ContentSize.x < Size.x) | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoNavInputs = 65536`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L81) | No keyboard/gamepad navigation within the window | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoNavFocus = 131072`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L85) | No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab) | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.UnsavedDocument = 262144`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L89) | Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoDocking = 524288`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L93) | Disable docking of this window | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoNav = Inno.Editor.ImGui.ImGuiWindowFlags.NoNavInputs \| Inno.Editor.ImGui.ImGuiWindowFlags.NoNavFocus`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L97) | Selects the no nav presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoDecoration = Inno.Editor.ImGui.ImGuiWindowFlags.NoTitleBar \| Inno.Editor.ImGui.ImGuiWindowFlags.NoResize \| Inno.Editor.ImGui.ImGuiWindowFlags.NoScrollbar \| Inno.Editor.ImGui.ImGuiWindowFlags.NoCollapse`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L101) | Selects the no decoration presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.NoInputs = Inno.Editor.ImGui.ImGuiWindowFlags.NoMouseInputs \| Inno.Editor.ImGui.ImGuiWindowFlags.NoNav`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L105) | Selects the no inputs presentation behavior. | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.DockNodeHost = 8388608`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L109) | Don't use! For internal use by Begin()/NewFrame() | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.ChildWindow = 16777216`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L113) | Don't use! For internal use by BeginChild() | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.Tooltip = 33554432`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L117) | Don't use! For internal use by BeginTooltip() | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.Popup = 67108864`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L121) | Don't use! For internal use by BeginPopup() | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.Modal = 134217728`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L125) | Don't use! For internal use by BeginPopupModal() | — | — | +| [`Inno.Editor.ImGui.ImGuiWindowFlags.ChildMenu = 268435456`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L129) | Don't use! For internal use by BeginMenu() | — | — | +| [`public readonly struct Inno.Editor.ImGui.EditorDrawList`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L11) | A pointer-free drawing borrow valid only in its original window, UI session and frame. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Line(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L41) | Adds a straight screen-space stroke.
`start`: The first endpoint.
`end`: The final endpoint.
`color`: Packed RGBA color.
`thickness`: Stroke width in UI pixels. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.FilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, bool topCornersOnly = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L65) | Adds a filled rectangular surface.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`topCornersOnly`: Whether only the two top corners should be rounded. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Rectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L90) | Adds a rectangular outline.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`thickness`: Stroke width in UI pixels. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.FilledCircle(System.Numerics.Vector2 center, float radius, uint color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L109) | Adds a filled circle with backend-selected tessellation.
`center`: The screen-space center.
`radius`: The nonnegative radius.
`color`: Packed RGBA color. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Text(System.Numerics.Vector2 position, uint color, string text, float scale = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L133) | Adds literal text using the current font and an explicit scale.
`position`: Screen-space text origin.
`color`: Packed RGBA color.
`text`: Literal managed text; encoded only for this synchronous call.
`scale`: Positive scale relative to the current font size. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.PushClip(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, bool intersect = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L159) | Restricts subsequent drawing to one screen rectangle.
`minimum`: The clip minimum.
`maximum`: The clip maximum.
`intersect`: Whether to intersect the enclosing clip. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.PopClip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L171) | Restores the enclosing clip after a matching PushClip. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.SplitChannels(int count)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L178) | Begins ordered draw channels within this window.
`count`: The positive number of layers. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.SetChannel(int index)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L190) | Selects a channel from the current split.
`index`: The zero-based channel. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.MergeChannels()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L194) | Merges the current split back into ordered draw commands. | — | — | +| [`public static class Inno.Editor.ImGui.EditorDragDropRenderer`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L12) | Bridges managed editor drag sessions to the native ImGui payload API. | — | — | +| [`public static bool Inno.Editor.ImGui.EditorDragDropRenderer.Source(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDragData data, System.Action? drawPreview = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L30) | Publishes managed drag data for the most recently submitted ImGui item.
`interaction`: The interaction area and target that produced the drag source.
`data`: The managed drag data published by the source.
`drawPreview`: An optional callback that draws the native drag preview. | while the item is an active drag source. | — | +| [`public static Inno.Editor.ImGui.EditorDropWidgetResult Inno.Editor.ImGui.EditorDragDropRenderer.Target(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L51) | Evaluates and, on delivery, accepts a managed drag on the most recently submitted ImGui item.
`interaction`: The interaction area and managed drop target.
`placement`: The requested position relative to the target. | The native preview state together with the managed compatibility and delivery results. | — | +| [`public readonly record struct Inno.Editor.ImGui.EditorDropWidgetResult : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Reports the preview and delivery state of an ImGui editor drop target. | — | — | +| [`public Inno.Editor.ImGui.EditorDropWidgetResult.EditorDropWidgetResult(bool isPreviewing, Inno.Editor.Interactions.EditorDropStatus status, Inno.Editor.Interactions.EditorDropResult result)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L88) | Creates a combined native-preview and managed-drop result.
`isPreviewing`: Whether a compatible native payload is currently hovering the target.
`status`: The compatibility and visual state returned by the managed drop router.
`result`: The result produced when the payload was delivered. | — | — | +| [`public bool Inno.Editor.ImGui.EditorDropWidgetResult.isPreviewing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L101) | Gets whether a compatible native payload is hovering the target. | — | — | +| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.ImGui.EditorDropWidgetResult.status { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L105) | Gets the managed drop compatibility status. | — | — | +| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.ImGui.EditorDropWidgetResult.result { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L109) | Gets the delivered drop result. | — | — | +| [`public static Inno.Editor.ImGui.EditorDropWidgetResult Inno.Editor.ImGui.EditorDropWidgetResult.none { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L113) | Gets an inactive drop target result. | — | — | +| [`public override string Inno.Editor.ImGui.EditorDropWidgetResult.ToString()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.ImGui.EditorDropWidgetResult.operator !=(Inno.Editor.ImGui.EditorDropWidgetResult left, Inno.Editor.ImGui.EditorDropWidgetResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.ImGui.EditorDropWidgetResult.operator ==(Inno.Editor.ImGui.EditorDropWidgetResult left, Inno.Editor.ImGui.EditorDropWidgetResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.ImGui.EditorDropWidgetResult.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.ImGui.EditorDropWidgetResult.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.EditorDropWidgetResult.Equals(Inno.Editor.ImGui.EditorDropWidgetResult other)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L74) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.ImGui.EditorGameInputCapture`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L13) | Routes platform input to the Play session only while the visible Game View owns focus. | — | — | +| [`public Inno.Editor.ImGui.EditorGameInputCapture.EditorGameInputCapture(System.Func resolveWindow)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L35) | Creates a capture policy using a presentation viewport to platform window resolver.
`resolveWindow`: Returns a live window ID for a viewport, or null when absent. | — | `System.ArgumentNullException`: Thrown when the resolver is null. | +| [`public void Inno.Editor.ImGui.EditorGameInputCapture.BeginFrame()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L43) | Starts a presentation pass in which the visible Game View must report its current bounds. | — | — | +| [`public bool Inno.Editor.ImGui.EditorGameInputCapture.Report(uint viewportId, System.Numerics.Vector2 origin, System.Numerics.Vector2 size, bool focused, bool hovered)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L66) | Reports the Game View image in coordinates local to its platform window.
`viewportId`: The presentation viewport containing the Game View.
`origin`: The image's upper-left corner relative to that window.
`size`: The visible image size in logical pixels.
`focused`: Whether the Game View is selected and its platform window has focus.
`hovered`: Whether the image is inside the frontmost hovered ImGui window. | when the Game View and its platform window both own focus. | — | +| [`public uint Inno.Editor.ImGui.EditorGameInputCapture.CompleteFrame()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L89) | Ends a presentation pass and identifies input that must be released after focus loss. | The former platform window ID to reset, or zero if no reset is needed. | — | +| [`public Inno.Core.Events.Event? Inno.Editor.ImGui.EditorGameInputCapture.Route(Inno.Core.Events.Event evnt)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorGameInputCapture.cs#L126) | Filters an engine event through the current Game View focus and pointer bounds.
`evnt`: The original backend-neutral platform event. | A routed event or a synthetic focus reset, or null when the Game View must not receive input. | `System.ArgumentNullException`: Thrown when the event is null. | +| [`public sealed class Inno.Editor.ImGui.EditorMenuRenderer`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L15) | Renders immutable editor menu models through ImGui. | — | — | +| [`public bool Inno.Editor.ImGui.EditorMenuRenderer.ContextMenu(string id, Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L43) | Draws a resolved right-click menu for the most recently submitted ImGui item.
`id`: The stable popup identifier in the current ImGui ID scope.
`interaction`: The interaction area and optional operation target. | while the context popup is open and its items were drawn. | — | +| [`public bool Inno.Editor.ImGui.EditorMenuRenderer.ContextMenu(string id, Inno.Editor.Interactions.EditorInteraction scopeInteraction, Inno.Editor.Interactions.EditorInteraction itemInteraction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L88) | Draws one context popup composed from an item interaction and its containing-scope interaction.
`id`: Stable popup identifier in the current ImGui ID scope.
`scopeInteraction`: Container interaction whose commands are shown first, such as creation commands for a directory.
`itemInteraction`: Selected-item interaction whose commands are shown after the container commands. | while the context popup is open and at least one command group was drawn. | — | +| [`public bool Inno.Editor.ImGui.EditorMenuRenderer.WindowContextMenu(string id, Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L132) | Draws a resolved right-click menu when the current ImGui window's unoccupied background is clicked.
`id`: The stable popup identifier in the current ImGui ID scope.
`interaction`: The interaction area and directory target for the background operation. | while the background context popup is open and its items were drawn. | — | +| [`public void Inno.Editor.ImGui.EditorMenuRenderer.MainMenu(Inno.Editor.Interactions.EditorInteraction interaction)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L215) | Draws the complete editor main menu bar for the supplied menu context.
`interaction`: The main-menu interaction area. | — | — | +| [`public void Inno.Editor.ImGui.EditorMenuRenderer.DrawItems(Inno.Editor.Interactions.EditorInteraction interaction, System.Collections.Generic.IReadOnlyList items)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L309) | Recursively draws resolved menu nodes into the currently open popup or menu.
`interaction`: The interaction used to resolve shortcuts and enqueue selected actions.
`items`: The immutable menu nodes to draw in display order. | — | — | +| [`public bool Inno.Editor.ImGui.EditorMenuRenderer.DrawSearchItems(Inno.Editor.Interactions.EditorInteraction interaction, System.Collections.Generic.IReadOnlyList items, string search)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L364) | Draws matching leaf commands as a flat searchable list.
`interaction`: The interaction used to enqueue selected actions.
`items`: The immutable menu tree whose leaves should be searched.
`search`: The case-insensitive text matched against each slash-delimited leaf path. | when a command was selected. | — | +| [`public readonly struct Inno.Editor.ImGui.EditorUiInput`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L8) | Copies frame input facts used by presentation without exposing native input storage. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.alt { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L13) | Gets whether Alt is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.control { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L17) | Gets whether Control is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.shift { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L21) | Gets whether Shift is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.super { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L25) | Gets whether the platform super key is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.wantsText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L29) | Gets whether an active control owns text input. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiInput.mouseDelta { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L33) | Gets the current pointer movement in UI units. | — | — | +| [`public float Inno.Editor.ImGui.EditorUiInput.wheel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L37) | Gets the vertical scroll movement. | — | — | +| [`public float Inno.Editor.ImGui.EditorUiInput.deltaTime { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L41) | Gets the frame duration in seconds. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiInput.framebufferScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L45) | Gets the drawable pixel scale relative to logical UI coordinates. | — | — | +| [`public sealed class Inno.Editor.ImGui.EditorUiSession : System.IDisposable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L12) | Owns editor controls and transient presentation state for one host, independently from other ImGui contexts. | — | — | +| [`public Inno.Editor.ImGui.EditorUiSession.EditorUiSession()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L26) | Creates independent controls and style state; the presentation backend retains its native context ownership. | — | — | +| [`public static Inno.Editor.ImGui.EditorUiSession Inno.Editor.ImGui.EditorUiSession.current { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L38) | Gets the current synchronous drawing scope; this accessor never creates or owns a session. | — | `System.InvalidOperationException`: No UI drawing scope is active on the calling thread. | +| [`public Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget Inno.Editor.ImGui.EditorUiSession.widgets { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L42) | Gets the controls whose transient state belongs exclusively to this session. | — | — | +| [`public Inno.Editor.ImGui.EditorMenuRenderer Inno.Editor.ImGui.EditorUiSession.menus { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L48) | Gets the menu presenter whose search state belongs exclusively to this session. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiSession.isDisposed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L52) | Gets whether this session has successfully retired its presentation state. | — | — | +| [`public System.IDisposable Inno.Editor.ImGui.EditorUiSession.Enter()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L67) | Activates this session for a synchronous callback while preserving the enclosing scope. | An owner-thread scope that must be released in reverse activation order. | `System.ObjectDisposedException`: The session has retired.
`System.InvalidOperationException`: The caller is not the owner thread. | +| [`public void Inno.Editor.ImGui.EditorUiSession.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L83) | Releases presentation state after all drawing scopes and domain edit gestures have ended. | — | `System.InvalidOperationException`: A drawing scope is active or the caller is not the owner thread. | +| [`public sealed class Inno.Editor.ImGui.ImGuiEditorRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L21) | Presents the backend-independent editor interaction runtime through ImGui. | — | — | +| [`public Inno.Editor.ImGui.ImGuiEditorRuntime.ImGuiEditorRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IEnumerable hostServices)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L52) | Creates an ImGui editor runtime with stable host-owned extension services.
`context`: The shared editor context that owns project settings and frame state.
`types`: The host-owned type catalog that coordinates extension generations.
`logs`: The host-owned logging router used by editor infrastructure.
`hostServices`: Services available to discovered extension constructors. | — | `System.ArgumentNullException`: Thrown when , , or is . | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.ImGui.ImGuiEditorRuntime.interactions { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L75) | Gets the active presentation-independent interaction entry point. | — | — | +| [`public int Inno.Editor.ImGui.ImGuiEditorRuntime.panelCount { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L79) | Gets the number of active dockable panels. | — | — | +| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Start()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L84) | Starts value processing after validating the current state. | — | — | +| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L96) | Recomputes owned state from the current validated inputs.
`frame`: The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.SaveState()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L108) | Captures all stateful active modules and panels and flushes their project state to disk. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.PrepareShutdown()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L126) | Freezes automatic extension-state persistence and writes the final project state before editor modules begin shutting down. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.Draw()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L137) | Draws the complete editor frame through ImGui. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L264) | Dispatches contextual shortcuts, leaving editing keys with active text widgets while permitting explicit save.
`keyEvent`: The keyboard event received from the application event stream. | — | — | +| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L285) | Releases the resources owned by this implementation. | — | `Inno.Core.Execution.RetirementPendingException`: Editor extensions still own live work. The interaction runtime remains retained for safe shutdown. | +| [`public static class Inno.Editor.ImGui.EditorPalette`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L11) | Defines the complete editor color palette in one theme surface. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityNone = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L16) | Gets zero opacity for invisible surfaces. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityTrace = 0.005`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L21) | Gets trace opacity for barely perceptible alternating surfaces. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityFaint = 0.04`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L26) | Gets faint opacity for quiet guides and separators. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacitySubtle = 0.12`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L31) | Gets subtle opacity for selection washes and low-emphasis overlays. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacitySoft = 0.2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L36) | Gets soft opacity for tinted container surfaces. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityMuted = 0.28`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L41) | Gets muted opacity for shadows and subdued outlines. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityMedium = 0.45`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L46) | Gets medium opacity for secondary content and overlays. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityStrong = 0.55`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L51) | Gets strong opacity for accents that remain translucent. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityProminent = 0.7`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L56) | Gets prominent opacity for navigation emphasis. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityEmphasized = 0.82`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L61) | Gets emphasized opacity for borders and interactive grips. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityNearOpaque = 0.92`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L66) | Gets near-opaque opacity for active overlays. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityPopup = 0.985`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L71) | Gets popup opacity, retaining only enough transparency for depth. | — | — | +| [`public const float Inno.Editor.ImGui.EditorPalette.opacityOpaque = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L76) | Gets full opacity. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.transparent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L81) | Gets fully transparent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.text { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L86) | Gets the primary text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.textDisabled { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L91) | Gets disabled text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.error { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L96) | Gets the standard error color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.warning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L101) | Gets the standard warning color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L106) | Gets the neutral Shader node heading surface. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderInputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L111) | Gets the orange heading surface shared by Shader input nodes. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderOutputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L116) | Gets the purple heading surface shared by Shader output nodes. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.windowBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L121) | Gets the editor window background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.popupBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L126) | Gets the editor popup background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L131) | Gets text color used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L136) | Gets the background used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItem { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L141) | Gets the resting background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L146) | Gets the hovered background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L151) | Gets the active background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuSeparator { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L156) | Gets the separator color used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.border { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L161) | Gets the standard border color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.borderShadow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L166) | Gets the standard border shadow color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frame { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L171) | Gets the standard frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L176) | Gets the hovered frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L181) | Gets the active frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.title { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L186) | Gets the inactive title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L191) | Gets the active title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleCollapsed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L196) | Gets the collapsed title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L201) | Gets the standard scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L206) | Gets the hovered scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L211) | Gets the active scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L216) | Gets the standard accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L221) | Gets the hovered accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L226) | Gets the active accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L231) | Gets the standard tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L236) | Gets the hovered tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L241) | Gets the selected tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L246) | Gets the dimmed tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmedSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L251) | Gets the selected dimmed tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelectedOverline { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L256) | Gets the selected tab overline color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L261) | Gets the table header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderStrong { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L266) | Gets the strong table border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderLight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L271) | Gets the light table border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L276) | Gets the alternate table row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.dragDropTarget { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L281) | Gets the standard drag target color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationHighlight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L286) | Gets navigation highlight color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L291) | Gets navigation dim background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.modalDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L296) | Gets modal dim background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L301) | Gets inspector card header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L306) | Gets the persistent Inspector target header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeaderBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L311) | Gets the persistent Inspector target header border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTagLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L316) | Gets the subdued background of Tag labels in Inspector target headers. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorLayerLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L321) | Gets the subdued background of Layer labels in Inspector target headers. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBody { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L326) | Gets inspector card body background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBodyBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L331) | Gets inspector card body border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorSectionBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L336) | Gets the outline color of framed Inspector sections. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeNumeric { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L341) | Gets the accent used by numeric Shader type badges. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeInteger { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L346) | Gets the accent used by integer Shader type badges. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeBoolean { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L351) | Gets the accent used by Boolean Shader type badges. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeResource { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L356) | Gets the accent used by Shader resource type badges. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeOther { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L361) | Gets the accent used by unclassified Shader type badges. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisabledText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L366) | Gets disabled inspector card text. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisclosureHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L371) | Gets inspector disclosure hover background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.compactControlHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L376) | Gets compact control hover color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L381) | Gets the deepest collection background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L386) | Gets the primary collection row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L391) | Gets the alternate collection row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L396) | Gets the translucent primary background of a complete Settings field. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsFieldAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L401) | Gets the translucent alternate background of a complete Settings field. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L406) | Gets the asset browser field background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L411) | Gets the asset browser border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorderSoft { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L416) | Gets the soft asset browser border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L421) | Gets the asset browser text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetTextMuted { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L426) | Gets the muted asset browser text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBreadcrumbText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L431) | Gets the subdued text color used by asset browser breadcrumb paths. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetAccent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L436) | Gets the opaque asset browser accent. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetHovered(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L447) | Gets a hover treatment derived from a base theme color.
`color`: The color consumed by get hovered; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard hover amount. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetActive(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L458) | Gets an active treatment derived from a base theme color.
`color`: The color consumed by get active; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard active amount. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchySceneRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L463) | Gets scene row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchyInactiveText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L468) | Gets inactive hierarchy text. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.treeGuide { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L473) | Gets hierarchy tree guide color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisX { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L478) | Gets X axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisY { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L483) | Gets Y axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisZ { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L488) | Gets Z axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisW { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L493) | Gets W axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logDebug { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L498) | Gets debug log color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logInfo { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L503) | Gets informational log color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logWarning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L508) | Gets warning log color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logError { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L513) | Gets error log color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logFatal { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L518) | Gets fatal log color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedCard { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L523) | Gets collapsed log card background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L528) | Gets collapsed log card border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L533) | Gets the base expanded log card background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBorderBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L538) | Gets the base expanded log card border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggle { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L543) | Gets log header button background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L548) | Gets hovered log header button background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L553) | Gets active log header button background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedCard(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L564) | Gets the expanded log card background derived from a severity color.
`severityColor`: The palette color representing the log severity. | The standard expanded-card base blended with the severity color. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedBorder(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L575) | Gets the expanded log card border derived from a severity color.
`severityColor`: The palette color representing the log severity. | The standard expanded-border base blended with the severity color. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogSeparator(System.Numerics.Vector4 cardColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L586) | Gets the separator color used inside an expanded log card.
`cardColor`: The resolved background color of the expanded card. | The card color blended toward the palette text color. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.Lerp(System.Numerics.Vector4 from, System.Numerics.Vector4 to, float amount)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L603) | Linearly interpolates two palette colors using a clamped amount.
`from`: The color returned when is zero.
`to`: The color returned when is one.
`amount`: The interpolation amount, clamped to the inclusive zero-to-one range. | The component-wise interpolated color. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.WithOpacity(System.Numerics.Vector4 color, float opacity)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L686) | Returns a theme color with a caller-selected opacity, normally one of the shared opacity levels.
`color`: Base RGB color.
`opacity`: Replacement alpha in the inclusive zero-to-one range. | The validated vector4 that represents the completed operation. | — | +| [`public sealed class Inno.Editor.ImGui.EditorStyleMetrics`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | Defines named editor layout metrics used by widgets and feature panels. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MIN_ZOOM = 0.75`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L14) | Defines the smallest supported editor UI zoom. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MAX_ZOOM = 1.5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L19) | Defines the largest supported editor UI zoom. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_ZOOM_STEP = 0.1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L24) | Defines one keyboard or menu zoom increment. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L32) | Gets the current editor UI zoom multiplier. | — | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.isCompact { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L37) | Gets whether compact editor density is active. | — | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetCompactMode(bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L48) | Switches between comfortable and compact editor density.
`value`: for compact density. | when the density changed. | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetZoom(float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L68) | Sets the editor UI zoom after clamping it to the supported range.
`value`: The requested UI zoom multiplier. | when the effective zoom changed. | `System.ArgumentOutOfRangeException`: Thrown when is not finite. | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomIn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L85) | Increases editor UI zoom by one bounded increment. | when the zoom changed. | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomOut()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L93) | Decreases editor UI zoom by one bounded increment. | when the zoom changed. | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ResetZoom()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L101) | Restores the baseline editor UI zoom. | when the zoom changed. | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.fontScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L106) | Gets global content scale. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disabledAlpha { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L111) | Gets disabled content opacity. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L116) | Gets standard window padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.windowRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L121) | Gets standard window rounding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.borderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L126) | Gets standard border thickness. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L131) | Gets minimum window size. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.framePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L136) | Gets standard frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.panelTabFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L141) | Gets the frame padding used while docked panel tabs are laid out. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L146) | Gets the uniform content padding of editor context menus. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L151) | Gets the uniform item padding of editor context menus. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuSearchFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L157) | Gets the compact padding used by the search field at the top of editor context menus. Its vertical padding matches the half-spacing that menu entries add above and below a line. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L162) | Gets the uniform spacing between editor context-menu items. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L167) | Gets editor context-menu corner rounding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuBorderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L172) | Gets editor context-menu border thickness. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L177) | Gets compact frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inlineRenameFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L182) | Gets the compact inner padding of inline rename fields. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.toolbarFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L187) | Gets toolbar frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.breadcrumbFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L192) | Gets breadcrumb frame padding. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.frameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L197) | Gets standard frame rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.grabMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L272) | Gets minimum grab size. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyBlankMinimumHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L477) | Gets the minimum height of the hierarchy blank drop area. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L202) | Gets standard item spacing. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L207) | Gets compact vertical item spacing. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.hierarchyItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L212) | Gets hierarchy item spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L467) | Gets the minimum inline hierarchy rename width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameTrailingGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L472) | Gets the hierarchy rename trailing-control gap. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.iconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L412) | Gets spacing between a leading icon and its text. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemInnerSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L217) | Gets standard inner item spacing. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L222) | Gets standard table cell padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.settingsFieldPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L227) | Gets the inner padding applied to one complete Settings field. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L232) | Gets asset browser window padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L237) | Gets asset browser cell padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L242) | Gets asset browser item spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetFrameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L247) | Gets asset browser frame rounding. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.indentSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L252) | Gets tree indentation. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inlineRenameFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L182) | Gets the compact inner padding of inline rename fields. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorAddButtonTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L462) | Gets top padding above inspector add buttons. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardBodyPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L342) | Gets inspector card body padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L347) | Gets inspector card header padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.columnMinimumSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L257) | Gets minimum column spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L262) | Gets scrollbar width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L267) | Gets the inset that keeps an overlay scrollbar grab visually lightweight. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.grabMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L272) | Gets minimum grab size. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetPaneMinimumVisibleWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L277) | Gets the minimum visible width retained for either asset browser pane while its splitter is dragged. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetSplitterMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L282) | Gets the minimum draggable asset browser splitter width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L287) | Gets asset breadcrumb bar height. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumCellSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L292) | Gets minimum asset grid cell size. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L297) | Gets asset grid cell padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridDefaultScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L302) | Gets default asset grid scale. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L307) | Gets minimum asset grid scale. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMaximumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L312) | Gets maximum asset grid scale. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L317) | Gets spacing between asset list rows. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L322) | Gets spacing between inspector cards. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorCollapsedSectionCapLength { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L367) | Gets the length of each centered end cap on a collapsed Inspector section. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderControlSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L422) | Gets spacing between leading controls inside card headers. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L457) | Gets spacing between distinct control groups in an Inspector target header row. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionLegendGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L362) | Gets the gap between an Inspector section legend and its interrupted top border. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L357) | Gets the inner padding of a framed Inspector section. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L382) | Gets the subtle corner rounding used by framed content sections inside editor containers. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L377) | Gets the vertical distance after a framed Inspector section. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L327) | Gets the inner padding of the persistent Inspector target header. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetHeaderRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L332) | Gets spacing between the two rows of the persistent Inspector target header. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetIconScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L337) | Gets the font-size multiplier used by the large Inspector target icon. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.interactionOverlayThickness { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L582) | Gets the standard foreground interaction-overlay thickness. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.isCompact { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L37) | Gets whether compact editor density is active. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemInnerSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L217) | Gets standard inner item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L202) | Gets standard item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.labelChipPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L427) | Gets the inner padding of compact colored label chips. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.labelChipRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L432) | Gets the corner rounding of compact colored label chips. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardBodyPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L342) | Gets inspector card body padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L347) | Gets inspector card header padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.sectionHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L352) | Gets the shared padding around separator-style section titles. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L357) | Gets the inner padding of a framed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionLegendGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L362) | Gets the gap between an Inspector section legend and its interrupted top border. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorCollapsedSectionCapLength { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L367) | Gets the length of each centered end cap on a collapsed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyMetadataSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L372) | Gets the spacing around the separator between inline metadata and its value. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L377) | Gets the vertical distance after a framed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L382) | Gets the subtle corner rounding used by framed content sections inside editor containers. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disclosureButtonInset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L387) | Gets inspector disclosure inset. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyLabelRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L392) | Gets property label width ratio. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.logAutoScrollTolerance { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L397) | Gets the distance from the bottom treated as auto-scroll. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.logDisclosurePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L402) | Gets log disclosure padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuBorderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L172) | Gets editor context-menu border thickness. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L151) | Gets the uniform item padding of editor context menus. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L162) | Gets the uniform spacing between editor context-menu items. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L167) | Gets editor context-menu corner rounding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuSearchFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L157) | Gets the compact padding used by the search field at the top of editor context menus. Its vertical padding matches the half-spacing that menu entries add above and below a line. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L146) | Gets the uniform content padding of editor context menus. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeInSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L597) | Gets the modal fade-in duration in seconds. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeOutSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L607) | Gets the modal fade-out duration in seconds. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalMinimumVisibleSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L602) | Gets the minimum modal visibility duration in seconds. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.modalWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L592) | Gets the standard centered modal width. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.panelTabFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L141) | Gets the frame padding used while docked panel tabs are laid out. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyLabelRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L392) | Gets property label width ratio. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyMetadataSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L372) | Gets the spacing around the separator between inline metadata and its value. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scriptCompilationWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L587) | Gets script compilation modal width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L267) | Gets the inset that keeps an overlay scrollbar grab visually lightweight. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L262) | Gets scrollbar width. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.searchPopupWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L407) | Gets the default width of searchable editor popups. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.sectionHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L352) | Gets the shared padding around separator-style section titles. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.settingsFieldPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L227) | Gets the inner padding applied to one complete Settings field. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.statisticRowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L452) | Gets the vertical padding inside one statistics row. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.textDecorationOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L627) | Gets the vertical offset of text decorations from the baseline. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.toolbarFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L187) | Gets toolbar frame padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeFolderConnectorPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L622) | Gets additional connector padding for expandable tree nodes. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLeftOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L612) | Gets tree guide left offset. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLineOverlap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L617) | Gets the overlap used to join adjacent tree guide segments. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.iconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L412) | Gets spacing between a leading icon and its text. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.compactCheckboxSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L417) | Gets the compact checkbox size used by card headers. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderControlSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L422) | Gets spacing between leading controls inside card headers. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.labelChipPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L427) | Gets the inner padding of compact colored label chips. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.labelChipRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L432) | Gets the corner rounding of compact colored label chips. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.typeBadgePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L437) | Gets the compact inner padding of outlined type badges. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L442) | Gets the corner rounding of outlined type badges. | — | — | | [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L447) | Gets the horizontal gap between a property name and its type badge. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L131) | Gets minimum window size. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L116) | Gets standard window padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.windowRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L121) | Gets standard window rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L32) | Gets the current editor UI zoom multiplier. | — | — | -| [`public sealed class Inno.Editor.ImGui.EditorStyleMetrics`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | Defines named editor layout metrics used by widgets and feature panels. | — | — | - -### `Inno.Editor.ImGui.ImGui` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginChild(string id, System.Numerics.Vector2 size = default(System.Numerics.Vector2), Inno.Native.ImGui.ImGuiChildFlags flags = Inno.Native.ImGui.ImGuiChildFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L304) | Begins a child region.
返回:true when child contents should be submitted.
`id`:The child identity.
`size`:The requested child size.
`flags`:Child-region behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginCombo(string label, string preview, Inno.Native.ImGui.ImGuiComboFlags flags = Inno.Native.ImGui.ImGuiComboFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L399) | Begins a combo popup.
返回:true when combo contents should be submitted.
`label`:The control label.
`preview`:The preview text.
`flags`:Combo behavior. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.BeginDisabled(bool disabled = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L590) | Begins a disabled block.
`disabled`:Whether controls in the block are disabled. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.BeginGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L600) | Begins an item group. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginPopup(string id, Inno.Native.ImGui.ImGuiWindowFlags flags = Inno.Native.ImGui.ImGuiWindowFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L569) | Begins a named popup.
返回:true when popup contents should be submitted.
`id`:The popup identity.
`flags`:Popup window behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabBar(string id, Inno.Native.ImGui.ImGuiTabBarFlags flags = Inno.Native.ImGui.ImGuiTabBarFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L485) | Begins a tab bar.
返回:true when tab contents should be submitted.
`id`:The tab bar identity.
`flags`:Tab-bar behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabItem(string label, Inno.Native.ImGui.ImGuiTabItemFlags flags = Inno.Native.ImGui.ImGuiTabItemFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L507) | Begins one tab item.
返回:true when tab contents should be submitted.
`label`:The tab label and identity.
`flags`:Tab-item behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginTabItem(string label, ref bool isOpen, Inno.Native.ImGui.ImGuiTabItemFlags flags = Inno.Native.ImGui.ImGuiTabItemFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L527) | Begins one closeable tab item.
返回:true when tab contents should be submitted.
`label`:The tab label and identity.
`isOpen`:Receives whether the tab remains open.
`flags`:Tab-item behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.BeginTable(string id, int columns, Inno.Native.ImGui.ImGuiTableFlags flags = Inno.Native.ImGui.ImGuiTableFlags.None, System.Numerics.Vector2 size = default(System.Numerics.Vector2))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L334) | Begins a table.
返回:true when table contents should be submitted.
`id`:The table identity.
`columns`:The positive column count.
`flags`:Table behavior.
`size`:The requested outer size. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.Button(string label, System.Numerics.Vector2 size = default(System.Numerics.Vector2))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L35) | Draws a standard button.
返回:true when the button is activated.
`label`:The visible label and optional ImGui identity suffix.
`size`:The requested size, or zero components for automatic sizing. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.Checkbox(string label, ref bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L63) | Draws and edits a Boolean value.
返回:true when the value changes.
`label`:The control label.
`value`:The concrete value read or transformed by this operation. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.CloseCurrentPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L582) | Closes the current popup. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.CollapsingHeader(string label, Inno.Native.ImGui.ImGuiTreeNodeFlags flags = Inno.Native.ImGui.ImGuiTreeNodeFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L445) | Draws a collapsible section header.
返回:true when the section is open.
`label`:The visible label and identity.
`flags`:Tree-node behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.ColorEdit4(string label, ref System.Numerics.Vector4 value, Inno.Native.ImGui.ImGuiColorEditFlags flags = Inno.Native.ImGui.ImGuiColorEditFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L243) | Draws and edits a four-component color.
返回:true when the value changes.
`label`:The control label.
`value`:The RGBA value to display and update.
`flags`:Color editing behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.ColorEditLinear4(string label, ref System.Numerics.Vector4 value, Inno.Native.ImGui.ImGuiColorEditFlags flags = Inno.Native.ImGui.ImGuiColorEditFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L270) | Displays a linear RGBA color through a display-sRGB picker and returns edits in linear space.
返回:True if the display color changed and value contains decoded linear RGB.
`label`:The stable identity and visible label of the color control.
`value`:Linear RGB and alpha; only changed RGB channels are decoded, while alpha is never transfer-encoded.
`flags`:Dear ImGui color-edit flags; display modes interpret sRGB values and input storage must be RGB. | — | `System.ArgumentException`:HSV input storage conflicts with the linear-RGB value contract. | -| [`public static bool Inno.Editor.ImGui.ImGui.DragFloat(string label, ref float value, float speed = 1, float minimum = 0, float maximum = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L189) | Draws a floating-point drag control.
返回:true when the value changes.
`label`:The control label.
`value`:The value to display and update.
`speed`:The drag speed.
`minimum`:The inclusive minimum, or zero with a zero maximum for no clamping.
`maximum`:The inclusive maximum, or zero with a zero minimum for no clamping. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L899) | Adds a filled rectangle to the current window draw list.
`minimum`:The screen-space minimum corner.
`maximum`:The screen-space maximum corner.
`color`:The packed ImGui color.
`rounding`:The corner rounding radius. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L849) | Adds a line to the current window draw list.
`start`:The screen-space start point.
`end`:The screen-space end point.
`color`:The packed ImGui color.
`thickness`:The line thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L875) | Adds a rectangle outline to the current window draw list.
`minimum`:The screen-space minimum corner.
`maximum`:The screen-space maximum corner.
`color`:The packed ImGui color.
`rounding`:The corner rounding radius.
`thickness`:The outline thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L919) | Adds text to the current window draw list.
`position`:The screen-space text origin.
`color`:The packed ImGui color.
`text`:The text to draw. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Dummy(System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L724) | Advances layout by an invisible size.
`size`:The size consumed by dummy; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndChild()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L314) | Ends the current child region. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndCombo()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L409) | Ends the current combo popup. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndDisabled()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L595) | Ends the current disabled block. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L605) | Ends the current item group. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L577) | Ends the current popup. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndTabBar()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L493) | Ends the current tab bar. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndTabItem()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L541) | Ends the current tab item. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndTable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L345) | Ends the current table. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetContentRegionAvailable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L740) | Gets the remaining content size in the current region.
返回:The remaining local width and height. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L748) | Gets the current local cursor position.
返回:The local cursor position. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorScreenPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L764) | Gets the current screen-space cursor position.
返回:The screen-space cursor position. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMaximum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L780) | Gets the maximum screen-space corner of the previous item.
返回:The item maximum corner. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMinimum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L772) | Gets the minimum screen-space corner of the previous item.
返回:The item minimum corner. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.InputFloat(string label, ref float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L163) | Draws and edits a floating-point value.
返回:true when the value changes.
`label`:The control label.
`value`:The concrete value read or transformed by this operation. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.InputInt(string label, ref int value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L146) | Draws and edits an integer value.
返回:true when the value changes.
`label`:The control label.
`value`:The concrete value read or transformed by this operation. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.InputText(string label, ref string value, int capacity = 1024, Inno.Native.ImGui.ImGuiInputTextFlags flags = Inno.Native.ImGui.ImGuiInputTextFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L89) | Draws and edits a bounded UTF-8 text value.
返回:true when the control reports an edit or submit event.
`label`:The control label.
`value`:The managed string to display and update.
`capacity`:The positive maximum UTF-8 buffer capacity, including the terminator.
`flags`:Text editing behavior. | — | `System.ArgumentOutOfRangeException`:Thrown when capacity is not positive. | -| [`public static bool Inno.Editor.ImGui.ImGui.InputTextWithHint(string label, string hint, ref string value, int capacity = 1024, Inno.Native.ImGui.ImGuiInputTextFlags flags = Inno.Native.ImGui.ImGuiInputTextFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L123) | Draws and edits a bounded UTF-8 text value with placeholder text.
返回:true when the control reports an edit or submit event.
`label`:The control label.
`hint`:The placeholder shown while the value is empty.
`value`:The managed string to display and update.
`capacity`:The positive maximum UTF-8 buffer capacity, including the terminator.
`flags`:Text editing behavior. | — | `System.ArgumentOutOfRangeException`:Thrown when capacity is not positive. | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemActive()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L810) | Gets whether the previous item is active.
返回:true while the item owns an active interaction. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseButtonDown(Inno.Native.ImGui.ImGuiMouseButton button = Inno.Native.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L836) | UI 帧中的按钮保持状态;必须与 item 命中/激活状态共同使用。 | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMouseDragDelta(Inno.Native.ImGui.ImGuiMouseButton button = Inno.Native.ImGui.ImGuiMouseButton.Left, float threshold = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L851) | UI 拖动开始位置至当前指针的累计位移;负阈值使用 Editor 配置。 | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemClicked(Inno.Native.ImGui.ImGuiMouseButton button = Inno.Native.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L821) | Gets whether the previous item was clicked with a mouse button.
返回:true when the item was clicked.
`button`:The mouse button to query. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemHovered(Inno.Native.ImGui.ImGuiHoveredFlags flags = Inno.Native.ImGui.ImGuiHoveredFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L802) | Gets whether the previous item is hovered.
返回:true when the query succeeds.
`flags`:Hovered query behavior. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L791) | Measures text using the current font.
返回:The measured width and height.
`text`:The text text validated by the measure text operation. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.OpenPopup(string id, Inno.Native.ImGui.ImGuiPopupFlags flags = Inno.Native.ImGui.ImGuiPopupFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L552) | Opens a named popup.
`id`:The popup identity.
`flags`:Popup opening behavior. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopId()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L626) | Pops one identity from the ImGui ID stack. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopStyleColor(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L648) | Pops color overrides.
`count`:The positive number of overrides to pop. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopStyleVar(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L684) | Pops style-variable overrides.
`count`:The positive number of overrides to pop. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushId(int id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L621) | Pushes an integer identity onto the ImGui ID stack.
`id`:The stable identity used to locate the requested value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushId(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L613) | Pushes a string identity onto the ImGui ID stack.
`id`:The stable identity used to locate the requested value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleColor(Inno.Native.ImGui.ImGuiCol color, System.Numerics.Vector4 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L637) | Pushes one color override.
`color`:The style color slot.
`value`:The linear RGBA value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Native.ImGui.ImGuiStyleVar style, System.Numerics.Vector2 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L673) | Pushes one two-component style override.
`style`:The style variable.
`value`:The vector value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Native.ImGui.ImGuiStyleVar style, float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L659) | Pushes one scalar style override.
`style`:The style variable.
`value`:The scalar value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SameLine(float offset = 0, float spacing = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L695) | Places the next item on the same line.
`offset`:The horizontal offset from the line start, or zero for automatic placement.
`spacing`:The explicit spacing, or a negative value for the current style spacing. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.Selectable(string label, bool selected = false, Inno.Native.ImGui.ImGuiSelectableFlags flags = Inno.Native.ImGui.ImGuiSelectableFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L426) | Draws one selectable item.
返回:true when the item is activated.
`label`:The visible label and identity.
`selected`:Whether the item is currently selected.
`flags`:Selectable behavior. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Separator()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L703) | Draws a horizontal separator. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SeparatorText(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L711) | Draws a labeled horizontal separator.
`label`:The label text validated by the separator text operation. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosition(System.Numerics.Vector2 position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L756) | Sets the current local cursor position.
`position`:The position consumed by set cursor position; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SetNextItemWidth(float width)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L732) | Sets the width of the next item.
`width`:The width in logical units or pixels required by this operation. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.SliderFloat(string label, ref float value, float minimum, float maximum, Inno.Native.ImGui.ImGuiSliderFlags flags = Inno.Native.ImGui.ImGuiSliderFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L219) | Draws a bounded floating-point slider.
返回:true when the value changes.
`label`:The control label.
`value`:The value to display and update.
`minimum`:The inclusive minimum.
`maximum`:The inclusive maximum.
`flags`:Slider behavior. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.SmallButton(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L49) | Draws a compact button.
返回:true when the button is activated.
`label`:The label text validated by the small button operation. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Spacing()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L716) | Adds one standard vertical spacing unit. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.TableHeadersRow()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L369) | Draws the table header row from configured column labels. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.TableNextColumn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L382) | Advances to the next table column.
返回:true when the column is visible. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.TableNextRow()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L374) | Advances to the next table row. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.TableSetupColumn(string label, Inno.Native.ImGui.ImGuiTableColumnFlags flags = Inno.Native.ImGui.ImGuiTableColumnFlags.None, float widthOrWeight = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L359) | Declares one table column.
`label`:The column label.
`flags`:Column behavior.
`widthOrWeight`:The initial fixed width or stretch weight. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Text(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L21) | Draws unformatted text.
`text`:The text text validated by the text operation. | — | — | -| [`public static uint Inno.Editor.ImGui.ImGui.ToPackedColor(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L832) | Converts a floating-point RGBA color to Dear ImGui packed color order.
返回:The packed color value.
`color`:The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGui.TreeNode(string label, Inno.Native.ImGui.ImGuiTreeNodeFlags flags = Inno.Native.ImGui.ImGuiTreeNodeFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L463) | Draws a tree node and pushes its tree scope when it is open.
返回:true when child contents should be submitted.
`label`:The visible label and identity.
`flags`:Tree-node behavior. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.TreePop()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L471) | Ends the current open tree-node scope. | — | — | -| [`public static class Inno.Editor.ImGui.ImGui`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L13) | Exposes a script-safe, pointer-free subset of Dear ImGui for custom Editor tools. Every call is valid only while an Editor drawing callback is active. | — | — | - -### `Inno.Editor.ImGui.ImGuiEditorRuntime` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.ImGui.ImGuiEditorRuntime.ImGuiEditorRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IEnumerable hostServices)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L51) | Creates an ImGui editor runtime with stable host-owned extension services.
`context`:The shared editor context that owns project settings and frame state.
`types`:The host-owned type catalog that coordinates extension generations.
`logs`:The host-owned logging router used by editor infrastructure.
`hostServices`:Services available to discovered extension constructors. | — | `System.ArgumentNullException`:Thrown when context, types, or hostServices is null. | -| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L267) | Releases the resources owned by this implementation. | — | `Inno.Core.Execution.RetirementPendingException`:Editor extensions still own live work. The interaction runtime remains retained for safe shutdown. | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.Draw()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L120) | Draws the complete editor frame through ImGui. | — | — | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L248) | Dispatches contextual shortcuts, leaving editing keys with active text widgets while permitting explicit save.
`keyEvent`:The keyboard event received from the application event stream. | — | — | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.PrepareShutdown()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L111) | Freezes automatic extension-state persistence and writes the final project state before editor modules begin shutting down. | This operation is idempotent and prevents module teardown from overwriting the saved module sections with transient empty state. | `System.ObjectDisposedException`:Thrown after this runtime has been disposed. | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.SaveState()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L94) | Captures all stateful active modules and panels and flushes their project state to disk. | — | `System.ObjectDisposedException`:Thrown after this runtime has been disposed. | -| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Start()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L78) | Starts value processing after validating the current state. | — | — | -| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L86) | Recomputes owned state from the current validated inputs.
`frame`:The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.ImGui.ImGuiEditorRuntime.interactions { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L68) | Gets the active presentation-independent interaction entry point. | — | — | -| [`public int Inno.Editor.ImGui.ImGuiEditorRuntime.panelCount { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L73) | Gets the number of active dockable panels. | — | — | -| [`public sealed class Inno.Editor.ImGui.ImGuiEditorRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L23) | Presents the backend-independent editor interaction runtime through ImGui. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AddGlyphCentered(Inno.Native.ImGui.ImDrawList* drawList, Inno.Native.ImGui.ImFont* font, float fontSize, string text, System.Numerics.Vector2 center, uint color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L80) | Draws one glyph so the center of its visible bounds matches a requested point.
`drawList`:Draw list that receives the glyph.
`font`:Font that owns the glyph.
`fontSize`:Requested baked font size.
`text`:Text containing the glyph to draw.
`center`:Target center in screen coordinates.
`color`:Packed ImGui text color. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AxisDragFloat(string id, string axis, ref float value, float width, float speed = 0.1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L225) | Draws a float drag field with a compact colored axis prefix.
返回:true when the value changed.
`id`:Stable control identifier.
`axis`:Axis label such as X, Y, Z, or W.
`value`:Mutable numeric value.
`width`:Total width including the axis prefix.
`speed`:Drag speed. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AxisDragInt(string id, string axis, ref int value, float width, float speed = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L351) | Draws an integer drag field with a compact colored axis prefix.
返回:true when the value changed.
`id`:Stable control identifier.
`axis`:Axis label such as X, Y, Z, or W.
`value`:Mutable numeric value.
`width`:Total width including the axis prefix.
`speed`:Drag speed. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo(string id, string preview, Inno.Native.ImGui.ImGuiComboFlags flags = Inno.Native.ImGui.ImGuiComboFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L40) | Begins a combo whose popup opens below the control and stays within its containing window.
返回:true when the combo popup is open and its contents should be submitted; otherwise, false.
`id`:Stable combo identifier in the current ImGui scope.
`preview`:Text displayed by the closed combo.
`flags`:Native combo presentation flags. Only Inno.Native.ImGui.ImGuiComboFlags.WidthFitPreview is supported. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when preview is null.
`System.ArgumentOutOfRangeException`:Thrown when flags contains a presentation mode unsupported by the editor selector. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginContextMenu(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L163) | Begins a parent-viewport styled right-click context menu for the most recently submitted item.
返回:true when context-menu content should be drawn; otherwise, false.
`id`:The stable popup identifier in the current ImGui ID scope. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuPopup(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L34) | Begins an explicitly opened popup using the editor context-menu presentation contract. The popup stays in its parent viewport, sizes itself to submitted content, and scrolls when its work-area bound is reached.
返回:true when popup content should be submitted; otherwise, false.
`id`:The stable identifier previously passed to ImGui when opening the popup. | — | `System.ArgumentException`:Thrown when id is empty. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuSelector(string id, string preview, float width, float minimumPopupWidth)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L90) | Draws a compact selector control and begins a work-area-bounded menu popup.
返回:true when the popup is open and its contents should be submitted; otherwise, false.
`id`:Stable selector identifier in the current ImGui scope.
`preview`:Text displayed by the closed selector.
`width`:Width reserved for the selector control.
`minimumPopupWidth`:Minimum outer width of the popup, including its window padding. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when preview is null.
`System.ArgumentOutOfRangeException`:Thrown when width or minimumPopupWidth is not positive. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuTooltip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L63) | Begins a parent-viewport tooltip using the same padding, colors, border, and spacing as editor menus.
返回:true when tooltip content should be submitted; otherwise, false. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginSearchPopup(string id, ref string query, string hint, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L67) | Begins a popup and draws its focused search field.
返回:true when popup content should be drawn; otherwise, false.
`id`:The popup identifier previously supplied to ImGui.
`query`:The mutable search query.
`hint`:The hint shown while the query is empty.
`capacity`:The maximum UTF-8 buffer capacity.
`width`:The search field width, or a negative value to use the editor default. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginWindowContextMenu(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L184) | Begins a parent-viewport styled right-click context menu for the current window's unoccupied background.
返回:true when context-menu content should be drawn; otherwise, false.
`id`:The stable popup identifier in the current ImGui ID scope. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CardBody(string id, System.Action drawContent, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L253) | Draws expanded collapsible-card content inside a framed body.
`id`:The stable card identifier.
`drawContent`:The callback that draws card content.
`dimmed`:Whether content is visually disabled and non-interactive. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredButton(string label, float topPadding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L262) | Draws a horizontally centered button with optional space above it.
返回:true when the button is pressed.
`label`:Visible label and optional ImGui identifier suffix.
`topPadding`:Additional vertical space above the button. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredProgressBar(float fraction, System.Numerics.Vector2 size, string overlay)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L298) | Draws a progress bar whose overlay remains centered over the complete bar.
`fraction`:The completed fraction rendered by the progress bar.
`size`:The requested ImGui progress-bar size.
`overlay`:The text drawn at the geometric center of the complete progress bar. | — | `System.ArgumentNullException`:Thrown when overlay is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredText(string text, System.Numerics.Vector2 areaSize)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L311) | Draws non-interactive text centered inside a reserved layout area.
`text`:Visible text.
`areaSize`:Size of the layout area that contains the text. | — | `System.ArgumentNullException`:Thrown when text is null.
`System.ArgumentOutOfRangeException`:Thrown when either component of areaSize is not positive. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredWrappedText(string text, System.Numerics.Vector2 areaSize, System.Numerics.Vector2 padding)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L351) | Draws wrapped non-interactive text as a centered block inside a padded layout area.
`text`:The literal text to wrap and draw.
`areaSize`:The complete area reserved by the widget.
`padding`:The minimum horizontal and vertical distance retained from the area edges whenever space permits. | — | `System.ArgumentNullException`:Thrown when text is null.
`System.ArgumentOutOfRangeException`:Thrown when an area component is not positive or a padding component is negative. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Checkbox(string label, ref bool value, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L239) | Draws a standard checkbox with the shared editor tooltip behavior.
返回:true when the value changed.
`label`:Visible label and stable ImGui identifier.
`value`:Mutable checked state.
`tooltip`:Optional explanation displayed while the checkbox is hovered. | — | `System.ArgumentException`:Thrown when label is empty or whitespace. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableIcon(string id, string icon, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L199) | Draws a clickable icon inside the same square interaction slot used by editor close controls. The icon has no resting background and changes only its glyph color while hovered or active.
返回:true when the icon is pressed.
`id`:Stable identifier used by ImGui to track the interaction.
`icon`:The visible icon glyph.
`tooltip`:Optional tooltip displayed while the interaction is hovered. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when icon is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableText(string id, string text, System.Numerics.Vector2 controlSize, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L257) | Draws clickable text centered inside an explicitly sized transparent interaction area.
返回:true when the text is pressed.
`id`:Stable identifier used by ImGui to track the interaction.
`text`:Visible text or icon glyph.
`controlSize`:Size of the transparent interaction area.
`tooltip`:Optional tooltip displayed while the interaction is hovered. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when text is null.
`System.ArgumentOutOfRangeException`:Thrown when either component of controlSize is not positive. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableText(string id, string text, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L122) | Draws clickable text without a persistent or hovered background.
返回:true when the text is pressed.
`id`:Stable identifier used by ImGui to track the interaction.
`text`:Visible text or icon glyph.
`tooltip`:Optional tooltip displayed while the interaction is hovered. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when text is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CollapsingCard(string id, string title, System.Action? drawLeadingControl = null, System.Action? drawTrailingControl = null, bool defaultOpen = true, bool dimmed = false, float trailingControlWidth = 0, System.Action? drawContextMenu = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L44) | Draws a component-style collapsible card header.
返回:true when card content should be drawn; otherwise, false.
`id`:The stable card identifier.
`title`:The visible card title.
`drawLeadingControl`:An optional control drawn before the title.
`drawTrailingControl`:An optional control aligned to the right edge.
`defaultOpen`:Whether the card starts expanded.
`dimmed`:Whether header controls and title use the inactive text color.
`trailingControlWidth`:The optional width reserved for the trailing control group.
`drawContextMenu`:An optional callback that binds a context menu to the complete header item. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CollectionSectionHeader(string title)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L76) | Draws a square collection-section band whose background, separator, and padding are shared with hierarchy and Inspector presentation.
`title`:Literal section title. | — | `System.ArgumentNullException`:Thrown when title is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ColoredText(System.Numerics.Vector4 color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L416) | Draws unformatted text with a temporary foreground color.
`color`:The foreground color applied only while drawing the text.
`text`:The literal text to draw without format-string interpretation. | — | `System.ArgumentNullException`:Thrown when text is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactCheckbox(string id, ref bool value, float size = -1, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L156) | Draws a compact checkbox whose checked fill uses the current text color.
返回:true when the value changed.
`id`:Stable control identifier.
`value`:Mutable checked state.
`size`:Visual square size in pixels.
`tooltip`:Optional explanation displayed while the checkbox is hovered. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactDragFloat(string label, ref float value, float speed = 0.1, float? minimum = null, float? maximum = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L257) | Draws a floating-point drag field with a compact one-decimal presentation and precise text editing.
返回:true when the exact value changed.
`label`:Stable ImGui label and identifier.
`value`:Exact floating-point value to edit. Compact presentation never rounds the stored value.
`speed`:Drag speed.
`minimum`:Optional inclusive lower bound.
`maximum`:Optional inclusive upper bound. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactSliderFloat(string label, ref float value, float minimum, float maximum)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L300) | Draws a bounded slider with compact presentation and a double-click precise input mode.
返回:true when the exact value changed.
`label`:Stable ImGui label and identifier.
`value`:Exact floating-point value to edit. Compact presentation never rounds the stored value.
`minimum`:Inclusive lower bound.
`maximum`:Inclusive upper bound. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ConstrainedContent(string id, System.Action drawContent, bool useWindowPadding = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L112) | Draws a vertically auto-sized content region that is constrained to the current available width and cannot create an independent scroll range.
`id`:Stable identifier used by ImGui to track the content region.
`drawContent`:Callback that draws the complete region contents.
`useWindowPadding`:Whether the region should apply the centralized standard window padding exactly once. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when drawContent is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Disabled(bool disabled, System.Action draw)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L371) | Draws content inside an ImGui disabled scope when requested.
`disabled`:Whether interaction is disabled.
`draw`:Drawing callback. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropSource(string payloadType, in TPayload payload, System.Action? drawPreview = null, bool allowHoldToOpenOthers = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L38) | Publishes an unmanaged drag payload for the most recently submitted item.
返回:true while the item is an active drag source.
`payloadType`:Stable ImGui payload type identifier.
`payload`:Payload value.
`drawPreview`:Optional preview drawing callback.
`allowHoldToOpenOthers`:Whether hovering over another openable control may open it after the native drag-hold delay. | — | `System.ArgumentException`:Thrown when payloadType is empty or whitespace. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropSource(string payloadType, System.Func payloadFactory, System.Action? drawPreview = null, bool allowHoldToOpenOthers = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L88) | Publishes a lazily created unmanaged drag payload for the most recently submitted item.
返回:true while the item is an active drag source.
`payloadType`:Stable ImGui payload type identifier.
`payloadFactory`:Creates the payload only after dragging starts.
`drawPreview`:Optional preview drawing callback.
`allowHoldToOpenOthers`:Whether hovering over another openable control may open it after the native drag-hold delay. | — | `System.ArgumentException`:Thrown when payloadType is empty or whitespace.
`System.ArgumentNullException`:Thrown when payloadFactory is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload, out bool isPreviewing, bool drawDefaultHighlight) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L186) | Accepts an unmanaged payload and controls whether ImGui draws its default target rectangle.
返回:true only when a compatible payload is delivered.
`payloadType`:Stable ImGui payload type identifier.
`payload`:Previewed or delivered payload value.
`isPreviewing`:Whether a compatible payload is hovering over the target.
`drawDefaultHighlight`:Whether ImGui draws its default target rectangle. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload, out bool isPreviewing) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L155) | Accepts an unmanaged payload and reports its preview state on the most recently submitted item.
返回:true only when a compatible payload is delivered.
`payloadType`:Stable ImGui payload type identifier.
`payload`:Previewed or delivered payload value.
`isPreviewing`:Whether a compatible payload is hovering over the target. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L128) | Accepts an unmanaged payload on the most recently submitted item.
返回:true only when a compatible payload is delivered.
`payloadType`:Stable ImGui payload type identifier.
`payload`:Delivered payload value. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint targetId, out TPayload payload, out bool isPreviewing, bool drawDefaultHighlight = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L248) | Accepts an unmanaged payload on an explicit screen-space rectangle.
返回:true only when a compatible payload is delivered.
`payloadType`:Stable ImGui payload type identifier.
`minimum`:Minimum screen coordinate of the complete target.
`maximum`:Maximum screen coordinate of the complete target.
`targetId`:ImGui identifier unique within the current window.
`payload`:Previewed or delivered payload value.
`isPreviewing`:Whether a compatible payload is hovering over the target.
`drawDefaultHighlight`:Whether ImGui draws its default target rectangle. | — | `System.ArgumentException`:Thrown when payloadType is empty or whitespace. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawDisclosureIndicator(System.Numerics.Vector2 min, System.Numerics.Vector2 max, bool open, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L208) | Draws a vertically centered disclosure triangle with button-style hover feedback.
`min`:The minimum screen coordinate of the indicator area.
`max`:The maximum screen coordinate of the indicator area.
`open`:Whether the represented content is expanded.
`dimmed`:Whether the indicator uses the inactive text color. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawItemTooltip(string? text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L88) | Draws a consistently sized, wrapped editor tooltip for the most recently submitted item.
`text`:Tooltip text. Empty values do not draw a tooltip. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawTooltip(string? text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L105) | Draws the standard viewport-clamped tooltip when a custom-drawn canvas element is hovered.
`text`:Tooltip contents. The caller owns hit testing; empty text draws nothing. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DropTargetHighlight(System.Numerics.Vector2 min, System.Numerics.Vector2 max)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L422) | Draws the standard yellow rectangular drag-and-drop target highlight above all normal window content in screen coordinates.
`min`:Minimum target coordinate.
`max`:Maximum target coordinate. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L60) | Ends a combo opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo(System.String,System.String,Inno.Native.ImGui.ImGuiComboFlags). | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndContextMenu()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L203) | Ends a context menu opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginContextMenu(System.String) or Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginWindowContextMenu(System.String). | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L50) | Ends a popup opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuPopup(System.String) and restores the previous style. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuSelector()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L134) | Ends a selector popup opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuSelector(System.String,System.String,System.Single,System.Single). | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuTooltip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L76) | Ends a tooltip opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuTooltip and restores the previous style. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndSearchPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L89) | Ends a popup opened by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginSearchPopup(System.String,System.String@,System.String,System.UIntPtr,System.Single). | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EnsureSection(string title = "Properties", string? description = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L148) | Ensures that subsequently drawn default properties belong to a framed section.
返回:true when the current section accepts content; otherwise, false when that section is collapsed.
`title`:Title used only when the current section layout has not declared a section yet.
`description`:Optional hover explanation for the implicit section title. | — | `System.ArgumentNullException`:Thrown when title is null. | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetClickableTextSize(string text, System.Numerics.Vector2 padding)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L475) | Calculates a clickable text area from visible text and requested inner padding.
返回:The complete transparent interaction area size.
`text`:Visible text whose dimensions determine the content size.
`padding`:Horizontal and vertical padding surrounding the text. | — | `System.ArgumentNullException`:Thrown when text is null.
`System.ArgumentOutOfRangeException`:Thrown when either component of padding is negative. | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactClickableTextSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L498) | Gets the compact fixed interaction size used by icon-style clickable text.
返回:A fixed-size area that follows the editor icon slot convention. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactIconSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L510) | Gets the square icon interaction size shared by dock-header close controls and compact editor icons.
返回:A square size based on the current ImGui font size. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetGlyphVisualBounds(Inno.Native.ImGui.ImFont* font, float fontSize, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L36) | Gets the visible bounds of the first glyph in a string at a requested font size.
返回:A vector containing the glyph's left, top, right, and bottom offsets relative to the text drawing origin.
`font`:Font that owns the glyph.
`fontSize`:Requested baked font size.
`text`:Text whose first Unicode scalar identifies the glyph. | — | `System.ArgumentException`:Thrown when text is empty. | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetLabelChipSize(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L26) | Gets the layout size of a compact colored label chip using the centralized editor style.
返回:The size reserved by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.LabelChip(System.String,System.Numerics.Vector4) for the supplied text.
`label`:Visible label text. | — | `System.ArgumentNullException`:Thrown when label is null. | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetTypeBadgeSize(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L82) | Gets the layout size of a compact outlined type badge.
返回:The size reserved by Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TypeBadge(System.String,System.Numerics.Vector4,System.String).
`label`:Visible type name. | — | `System.ArgumentNullException`:Thrown when label is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HeaderSurface(string id, System.Action drawContent, bool spanWindowPadding = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L164) | Draws a square-cornered editor header surface using the shared target-header palette, border, and padding.
`id`:Stable identifier used by ImGui to track the header region.
`drawContent`:Callback that draws the complete header content.
`spanWindowPadding`:Whether the header should extend through the current parent window padding to both edges. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when drawContent is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HelpBox(string text, string icon, System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L28) | Draws a wrapped contextual message with a semantic icon and a subdued status surface.
`text`:Literal message shown within the current content width.
`icon`:Icon-font glyph identifying the message severity.
`color`:Semantic accent for the icon, outline, and leading stripe. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Hint(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L352) | Draws disabled hint text that wraps to the current content width.
`text`:Hint text. | — | `System.ArgumentNullException`:Thrown when text is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HoverText(string id, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L147) | Draws a text link with no button background and highlights it only through text presentation.
返回:true when the link is pressed.
`id`:Stable identifier used by ImGui to track the interaction.
`text`:Visible link text. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentNullException`:Thrown when text is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.IconText(string icon, string text, bool highlight)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L544) | Draws icon and text with the icon's visible glyph bounds centered in a slot that expands when the glyph is wider than the normal editor icon slot.
`icon`:Icon text.
`text`:Main text.
`highlight`:Whether to underline and emphasize the drawn icon and text. | — | — | -| [`public static Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.InlineRename(string id, ref string text, ref bool requestFocus, float rowHeight, nuint capacity = 512, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L47) | Draws a compact single-line rename editor inside an existing row.
返回:The current rename outcome.
`id`:The stable control identifier.
`text`:The mutable text buffer.
`requestFocus`:Whether keyboard focus and complete-value selection should be requested. The value remains true until the input becomes active and its current contents are selected.
`rowHeight`:The height of the row area in which the field is centered.
`capacity`:The maximum UTF-8 buffer capacity.
`width`:The control width, or a negative value to fill the available space. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentOutOfRangeException`:Thrown when rowHeight is not finite or is not positive. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.InsertionLine(float fromX, float toX, float y)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L399) | Draws a horizontal insertion marker above normal window content in screen coordinates.
`fromX`:Marker start X coordinate.
`toX`:Marker end X coordinate.
`y`:Marker Y coordinate. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.LabelChip(string label, System.Numerics.Vector4 background)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L49) | Draws non-interactive text centered on a compact, softly rounded colored background.
`label`:Visible label text.
`background`:Background color drawn behind the label. | — | `System.ArgumentNullException`:Thrown when label is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.MetadataValue(string metadata, System.Action drawValue, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L152) | Draws a subdued metadata prefix followed by an interactive value on the same line.
`metadata`:Short contextual text such as a declared value type.
`drawValue`:Callback that draws the value or action following the metadata.
`tooltip`:Optional explanation displayed while the metadata is hovered. | — | `System.ArgumentNullException`:Thrown when metadata or drawValue is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.MetadataValue(string metadata, string value, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L185) | Draws one wrapped, subdued value whose metadata and content remain in the value column.
`metadata`:Short contextual text such as a declared value type.
`value`:Literal value description shown after the metadata separator.
`tooltip`:Optional explanation displayed while the combined value is hovered. | — | `System.ArgumentNullException`:Thrown when metadata or value is null. | -| [`public static string Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.NicifyName(string name)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L444) | Converts an identifier into a readable editor label.
返回:A label with separators and title casing.
`name`:Source identifier. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PanelWindow(string title, ref bool isOpen, System.Action drawBody, Inno.Native.ImGui.ImGuiWindowFlags flags = Inno.Native.ImGui.ImGuiWindowFlags.NoCollapse, bool useWindowPadding = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L34) | Opens a standard panel window and executes panel body.
`title`:Window title.
`isOpen`:Visible state.
`drawBody`:Panel body callback.
`flags`:Window flags.
`useWindowPadding`:Whether the panel body should use the current standard window padding. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyLabel(string label, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L126) | Draws a label within its current table cell, wrapping when the column is narrow.
`label`:Visible label text.
`tooltip`:Optional explanation shown while the label is hovered. | — | `System.ArgumentNullException`:Thrown when label is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyRow(string id, System.Action drawLabel, System.Action drawValue)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L70) | Draws a two-column property row whose label is supplied by a custom presentation callback.
`id`:Stable row identifier.
`drawLabel`:Label presentation callback. It may wrap or draw badges inside the label column.
`drawValue`:Value control callback. | — | `System.ArgumentException`:Thrown when id is empty or whitespace.
`System.ArgumentNullException`:Thrown when drawLabel or drawValue is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyRow(string id, string label, System.Action drawValue, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L39) | Draws a two-column property row with a stable internal identifier.
`id`:Stable row identifier.
`label`:Human-readable property label.
`drawValue`:Value control callback.
`tooltip`:Optional hover help displayed from the property label. | — | `System.ArgumentException`:Thrown when id is empty or whitespace.
`System.ArgumentNullException`:Thrown when label or drawValue is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SearchInput(string id, string hint, ref string query, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L34) | Draws a single-line search field with a stable identifier.
返回:true when the query changed; otherwise, false.
`id`:The stable control identifier.
`hint`:The hint shown while the query is empty.
`query`:The mutable search query.
`capacity`:The maximum UTF-8 buffer capacity.
`width`:The control width, or a negative value to fill the available space. | — | — | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SectionHeader(string title, string? description = null, System.Action? drawLeadingControl = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L180) | Draws a section heading with hover-only description using the shared Inspector presentation.
返回:true when the section content should be drawn; otherwise, false when the section is collapsed.
`title`:Section title.
`description`:Optional hover explanation.
`drawLeadingControl`:Optional interactive control drawn before the section title. | — | `System.ArgumentNullException`:Thrown when title is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SectionLayout(System.Action drawContent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L106) | Draws content in a scope where consecutive section headers become framed fieldsets.
`drawContent`:Callback that draws all content participating in the section layout. | — | `System.ArgumentNullException`:Thrown when drawContent is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetNextTreeNodeOpen(bool open)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L176) | Overrides the retained expansion state of the next submitted non-leaf tree row.
`open`:Whether the next row should be expanded. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetupPropertyColumns()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L108) | Configures the label and input columns of an active table with a shared two-to-three ratio. A following fixed-width action column may be added by the caller. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetupStyle()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs#L25) | Applies the centralized editor layout metrics and palette to the current ImGui context. | — | — | -| [`public static Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TreeNode(string id, System.Action onDraw, in Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions options)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L47) | Draws a full-width interactive tree row whose non-leaf content toggles expansion when double-clicked, while preserving single-click disclosure-arrow behavior.
返回:Interaction and row geometry for the submitted item.
`id`:Stable row identifier.
`onDraw`:Content drawing callback that receives the native row geometry established by ImGui.
`options`:Tree row options. | — | — | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TypeBadge(string label, System.Numerics.Vector4 accent, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L104) | Draws a compact, non-interactive type badge with a subdued fill and semantic outline.
`label`:Visible type name.
`accent`:Semantic type-family accent.
`tooltip`:Optional explanation shown while the badge is hovered. | — | `System.ArgumentNullException`:Thrown when label is null. | -| [`public static void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.WrappedText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L442) | Draws literal text wrapped to the remaining content width without using a native variadic formatting entry point.
`text`:The literal text to draw. | — | `System.ArgumentNullException`:Thrown when text is null. | -| [`public static bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.isSectionContentVisible { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L130) | Gets whether content belonging to the current Inspector section should be drawn. | Attribute-driven drawers use this value to skip properties until the next section header when the current section is collapsed. | — | -| [`public static Inno.Editor.ImGui.EditorStyleMetrics Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.style { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs#L20) | Gets the centralized editor layout metrics shared by every widget and feature panel. | — | — | -| [`public static class Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L12) | Provides reusable editor controls and rendering helpers built on the native ImGui API. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.InlineRenamePresentation(string id, float width, float rowHeight, nuint bufferSize = 512)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L176) | Creates inline rename presentation data.
`id`:The stable ImGui identifier used by the input field.
`width`:The requested input width in logical pixels.
`rowHeight`:The height of the row area in which the field is centered.
`bufferSize`:The maximum UTF-8 buffer size accepted by the input. | — | `System.ArgumentException`:Thrown when id is empty.
`System.ArgumentOutOfRangeException`:Thrown when width or rowHeight is not finite and positive, or when bufferSize is zero. | -| [`public nuint Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.bufferSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L229) | Gets the maximum UTF-8 buffer size accepted by the input. | — | — | -| [`public string Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.id { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L214) | Gets the stable ImGui identifier used by the input field. | — | — | -| [`public float Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L224) | Gets the height of the row area in which the input field is centered. | — | — | -| [`public float Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.width { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L219) | Gets the requested input width in logical pixels. | — | — | -| [`public sealed class Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L152) | Describes the view-owned geometry used to present an active inline rename action. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Cancel = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L255) | Indicates that the edited text should be discarded. | — | — | -| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Commit = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L245) | Indicates that the edited text should be committed. | — | — | -| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.FocusLost = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L250) | Indicates that the input lost focus and its valid value should be committed before closing. | — | — | -| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L240) | Indicates that the interaction remains active. | — | — | -| [`public enum Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L235) | Describes the outcome of an inline rename control. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public float Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L403) | Gets the actual native row height established for the current tree node. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L393) | Provides the native geometry of a tree row while its custom content is being drawn. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.backgroundColor { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L429) | Gets the custom background color used when Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.showBackground is enabled. | — | — | -| [`public System.Action? Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.drawViewportOverlay { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L445) | Gets the callback that draws controls fixed to the current viewport without extending the tree's horizontal content boundary. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.hideGuideLines { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L439) | Gets whether ancestor and branch guide lines are omitted for this row. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.isLeaf { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L419) | Gets whether the row has no expandable children. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.selected { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L414) | Gets whether the row is selected. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.showBackground { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L424) | Gets whether a custom background is drawn behind an unselected row. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.suppressHoverHighlight { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L434) | Gets whether the row keeps its configured background while hovered. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L409) | Configures an interactive tree row. | — | — | - -### `Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.contentMin { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L505) | Gets the minimum screen coordinate of the row's interactive content, excluding tree indentation. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L479) | Gets whether the content row was clicked. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isDoubleClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L485) | Gets whether the content row was double-clicked. Double-clicking a non-leaf content row also toggles its retained expansion state for the next frame. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L490) | Gets whether the full row is hovered. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isOpen { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L474) | Gets whether child content should be rendered. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.max { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L500) | Gets the row maximum screen coordinate. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.min { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L495) | Gets the row minimum screen coordinate. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L451) | Describes interaction state produced by a tree row. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.statisticRowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L452) | Gets the vertical padding inside one statistics row. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L457) | Gets spacing between distinct control groups in an Inspector target header row. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorAddButtonTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L462) | Gets top padding above inspector add buttons. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L467) | Gets the minimum inline hierarchy rename width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameTrailingGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L472) | Gets the hierarchy rename trailing-control gap. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyBlankMinimumHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L477) | Gets the minimum height of the hierarchy blank drop area. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarTightSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L482) | Gets the tight asset browser toolbar spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L487) | Gets the regular asset browser toolbar spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L492) | Gets spacing between asset browser toolbar sections. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListNameSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L497) | Gets the default normalized position of the asset list name/type separator. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListTypeSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L502) | Gets the default normalized position of the asset list type/source separator. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListMinimumColumnRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L507) | Gets the minimum normalized width reserved for each asset list column. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListSeparatorHitWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L512) | Gets the horizontal hit width of an asset list column separator. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListContentHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L517) | Gets the horizontal inset between an asset list separator and column content. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L522) | Gets horizontal padding for asset grid labels. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelBottomPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L527) | Gets bottom padding for asset grid labels. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelLineSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L532) | Gets the additional vertical spacing between asset grid label lines. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridFixedCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L537) | Gets fixed padding added to calculated asset grid cells. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridScaleBias { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L542) | Gets the scale bias added to asset grid icons. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L547) | Gets the top inset reserved above an asset grid icon. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L552) | Gets the horizontal inset that constrains an asset grid icon inside its card. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L557) | Gets the vertical spacing between an asset grid icon and its label. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L562) | Gets spacing around breadcrumb separators. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisValueMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L567) | Gets the minimum width of one axis value field. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L572) | Gets the minimum width of an axis prefix. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixWidthRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L577) | Gets the axis prefix share of the complete control. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.interactionOverlayThickness { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L582) | Gets the standard foreground interaction-overlay thickness. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scriptCompilationWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L587) | Gets script compilation modal width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.modalWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L592) | Gets the standard centered modal width. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeInSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L597) | Gets the modal fade-in duration in seconds. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalMinimumVisibleSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L602) | Gets the minimum modal visibility duration in seconds. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeOutSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L607) | Gets the modal fade-out duration in seconds. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLeftOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L612) | Gets tree guide left offset. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLineOverlap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L617) | Gets the overlap used to join adjacent tree guide segments. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeFolderConnectorPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L622) | Gets additional connector padding for expandable tree nodes. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.textDecorationOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L627) | Gets the vertical offset of text decorations from the baseline. | — | — | +| [`public Inno.Editor.ImGui.EditorStyleMetrics.EditorStyleMetrics()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public readonly struct Inno.Editor.ImGui.EditorUiLayout`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L8) | Copies the presentation metrics needed by custom layouts without retaining native style memory. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L13) | Gets spacing between neighboring controls. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L17) | Gets the current window inset. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L21) | Gets table cell padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.itemInnerSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L25) | Gets spacing between parts of a compound control. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.framePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L29) | Gets padding inside a control. | — | — | +| [`public float Inno.Editor.ImGui.EditorUiLayout.dockingSeparatorSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L33) | Gets dock splitter thickness. | — | — | +| [`public float Inno.Editor.ImGui.EditorUiLayout.scrollbarSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L37) | Gets scrollbar width. | — | — | +| [`public float Inno.Editor.ImGui.EditorUiLayout.frameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L41) | Gets control corner radius. | — | — | +| [`public static class Inno.Editor.ImGui.EditorIconCatalog`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs#L9) | Describes the host's complete standard glyph vocabulary as immutable presentation values. | — | — | +| [`public static System.Collections.Generic.IReadOnlyList<(string Name, string Glyph)> Inno.Editor.ImGui.EditorIconCatalog.standard { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs#L14) | Gets all named glyphs supplied by the installed UI font, in deterministic name order. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiFontStyle : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L11) | Chooses composable font emphasis while leaving actual font ownership with the presentation host. | — | — | +| [`Inno.Editor.ImGui.ImGuiFontStyle.Regular = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L16) | Uses the ordinary face. | — | — | +| [`Inno.Editor.ImGui.ImGuiFontStyle.Bold = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L20) | Emphasizes text with the bold face. | — | — | +| [`Inno.Editor.ImGui.ImGuiFontStyle.Italic = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L24) | Emphasizes text with the italic face. | — | — | +| [`public static class Inno.Editor.ImGui.ImGuiFont`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L30) | Uses the host's context-owned font catalog without exposing backend types to Editor features. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGuiFont.IsAvailable(Inno.Editor.ImGui.ImGuiFontStyle style)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L41) | Checks whether the current host registered an exact face.
`style`: The requested emphasis. | True when the exact face is available. | — | +| [`public static Inno.Editor.ImGui.ImGuiFontScope Inno.Editor.ImGui.ImGuiFont.PushStyle(Inno.Editor.ImGui.ImGuiFontStyle style)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L56) | Pushes a host-owned font face for one synchronous control group.
`style`: The requested emphasis. | An owner-window/frame scope; the host may use a registered nearest face. | — | +| [`public ref struct Inno.Editor.ImGui.ImGuiFontScope : System.IDisposable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L68) | Restores a font push only in its original session, frame and native stack position. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiFontScope.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs#L92) | Restores the preceding font after validating scope order; default and retired scopes are inert. | — | `System.InvalidOperationException`: The scope is used in another context, frame or stack position. | +| [`public static class Inno.Editor.ImGui.ImGuiIcon`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L5) | Provides the standard Editor glyph vocabulary; extension controls may also supply their own glyph text. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.AngleLeft = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L10) | Gets the angle left glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.AngleRight = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L14) | Gets the angle right glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.ArrowRotateLeft = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L18) | Gets the arrow rotate left glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.ArrowsUpDownLeftRight = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L22) | Gets the arrows up down left right glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Bug = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L26) | Gets the bug glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Camera = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L30) | Gets the camera glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.CircleInfo = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L34) | Gets the circle info glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.CircleQuestion = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L38) | Gets the circle question glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.CircleXmark = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L42) | Gets the circle xmark glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Cube = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L46) | Gets the cube glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Cubes = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L50) | Gets the cubes glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.DiceD6 = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L54) | Gets the dice d6 glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Eye = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L58) | Gets the eye glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.EyeSlash = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L62) | Gets the eye slash glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.File = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L66) | Gets the file glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.FileCode = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L70) | Gets the file code glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.FileLines = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L74) | Gets the file lines glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Folder = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L78) | Gets the folder glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.ForwardStep = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L82) | Gets the forward step glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Gears = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L86) | Gets the gears glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Globe = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L90) | Gets the globe glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.GripVertical = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L94) | Gets the grip vertical glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.LayerGroup = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L98) | Gets the layer group glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Lightbulb = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L102) | Gets the lightbulb glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Lock = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L106) | Gets the lock glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.LockOpen = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L110) | Gets the lock open glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.MagnifyingGlass = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L114) | Gets the magnifying glass glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Palette = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L118) | Gets the palette glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Pause = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L122) | Gets the pause glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Pen = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L126) | Gets the pen glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Play = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L130) | Gets the play glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Plus = "+"`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L134) | Gets the plus glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Rotate = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L138) | Gets the rotate glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.RupeeSign = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L142) | Gets the rupee sign glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.RupiahSign = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L146) | Gets the rupiah sign glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Shader = "S"`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L150) | Gets the shader glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.SkullCrossbones = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L154) | Gets the skull crossbones glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Stop = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L158) | Gets the stop glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.TriangleExclamation = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L162) | Gets the triangle exclamation glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.UpDownLeftRight = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L166) | Gets the up down left right glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.UpRightAndDownLeftFromCenter = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L170) | Gets the up right and down left from center glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.VectorSquare = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L174) | Gets the vector square glyph used by standard Editor controls. | — | — | +| [`public const string Inno.Editor.ImGui.ImGuiIcon.Xmark = ""`](../../src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs#L178) | Gets the xmark glyph used by standard Editor controls. | — | — | +| [`public sealed class Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L10) | Provides reusable editor controls and rendering helpers built on the native ImGui API. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CollapsingCard(string id, string title, System.Action? drawLeadingControl = null, System.Action? drawTrailingControl = null, bool defaultOpen = true, bool dimmed = false, float trailingControlWidth = 0, System.Action? drawContextMenu = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L42) | Draws a component-style collapsible card header.
`id`: The stable card identifier.
`title`: The visible card title.
`drawLeadingControl`: An optional control drawn before the title.
`drawTrailingControl`: An optional control aligned to the right edge.
`defaultOpen`: Whether the card starts expanded.
`dimmed`: Whether header controls and title use the inactive text color.
`trailingControlWidth`: The optional width reserved for the trailing control group.
`drawContextMenu`: An optional callback that binds a context menu to the complete header item. | when card content should be drawn; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawDisclosureIndicator(System.Numerics.Vector2 min, System.Numerics.Vector2 max, bool open, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L195) | Draws a vertically centered disclosure triangle with button-style hover feedback.
`min`: The minimum screen coordinate of the indicator area.
`max`: The maximum screen coordinate of the indicator area.
`open`: Whether the represented content is expanded.
`dimmed`: Whether the indicator uses the inactive text color. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CardBody(string id, System.Action drawContent, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L238) | Draws expanded collapsible-card content inside a framed body.
`id`: The stable card identifier.
`drawContent`: The callback that draws card content.
`dimmed`: Whether content is visually disabled and non-interactive. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuPopup(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L31) | Begins an explicitly opened popup using the editor context-menu presentation contract. The popup stays in its parent viewport, sizes itself to submitted content, and scrolls when its work-area bound is reached.
`id`: The stable identifier previously passed to ImGui when opening the popup. | when popup content should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L47) | Ends a popup opened by and restores the previous style. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuTooltip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L61) | Begins a parent-viewport tooltip using the same padding, colors, border, and spacing as editor menus. | when tooltip content should be submitted; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuTooltip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L75) | Ends a tooltip opened by and restores the previous style. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawItemTooltip(string? text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L88) | Draws a consistently sized, wrapped editor tooltip for the most recently submitted item.
`text`: Tooltip text. Empty values do not draw a tooltip. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawTooltip(string? text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L105) | Draws the standard viewport-clamped tooltip when a custom-drawn canvas element is hovered.
`text`: Tooltip contents. The caller owns hit testing; empty text draws nothing. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginContextMenu(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L161) | Begins a parent-viewport styled right-click context menu for the most recently submitted item.
`id`: The stable popup identifier in the current ImGui ID scope. | when context-menu content should be drawn; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginWindowContextMenu(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L183) | Begins a parent-viewport styled right-click context menu for the current window's unoccupied background.
`id`: The stable popup identifier in the current ImGui ID scope. | when context-menu content should be drawn; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndContextMenu()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L202) | Ends a context menu opened by or . | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetLabelChipSize(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L24) | Gets the layout size of a compact colored label chip using the centralized editor style.
`label`: Visible label text. | The size reserved by for the supplied text. | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.LabelChip(string label, System.Numerics.Vector4 background)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L44) | Draws non-interactive text centered on a compact, softly rounded colored background.
`label`: Visible label text.
`background`: Background color drawn behind the label. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetTypeBadgeSize(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L71) | Gets the layout size of a compact outlined type badge.
`label`: Visible type name. | The size reserved by . | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TypeBadge(string label, System.Numerics.Vector4 accent, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L94) | Draws a compact, non-interactive type badge with a subdued fill and semantic outline.
`label`: Visible type name.
`accent`: Semantic type-family accent.
`tooltip`: Optional explanation shown while the badge is hovered. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactCheckbox(string id, ref bool value, float size = -1, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L134) | Draws a compact checkbox whose checked fill uses the current text color.
`id`: Stable control identifier.
`value`: Mutable checked state.
`size`: Visual square size in pixels.
`tooltip`: Optional explanation displayed while the checkbox is hovered. | when the value changed. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Checkbox(string label, ref bool value, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L202) | Draws a standard checkbox with the shared editor tooltip behavior.
`label`: Visible label and stable ImGui identifier.
`value`: Mutable checked state.
`tooltip`: Optional explanation displayed while the checkbox is hovered. | when the value changed. | `System.ArgumentException`: Thrown when is empty or whitespace. | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredButton(string label, float topPadding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L226) | Draws a horizontally centered button with optional space above it.
`label`: Visible label and optional ImGui identifier suffix.
`topPadding`: Additional vertical space above the button. | when the button is pressed. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredProgressBar(float fraction, System.Numerics.Vector2 size, string overlay)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs#L263) | Draws a progress bar whose overlay remains centered over the complete bar.
`fraction`: The completed fraction rendered by the progress bar.
`size`: The requested ImGui progress-bar size.
`overlay`: The text drawn at the geometric center of the complete progress bar. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropSource(string payloadType, in TPayload payload, System.Action? drawPreview = null, bool allowHoldToOpenOthers = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L37) | Publishes an unmanaged drag payload for the most recently submitted item.
`payloadType`: Stable ImGui payload type identifier.
`payload`: Payload value.
`drawPreview`: Optional preview drawing callback.
`allowHoldToOpenOthers`: Whether hovering over another openable control may open it after the native drag-hold delay.
`TPayload`: Unmanaged payload type. | while the item is an active drag source. | `System.ArgumentException`: Thrown when is empty or whitespace. | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropSource(string payloadType, System.Func payloadFactory, System.Action? drawPreview = null, bool allowHoldToOpenOthers = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L85) | Publishes a lazily created unmanaged drag payload for the most recently submitted item.
`payloadType`: Stable ImGui payload type identifier.
`payloadFactory`: Creates the payload only after dragging starts.
`drawPreview`: Optional preview drawing callback.
`allowHoldToOpenOthers`: Whether hovering over another openable control may open it after the native drag-hold delay.
`TPayload`: Unmanaged payload type. | while the item is an active drag source. | `System.ArgumentException`: Thrown when is empty or whitespace.
`System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L123) | Accepts an unmanaged payload on the most recently submitted item.
`payloadType`: Stable ImGui payload type identifier.
`payload`: Delivered payload value.
`TPayload`: Unmanaged payload type. | only when a compatible payload is delivered. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload, out bool isPreviewing) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L150) | Accepts an unmanaged payload and reports its preview state on the most recently submitted item.
`payloadType`: Stable ImGui payload type identifier.
`payload`: Previewed or delivered payload value.
`isPreviewing`: Whether a compatible payload is hovering over the target.
`TPayload`: Unmanaged payload type. | only when a compatible payload is delivered. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, out TPayload payload, out bool isPreviewing, bool drawDefaultHighlight) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L181) | Accepts an unmanaged payload and controls whether ImGui draws its default target rectangle.
`payloadType`: Stable ImGui payload type identifier.
`payload`: Previewed or delivered payload value.
`isPreviewing`: Whether a compatible payload is hovering over the target.
`drawDefaultHighlight`: Whether ImGui draws its default target rectangle.
`TPayload`: Unmanaged payload type. | only when a compatible payload is delivered. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DragDropTarget(string payloadType, System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint targetId, out TPayload payload, out bool isPreviewing, bool drawDefaultHighlight = true) where TPayload : unmanaged`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs#L243) | Accepts an unmanaged payload on an explicit screen-space rectangle.
`payloadType`: Stable ImGui payload type identifier.
`minimum`: Minimum screen coordinate of the complete target.
`maximum`: Maximum screen coordinate of the complete target.
`targetId`: ImGui identifier unique within the current window.
`payload`: Previewed or delivered payload value.
`isPreviewing`: Whether a compatible payload is hovering over the target.
`drawDefaultHighlight`: Whether ImGui draws its default target rectangle.
`TPayload`: Unmanaged payload type. | only when a compatible payload is delivered. | `System.ArgumentException`: Thrown when is empty or whitespace. | +| [`public string? Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.editMergeKey { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L20) | Gets the current continuous-edit merge boundary, or null for a discrete edit. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginPropertyEdit(Inno.Editor.Interactions.IEditorHistory history, string path, string name)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L38) | Starts a History boundary only when a changed control is actively producing continuous input.
`history`: The authoritative domain History receiving the control's existing mutation records.
`path`: The stable owner-qualified property path.
`name`: The operation name shown by Undo and Redo. | — | `System.InvalidOperationException`: A previous gesture cannot complete or History rejects the transaction. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginEdit(Inno.Editor.Interactions.IEditorHistory history, string path, string name)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L62) | Begins or touches an explicitly captured continuous edit, such as a viewport manipulation.
`history`: The authoritative domain History.
`path`: The stable owner-qualified edit identity.
`name`: The user-facing Undo name. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.IsEditing(string path)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L92) | Checks whether this owner still owns a particular edit, including after a close or generation boundary.
`path`: The stable edit identity. | True only while the matching owner still has an active transaction. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompletePropertyEdit(string path)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L100) | Observes the current property after drawing, including release and cancellation without a new value sample.
`path`: The owner-qualified path passed to the property edit boundary. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CommitEdits()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L119) | Completes the current control edit before a document command, reload, Play transition or shutdown. | — | `System.InvalidOperationException`: A nested transaction prevents completion; ownership is retained. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CancelEdits()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs#L135) | Restores the control's initial domain values before discarding its transient presentation state. | — | `System.InvalidOperationException`: Rollback is unavailable; the gesture remains owned for recovery. | +| [`public Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TransformGizmo(Inno.Core.Mathematics.Matrix world, Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions options)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L146) | Draws a transform handle using stack-owned ABI buffers and restores native global style and masks before returning.
`world`: The current world matrix, copied into the native call.
`options`: The explicit viewport and interaction policy. | Copied output with no native pointer or domain object. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HelpBox(string text, string icon, System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L25) | Draws a wrapped contextual message with a semantic icon and a subdued status surface.
`text`: Literal message shown within the current content width.
`icon`: Icon-font glyph identifying the message severity.
`color`: Semantic accent for the icon, outline, and leading stripe. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CollectionSectionHeader(string title)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L75) | Draws a square collection-section band whose background, separator, and padding are shared with hierarchy and Inspector presentation.
`title`: Literal section title. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SectionLayout(System.Action drawContent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L110) | Draws content in a scope where consecutive section headers become framed fieldsets.
`drawContent`: Callback that draws all content participating in the section layout. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.isSectionContentVisible { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L135) | Gets whether content belonging to the current Inspector section should be drawn. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EnsureSection(string title = "Properties", string? description = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L153) | Ensures that subsequently drawn default properties belong to a framed section.
`title`: Title used only when the current section layout has not declared a section yet.
`description`: Optional hover explanation for the implicit section title. | when the current section accepts content; otherwise, when that section is collapsed. | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SectionHeader(string title, string? description = null, System.Action? drawLeadingControl = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs#L184) | Draws a section heading with hover-only description using the shared Inspector presentation.
`title`: Section title.
`description`: Optional hover explanation.
`drawLeadingControl`: Optional interactive control drawn before the section title. | when the section content should be drawn; otherwise, when the section is collapsed. | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.InlineRename(string id, ref string text, ref bool requestFocus, float rowHeight, nuint capacity = 512, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L44) | Draws a compact single-line rename editor inside an existing row.
`id`: The stable control identifier.
`text`: The mutable text buffer.
`requestFocus`: Whether keyboard focus and complete-value selection should be requested. The value remains until the input becomes active and its current contents are selected.
`rowHeight`: The height of the row area in which the field is centered.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The control width, or a negative value to fill the available space. | The current rename outcome. | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentOutOfRangeException`: Thrown when is not finite or is not positive. | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.isMixedValue { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs#L16) | Gets whether the currently drawn control group represents differing selected values. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.MixedValue(bool mixed, System.Action draw)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs#L27) | Draws one control group with the native mixed-value state, restoring the previous item flags on failure.
`mixed`: Whether the current selection contains different values.
`draw`: Synchronous control drawing; never retained. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PanelWindow(string title, ref bool isOpen, System.Action drawBody, Inno.Editor.ImGui.ImGuiWindowFlags flags = Inno.Editor.ImGui.ImGuiWindowFlags.NoCollapse, bool useWindowPadding = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L32) | Opens a standard panel window and executes panel body.
`title`: Window title.
`isOpen`: Visible state.
`drawBody`: Panel body callback.
`flags`: Window flags.
`useWindowPadding`: Whether the panel body should use the current standard window padding. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ConstrainedContent(string id, System.Action drawContent, bool useWindowPadding = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L112) | Draws a vertically auto-sized content region that is constrained to the current available width and cannot create an independent scroll range.
`id`: Stable identifier used by ImGui to track the content region.
`drawContent`: Callback that draws the complete region contents.
`useWindowPadding`: Whether the region should apply the centralized standard window padding exactly once. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HeaderSurface(string id, System.Action drawContent, bool spanWindowPadding = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L159) | Draws a square-cornered editor header surface using the shared target-header palette, border, and padding.
`id`: Stable identifier used by ImGui to track the header region.
`drawContent`: Callback that draws the complete header content.
`spanWindowPadding`: Whether the header should extend through the current parent window padding to both edges. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Hint(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs#L331) | Draws disabled hint text that wraps to the current content width.
`text`: Hint text. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyRow(string id, string label, System.Action drawValue, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L35) | Draws a two-column property row with a stable internal identifier.
`id`: Stable row identifier.
`label`: Human-readable property label.
`drawValue`: Value control callback.
`tooltip`: Optional hover help displayed from the property label. | — | `System.ArgumentException`: Thrown when is empty or whitespace.
`System.ArgumentNullException`: Thrown when or is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyRow(string id, System.Action drawLabel, System.Action drawValue)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L64) | Draws a two-column property row whose label is supplied by a custom presentation callback.
`id`: Stable row identifier.
`drawLabel`: Label presentation callback. It may wrap or draw badges inside the label column.
`drawValue`: Value control callback. | — | `System.ArgumentException`: Thrown when is empty or whitespace.
`System.ArgumentNullException`: Thrown when or is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetupPropertyColumns()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L100) | Configures the label and input columns of an active table with a shared two-to-three ratio. A following fixed-width action column may be added by the caller. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.PropertyLabel(string label, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L119) | Draws a label within its current table cell, wrapping when the column is narrow.
`label`: Visible label text.
`tooltip`: Optional explanation shown while the label is hovered. | — | `System.ArgumentNullException`: Thrown when is null. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.MetadataValue(string metadata, System.Action drawValue, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L146) | Draws a subdued metadata prefix followed by an interactive value on the same line.
`metadata`: Short contextual text such as a declared value type.
`drawValue`: Callback that draws the value or action following the metadata.
`tooltip`: Optional explanation displayed while the metadata is hovered. | — | `System.ArgumentNullException`: Thrown when or is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.MetadataValue(string metadata, string value, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L180) | Draws one wrapped, subdued value whose metadata and content remain in the value column.
`metadata`: Short contextual text such as a declared value type.
`value`: Literal value description shown after the metadata separator.
`tooltip`: Optional explanation displayed while the combined value is hovered. | — | `System.ArgumentNullException`: Thrown when or is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AxisDragFloat(string id, string axis, ref float value, float width, float speed = 0.1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L221) | Draws a float drag field with a compact colored axis prefix.
`id`: Stable control identifier.
`axis`: Axis label such as X, Y, Z, or W.
`value`: Mutable numeric value.
`width`: Total width including the axis prefix.
`speed`: Drag speed. | when the value changed. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactDragFloat(string label, ref float value, float speed = 0.1, float? minimum = null, float? maximum = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L254) | Draws a floating-point drag field with a compact one-decimal presentation and precise text editing.
`label`: Stable ImGui label and identifier.
`value`: Exact floating-point value to edit. Compact presentation never rounds the stored value.
`speed`: Drag speed.
`minimum`: Optional inclusive lower bound.
`maximum`: Optional inclusive upper bound. | when the exact value changed. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CompactSliderFloat(string label, ref float value, float minimum, float maximum)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L288) | Draws a bounded slider with compact presentation and a double-click precise input mode.
`label`: Stable ImGui label and identifier.
`value`: Exact floating-point value to edit. Compact presentation never rounds the stored value.
`minimum`: Inclusive lower bound.
`maximum`: Inclusive upper bound. | when the exact value changed. | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AxisDragInt(string id, string axis, ref int value, float width, float speed = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L328) | Draws an integer drag field with a compact colored axis prefix.
`id`: Stable control identifier.
`axis`: Axis label such as X, Y, Z, or W.
`value`: Mutable numeric value.
`width`: Total width including the axis prefix.
`speed`: Drag speed. | when the value changed. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Disabled(bool disabled, System.Action draw)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L349) | Draws content inside an ImGui disabled scope when requested.
`disabled`: Whether interaction is disabled.
`draw`: Drawing callback. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.InsertionLine(float fromX, float toX, float y)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L378) | Draws a horizontal insertion marker above normal window content in screen coordinates.
`fromX`: Marker start X coordinate.
`toX`: Marker end X coordinate.
`y`: Marker Y coordinate. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DropTargetHighlight(System.Numerics.Vector2 min, System.Numerics.Vector2 max)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L398) | Draws the standard yellow rectangular drag-and-drop target highlight above all normal window content in screen coordinates.
`min`: Minimum target coordinate.
`max`: Maximum target coordinate. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SearchInput(string id, string hint, ref string query, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L32) | Draws a single-line search field with a stable identifier.
`id`: The stable control identifier.
`hint`: The hint shown while the query is empty.
`query`: The mutable search query.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The control width, or a negative value to fill the available space. | when the query changed; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginSearchPopup(string id, ref string query, string hint, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L66) | Begins a popup and draws its focused search field.
`id`: The popup identifier previously supplied to ImGui.
`query`: The mutable search query.
`hint`: The hint shown while the query is empty.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The search field width, or a negative value to use the editor default. | when popup content should be drawn; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndSearchPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L89) | Ends a popup opened by . | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SelectTarget(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Core.Identity.IdentityObject target, System.Collections.Generic.IReadOnlyList visibleTargets)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L26) | Applies ordinary, additive or visible-range selection using the active keyboard modifiers.
`interactions`: The session's authoritative interaction service.
`target`: The freshly resolved clicked target.
`visibleTargets`: Current view order; collapsed or filtered items must be omitted. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo(string id, string preview, Inno.Editor.ImGui.ImGuiComboFlags flags = Inno.Editor.ImGui.ImGuiComboFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L37) | Begins a combo whose popup opens below the control and stays within its containing window.
`id`: Stable combo identifier in the current ImGui scope.
`preview`: Text displayed by the closed combo.
`flags`: Native combo presentation flags. Only is supported. | when the combo popup is open and its contents should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is null.
`System.ArgumentOutOfRangeException`: Thrown when contains a presentation mode unsupported by the editor selector. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L55) | Ends a combo opened by . | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuSelector(string id, string preview, float width, float minimumPopupWidth)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L84) | Draws a compact selector control and begins a work-area-bounded menu popup.
`id`: Stable selector identifier in the current ImGui scope.
`preview`: Text displayed by the closed selector.
`width`: Width reserved for the selector control.
`minimumPopupWidth`: Minimum outer width of the popup, including its window padding. | when the popup is open and its contents should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when or is not positive. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuSelector()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L122) | Ends a selector popup opened by . | — | — | +| [`public Inno.Editor.ImGui.EditorStyleMetrics Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.style { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs#L17) | Gets the centralized editor layout metrics shared by every widget and feature panel. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetupStyle()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs#L22) | Applies the centralized editor layout metrics and palette to the current ImGui context. | — | — | +| [`public float Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetIconCatalogWidth()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L21) | Measures the standard glyph column once per native frame in this UI session. | The widest visible standard glyph at the current font size, at least one text-line height. | — | +| [`public System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetGlyphVisualBounds(float fontSize, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L55) | Measures a glyph using the current font without exposing its native font handle.
`fontSize`: The positive baked font size.
`text`: A nonempty string whose first Unicode scalar identifies the glyph. | The glyph's visible left, top, right and bottom offsets. | `System.ArgumentException`: The glyph text is empty. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.AddGlyphCentered(float fontSize, string text, System.Numerics.Vector2 center, uint color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L78) | Centers a glyph in the current window without exposing a native draw list.
`fontSize`: The positive baked font size.
`text`: The glyph text.
`center`: The requested visible center in screen coordinates.
`color`: Packed text color. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableText(string id, string text, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L182) | Draws clickable text without a persistent or hovered background.
`id`: Stable identifier used by ImGui to track the interaction.
`text`: Visible text or icon glyph.
`tooltip`: Optional tooltip displayed while the interaction is hovered. | when the text is pressed. | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.HoverText(string id, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L205) | Draws a text link with no button background and highlights it only through text presentation.
`id`: Stable identifier used by ImGui to track the interaction.
`text`: Visible link text. | when the link is pressed. | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableIcon(string id, string icon, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L252) | Draws a clickable icon inside the same square interaction slot used by editor close controls. The icon has no resting background and changes only its glyph color while hovered or active.
`id`: Stable identifier used by ImGui to track the interaction.
`icon`: The visible icon glyph.
`tooltip`: Optional tooltip displayed while the interaction is hovered. | when the icon is pressed. | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClickableText(string id, string text, System.Numerics.Vector2 controlSize, string? tooltip = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L303) | Draws clickable text centered inside an explicitly sized transparent interaction area.
`id`: Stable identifier used by ImGui to track the interaction.
`text`: Visible text or icon glyph.
`controlSize`: Size of the transparent interaction area.
`tooltip`: Optional tooltip displayed while the interaction is hovered. | when the text is pressed. | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when either component of is not positive. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredText(string text, System.Numerics.Vector2 areaSize)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L347) | Draws non-interactive text centered inside a reserved layout area.
`text`: Visible text.
`areaSize`: Size of the layout area that contains the text. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when either component of is not positive. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CenteredWrappedText(string text, System.Numerics.Vector2 areaSize, System.Numerics.Vector2 padding)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L382) | Draws wrapped non-interactive text as a centered block inside a padded layout area.
`text`: The literal text to wrap and draw.
`areaSize`: The complete area reserved by the widget.
`padding`: The minimum horizontal and vertical distance retained from the area edges whenever space permits. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when an area component is not positive or a padding component is negative. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ColoredText(System.Numerics.Vector4 color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L433) | Draws unformatted text with a temporary foreground color.
`color`: The foreground color applied only while drawing the text.
`text`: The literal text to draw without format-string interpretation. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.WrappedText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L460) | Draws literal text wrapped to the remaining content width without using a native variadic formatting entry point.
`text`: The literal text to draw. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetClickableTextSize(string text, System.Numerics.Vector2 padding)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L493) | Calculates a clickable text area from visible text and requested inner padding.
`text`: Visible text whose dimensions determine the content size.
`padding`: Horizontal and vertical padding surrounding the text. | The complete transparent interaction area size. | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when either component of is negative. | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactClickableTextSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L514) | Gets the compact fixed interaction size used by icon-style clickable text. | A fixed-size area that follows the editor icon slot convention. | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactIconSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L527) | Gets the square icon interaction size shared by dock-header close controls and compact editor icons. | A square size based on the current ImGui font size. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.IconText(string icon, string text, bool highlight)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L560) | Draws icon and text with the icon's visible glyph bounds centered in a slot that expands when the glyph is wider than the normal editor icon slot.
`icon`: Icon text.
`text`: Main text.
`highlight`: Whether to underline and emphasize the drawn icon and text. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TreeNode(string id, System.Action onDraw, in Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions options)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L39) | Draws a full-width interactive tree row whose non-leaf content toggles expansion when double-clicked, while preserving single-click disclosure-arrow behavior.
`id`: Stable row identifier.
`onDraw`: Content drawing callback that receives the native row geometry established by ImGui.
`options`: Tree row options. | Interaction and row geometry for the submitted item. | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetNextTreeNodeOpen(bool open)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L138) | Overrides the retained expansion state of the next submitted non-leaf tree row.
`open`: Whether the next row should be expanded. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L14) | Chooses the transform handles rendered by the viewport presenter. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.Translate = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L19) | Renders translation handles. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.Rotate = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L23) | Renders rotation handles. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleX = 4`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L27) | Renders the X scale handle. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleY = 8`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L31) | Renders the Y scale handle. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleZ = 16`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L35) | Renders the Z scale handle. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.Scale = Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleX \| Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleY \| Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.ScaleZ`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L39) | Renders all local scale handles. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiWidget.TransformGizmoSpace : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L45) | Chooses how the manipulation axes are oriented. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoSpace.Local = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L50) | Uses the object's local orientation. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoSpace.World = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L54) | Uses the world's orientation. | — | — | +| [`public readonly record struct Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Supplies frame-local matrices and interaction policy without native handles. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.minimum { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L65) | Gets the viewport's screen-space minimum. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.maximum { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L69) | Gets the viewport's screen-space maximum. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.view { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L73) | Gets the column-vector view matrix. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.projection { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L77) | Gets the projection matrix supplied by the viewport owner. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.orthographic { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L81) | Gets whether this viewport uses an orthographic projection. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.enabled { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L85) | Gets whether manipulation can consume pointer input. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.operation { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L89) | Gets the requested handles. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.TransformGizmoSpace Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.space { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L93) | Gets the handle coordinate space. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.maskX { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L97) | Gets whether the X translation axis is excluded by the viewport plane. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.maskY { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L101) | Gets whether the Y translation axis is excluded by the viewport plane. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.maskZ { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L105) | Gets whether the Z translation axis is excluded by the viewport plane. | — | — | +| [`public override string Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.ToString()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.operator !=(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions left, Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.operator ==(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions left, Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions.Equals(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOptions other)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L60) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly record struct Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Returns copied manipulation output; the domain owner decides how to apply and record it.
`matrix`: The resulting world matrix.
`changed`: Whether the native presenter reported an edited value.
`isUsing`: Whether the handle currently captures the pointer.
`isOver`: Whether the pointer is over a handle. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.TransformGizmoResult(Inno.Core.Mathematics.Matrix matrix, bool changed, bool isUsing, bool isOver)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Returns copied manipulation output; the domain owner decides how to apply and record it.
`matrix`: The resulting world matrix.
`changed`: Whether the native presenter reported an edited value.
`isUsing`: Whether the handle currently captures the pointer.
`isOver`: Whether the pointer is over a handle. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.matrix { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L124) | The resulting world matrix. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.changed { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L125) | Whether the native presenter reported an edited value. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.isUsing { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L126) | Whether the handle currently captures the pointer. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.isOver { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L127) | Whether the pointer is over a handle. | — | — | +| [`public override string Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.ToString()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.operator !=(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult left, Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.operator ==(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult left, Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult right)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.Equals(Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult other)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.TransformGizmoResult.Deconstruct(out Inno.Core.Mathematics.Matrix matrix, out bool changed, out bool isUsing, out bool isOver)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L123) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L141) | Describes the view-owned geometry used to present an active inline rename action. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.InlineRenamePresentation(string id, float width, float rowHeight, nuint bufferSize = 512)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L165) | Creates inline rename presentation data.
`id`: The stable ImGui identifier used by the input field.
`width`: The requested input width in logical pixels.
`rowHeight`: The height of the row area in which the field is centered.
`bufferSize`: The maximum UTF-8 buffer size accepted by the input. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentOutOfRangeException`: Thrown when or is not finite and positive, or when is zero. | +| [`public string Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.id { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L197) | Gets the stable ImGui identifier used by the input field. | — | — | +| [`public float Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.width { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L201) | Gets the requested input width in logical pixels. | — | — | +| [`public float Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L205) | Gets the height of the row area in which the input field is centered. | — | — | +| [`public nuint Inno.Editor.ImGui.ImGuiWidget.InlineRenamePresentation.bufferSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L209) | Gets the maximum UTF-8 buffer size accepted by the input. | — | — | +| [`public enum Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L215) | Describes the outcome of an inline rename control. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L220) | Indicates that the interaction remains active. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Commit = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L224) | Indicates that the edited text should be committed. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.FocusLost = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L228) | Indicates that the input lost focus and its valid value should be committed before closing. | — | — | +| [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Cancel = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L232) | Indicates that the edited text should be discarded. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L335) | Provides the native geometry of a tree row while its custom content is being drawn. | — | — | +| [`public float Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L345) | Gets the actual native row height established for the current tree node. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L351) | Configures an interactive tree row. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.selected { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L356) | Gets whether the row is selected. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.isLeaf { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L360) | Gets whether the row has no expandable children. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.showBackground { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L364) | Gets whether a custom background is drawn behind an unselected row. | — | — | +| [`public System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.backgroundColor { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L368) | Gets the custom background color used when is enabled. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.suppressHoverHighlight { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L372) | Gets whether the row keeps its configured background while hovered. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.hideGuideLines { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L376) | Gets whether ancestor and branch guide lines are omitted for this row. | — | — | +| [`public System.Action? Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.drawViewportOverlay { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L381) | Gets the callback that draws controls fixed to the current viewport without extending the tree's horizontal content boundary. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L387) | Describes interaction state produced by a tree row. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isOpen { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L410) | Gets whether child content should be rendered. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L414) | Gets whether the content row was clicked. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isDoubleClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L419) | Gets whether the content row was double-clicked. Double-clicking a non-leaf content row also toggles its retained expansion state for the next frame. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L423) | Gets whether the full row is hovered. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.min { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L427) | Gets the row minimum screen coordinate. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.max { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L431) | Gets the row maximum screen coordinate. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.contentMin { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L435) | Gets the minimum screen coordinate of the row's interactive content, excluding tree indentation. | — | — | ## 项目依赖 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):公开引用边界由实际签名核对。 -- [Inno.Core.Events](../core/Inno.Core.Events.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Native.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Native.ImGuizmo` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Presentation.ImGui.Sdl3` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Core.Events` | 公开签名依赖 | +| `Inno.Core.Identity` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Core.Mathematics` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Inspection.md b/docs/editor/Inno.Editor.Inspection.md index 4f1559567..712327d49 100644 --- a/docs/editor/Inno.Editor.Inspection.md +++ b/docs/editor/Inno.Editor.Inspection.md @@ -54,6 +54,7 @@ public sealed class TextAssetDrawer : InspectionDrawer ```csharp using Inno.Editor.Inspection; +using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; public readonly record struct AuthoringWeight(float value); @@ -64,7 +65,7 @@ public sealed class AuthoringWeightDrawer : IPropertyDrawer public void Draw(PropertyDrawContext context) { float value = context.GetValue() is AuthoringWeight current ? current.value : 0f; - if (ImGuiWidget.CompactSliderFloat($"##{context.path}", ref value, 0f, 1f)) + if (EditorUiSession.current.widgets.CompactSliderFloat($"##{context.path}", ref value, 0f, 1f)) context.SetValue(new AuthoringWeight(value)); } } @@ -72,7 +73,7 @@ public sealed class AuthoringWeightDrawer : IPropertyDrawer PropertyDrawer 通过 declared property type 匹配。`PropertyDrawContext.SetValue` 会把修改交给所属 feature 的 edit service,由它写入中立的 Undo/Redo payload;Drawer 不应该绕过 context 直接修改 serialized owner。`PropertyDrawContext.tooltip` 暴露已经组合完成的 hover help;bool 与 nullable 内建 Drawer 会把它同时绑定到 checkbox 本身,因此 label 和 control 都能解释同一属性。 -`SerializedPropertyRenderer` 本身不依赖 Scene。创建 renderer 的 feature 提供 `IInspectionPropertyEditService`,负责把通用的 owner、root property 与 mutation 转换成自己的 Undo/Redo 协议。Inspector Panel 使用 Scene adapter;未来 Material、Animation 或 RenderGraph 检查器可以使用各自的 history adapter,而不用把 Scene 引入通用 Inspection 项目。 +`SerializedPropertyRenderer` 本身不依赖 Scene。创建 renderer 的 feature 提供 `IInspectionPropertyEditService`,负责把通用的 owner、root property 与 mutation 转换成自己的 Undo/Redo 协议。Inspector Panel 使用 Scene adapter;Material、Pipeline 和插件检查器使用各自的 history adapter,而不用把 Scene 引入通用 Inspection 项目。 ## Inspector presentation attributes @@ -133,7 +134,7 @@ Inspector 的浮点标量以及 `Vector2/3/4`、`Rect`、Quaternion Euler 和 Tr ## Feature 间 presentation 契约 -`IInspectionIconProvider` 用于共享目标所属 feature 的图标规则,而不引入 Panel→Panel 引用。例如 FileBrowser 的 `AssetEditorModule` 实现 `IInspectionIconProvider`,它自己的 `AssetSelectionInspectionDrawer` 因而可以复用同一个 Asset icon registry。Inspector 组合根只依赖该接口,不引用 FileBrowser 项目。Scene/GameObject Drawer 直接注入 `EditorSettings`,通过原始完整路径读取与 Hierarchy、Asset Browser 一致的 icon object;Settings 项目不提供 icon resolver。 +`IInspectionIconProvider` 用于共享目标所属 feature 的图标规则,而不引入 Panel→Panel 引用。例如 FileBrowser 的 `AssetEditingModule` 实现 `IInspectionIconProvider`,它自己的 `AssetSelectionInspectionDrawer` 因而可以复用同一个 Asset icon registry。Inspector 组合根只依赖该接口,不引用 FileBrowser 项目。Scene/GameObject Drawer 直接注入 `EditorSettings`,通过原始完整路径读取与 Hierarchy、Asset Browser 一致的 icon object;Settings 项目不提供 icon resolver。 ## Scripting API @@ -141,76 +142,35 @@ EditorScripts 通过显式 `using InnoEditor.Inspection;` 使用裁剪后的 `In ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Inspection.AssetInspectionSelection` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.AssetInspectionSelection.AssetInspectionSelection(System.Collections.Generic.IEnumerable assetIds)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs#L18) | Captures unique persistent identities in selection order.
`assetIds`:Nonempty identities; the last identity is the primary selection. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Inspection.AssetInspectionSelection.assetIds { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs#L29) | Gets stable asset identities in selection order. | — | — | -| [`public System.Guid Inno.Editor.Inspection.AssetInspectionSelection.primaryAssetId { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs#L33) | Gets the most recently selected asset identity. | — | — | -| [`public sealed class Inno.Editor.Inspection.AssetInspectionSelection`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs#L10) | Represents an ordered multi-asset selection without retaining asset or plugin-generation objects. | — | — | - -### `Inno.Editor.Inspection.IInspectionDrawer` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`(string name, System.Action? setter) Inno.Editor.Inspection.IInspectionDrawer.BindName(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L159) | Binds the target name displayed by the Inspector target header.
返回:The current target name and a callback that accepts a requested replacement name, or null for the callback when the name is read-only.
`context`:The active Inspector drawing context. | — | — | -| [`bool Inno.Editor.Inspection.IInspectionDrawer.CanInspect(object target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L136) | Determines whether a type-compatible selected value can be inspected by this drawer.
返回:Whether this drawer accepts the value; ordinary type-only drawers accept by default.
`target`:Selected value, never mutated by this query. | — | — | -| [`void Inno.Editor.Inspection.IInspectionDrawer.Draw(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L175) | Draws the target-specific Inspector body.
`context`:The active Inspector drawing context. | — | — | -| [`void Inno.Editor.Inspection.IInspectionDrawer.DrawHeader(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L167) | Draws the target-specific second header row.
`context`:The active Inspector drawing context. | — | — | -| [`string Inno.Editor.Inspection.IInspectionDrawer.GetIcon(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L147) | Gets the icon glyph displayed in the Inspector target header.
返回:The icon glyph for the current target.
`context`:The active Inspector drawing context. | — | — | -| [`public interface Inno.Editor.Inspection.IInspectionDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L125) | Defines the runtime bridge used to render a discovered inspection drawer. | — | — | - -### `Inno.Editor.Inspection.IInspectionIconProvider`1` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`string Inno.Editor.Inspection.IInspectionIconProvider.GetIcon(TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/IInspectionIconProvider.cs#L20) | Resolves the presentation icon for one inspected target.
返回:The icon glyph registered by the owning feature.
`target`:The target whose icon should be resolved. | — | — | -| [`public interface Inno.Editor.Inspection.IInspectionIconProvider`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/IInspectionIconProvider.cs#L9) | Resolves a presentation icon for inspected targets owned by another editor feature. | — | — | - -### `Inno.Editor.Inspection.IInspectionPropertyEditService` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`bool Inno.Editor.Inspection.IInspectionPropertyEditService.ChangeProperty(object owner, string propertyName, System.Action mutation, string historyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs#L36) | Applies a property mutation and records it when the owning feature observes a value change.
返回:true when the value changed and the owning feature recorded the edit; otherwise, false.
`owner`:The domain object that owns the root serialized property.
`propertyName`:The exact root serialized member name.
`mutation`:The callback that assigns the requested value.
`historyName`:The user-facing name for the resulting history entry. | — | `System.ArgumentNullException`:Thrown when owner or mutation is null.
`System.ArgumentException`:Thrown when propertyName or historyName is empty. | -| [`public interface Inno.Editor.Inspection.IInspectionPropertyEditService`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs#L8) | Applies one root serialized-property mutation through the history mechanism owned by a feature. | — | — | - -### `Inno.Editor.Inspection.IInspectorAttributeDrawer` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.DrawAfter(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L34) | Draws a decorator immediately after the property row.
`context`:Current presentation state for the annotated property. | — | — | -| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.DrawBefore(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L24) | Draws a decorator immediately before the property row.
`context`:Current presentation state for the annotated property. | — | — | -| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.Update(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L14) | Updates visibility, interactivity, labeling, help, or numeric constraints before layout.
`context`:Mutable presentation state for the annotated property. | — | — | -| [`public interface Inno.Editor.Inspection.IInspectorAttributeDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L6) | Extends serialized-property presentation with reusable attribute-driven behavior. | — | — | - -### `Inno.Editor.Inspection.InspectionDrawContext` - + + + + + + + + + + + + + + + + + + + + + + + @@ -221,22 +181,6 @@ EditorScripts 通过显式 `using InnoEditor.Inspection;` 使用裁剪后的 `In - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Inspection.InspectionDrawContext.DrawProperties()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L128) | Draws every runtime-visible serialized property through the shared attribute and property drawer pipeline. | Properties without an explicit Header attribute are placed in the shared implicit Properties section. Custom drawers may call this method before or after their own sections. | — | -| [`public void Inno.Editor.Inspection.InspectionDrawContext.DrawProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L110) | Draws one named property with its ordinary editor, history, and validation behavior.
`propertyName`:Exact serialized property name. | — | — | -| [`public T Inno.Editor.Inspection.InspectionDrawContext.GetProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L94) | Reads one runtime-visible serialized property from the target.
返回:The current strongly typed property value.
`propertyName`:Exact serialized property name. | — | — | -| [`public bool Inno.Editor.Inspection.InspectionDrawContext.HasProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L75) | Gets whether the target exposes one runtime-visible serialized property.
返回:true when the property is available.
`propertyName`:Exact serialized property name. | — | — | -| [`public bool Inno.Editor.Inspection.InspectionDrawContext.TryDrawInline(object target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L48) | Draws an exact target-specific body inside the caller's existing Inspector card.
返回:true when an exact drawer rendered the target.
`target`:Nested target whose exact registered drawer should render. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Inspection.InspectionDrawContext.editorContext { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L22) | Gets the shared editor context. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Inspection.InspectionDrawContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L27) | Gets the active editor interaction entry point. | — | — | -| [`public Inno.Editor.Inspection.SerializedPropertyRenderer Inno.Editor.Inspection.InspectionDrawContext.properties { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L37) | Gets the serialized property renderer. | — | — | -| [`public object Inno.Editor.Inspection.InspectionDrawContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L32) | Gets the selected target. | — | — | -| [`public sealed class Inno.Editor.Inspection.InspectionDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L15) | Provides services and state to an inspector drawer. | — | — | - -### `Inno.Editor.Inspection.InspectionDrawer`1` - @@ -245,140 +189,32 @@ EditorScripts 通过显式 `using InnoEditor.Inspection;` 使用裁剪后的 `In - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Inspection.InspectionDrawer.InspectionDrawer()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L11) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract (string name, System.Action? setter) Inno.Editor.Inspection.InspectionDrawer.BindName(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L45) | Binds the name displayed in the first row of the Inspector target header.
返回:The current target name and a callback that accepts a requested replacement name, or null for the callback when the name is read-only.
`context`:The active Inspector drawing context.
`target`:The strongly typed target being inspected. | — | — | -| [`protected virtual bool Inno.Editor.Inspection.InspectionDrawer.CanInspect(TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L28) | Determines whether this drawer handles the selected value, in addition to its registered type.
返回:Whether this drawer accepts the target.
`target`:Current target. The check must not mutate it. | — | — | -| [`protected abstract void Inno.Editor.Inspection.InspectionDrawer.Draw(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L95) | Draws the target-specific Inspector body below the shared target header.
`context`:The active Inspector drawing context.
`target`:The strongly typed target being inspected. | — | — | -| [`protected virtual void Inno.Editor.Inspection.InspectionDrawer.DrawHeader(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L80) | Draws target-specific controls in the second row of the Inspector target header.
`context`:The active Inspector drawing context.
`target`:The strongly typed target being inspected. | The callback is clipped to one header row. Implementations should keep all controls on the current line and must not begin another window, child, table, or popup. | — | -| [`protected virtual string Inno.Editor.Inspection.InspectionDrawer.GetIcon(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L62) | Resolves the icon glyph displayed for the current target.
返回:The target-specific icon glyph. The default implementation returns Inno.Editor.Inspection.InspectionDrawer`1.icon.
`context`:The active Inspector drawing context.
`target`:The strongly typed target being inspected. | — | — | -| [`public abstract string Inno.Editor.Inspection.InspectionDrawer.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L17) | Gets the icon glyph displayed in the large leading slot of the Inspector target header. | — | — | -| [`public abstract class Inno.Editor.Inspection.InspectionDrawer where TTarget : class : Inno.Editor.Inspection.IInspectionDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L11) | Provides the target-specific header presentation and body drawing behavior for an inspected target. | — | — | - -### `Inno.Editor.Inspection.InspectionDrawerAttribute` - + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.InspectionDrawerAttribute.InspectionDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0, bool conditional = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L49) | Creates an inspector drawer registration.
`targetType`:The selected object type handled by the drawer.
`useForChildren`:Whether the drawer may handle assignable derived target types.
`priority`:The tie-breaking priority after exactness and inheritance distance.
`conditional`:Whether CanInspect is a target-dependent predicate. Equally specific accepted peers remain an explicit ambiguity error. | — | `System.ArgumentNullException`:Thrown when targetType is null. | -| [`public bool Inno.Editor.Inspection.InspectionDrawerAttribute.conditional { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L29) | Gets whether CanInspect selects a subset of targets, allowing peer conditional registrations at the same priority. | — | — | -| [`public int Inno.Editor.Inspection.InspectionDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L24) | Gets the tie-breaking registration priority. | — | — | -| [`public System.Type Inno.Editor.Inspection.InspectionDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L14) | Gets the selected target type handled by the drawer. | — | — | -| [`public bool Inno.Editor.Inspection.InspectionDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L19) | Gets whether derived target types are accepted. | — | — | -| [`public sealed class Inno.Editor.Inspection.InspectionDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L9) | Associates an inspector drawer with a selected target type. | — | — | - -### `Inno.Editor.Inspection.InspectionDrawerFactory` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.InspectionDrawerFactory.InspectionDrawerFactory(object @object, nint method)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public virtual System.IAsyncResult Inno.Editor.Inspection.InspectionDrawerFactory.BeginInvoke(System.Type drawerType, System.AsyncCallback callback, object @object)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | — | — | — | -| [`public virtual Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory.EndInvoke(System.IAsyncResult result)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | — | — | — | -| [`public virtual Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory.Invoke(System.Type drawerType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Creates one discovered inspection drawer through the owning editor composition root.
返回:A new drawer instance whose runtime type is drawerType.
`drawerType`:The concrete drawer type selected from the active type catalog. | — | `System.ArgumentNullException`:Implementations may throw when drawerType is null. | -| [`public delegate Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory(System.Type drawerType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Creates one discovered inspection drawer through the owning editor composition root.
返回:A new drawer instance whose runtime type is drawerType.
`drawerType`:The concrete drawer type selected from the active type catalog. | — | `System.ArgumentNullException`:Implementations may throw when drawerType is null. | - -### `Inno.Editor.Inspection.InspectionDrawerRegistry` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.InspectionDrawerRegistry.InspectionDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Inspection.InspectionDrawerFactory factory, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L41) | Creates a generation-aware inspection drawer registry.
`interactions`:The active editor interaction entry point exposed to draw contexts.
`factory`:The composition-root factory used to construct discovered drawer types and resolve their module-specific dependencies.
`types`:The host-owned type catalog that coordinates drawer generations.
`serialization`:Serialization registry used to expose the selected target's visible properties to custom drawers. | — | `System.ArgumentNullException`:Thrown when interactions or factory is null. | -| [`public void Inno.Editor.Inspection.InspectionDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L161) | Releases every active drawer snapshot and unregisters the registry from type refreshes. | — | — | -| [`public bool Inno.Editor.Inspection.InspectionDrawerRegistry.TryResolve(Inno.Editor.Core.EditorContext editorContext, object target, Inno.Editor.Inspection.SerializedPropertyRenderer renderer, out Inno.Editor.Inspection.IInspectionDrawer? drawer, out Inno.Editor.Inspection.InspectionDrawContext? context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L76) | Resolves the most specific registered drawer and creates its drawing context.
返回:true when a matching drawer was resolved.
`editorContext`:The shared editor context exposed to the selected drawer.
`target`:The selected object whose runtime type determines the drawer.
`renderer`:The serialized property renderer exposed to the selected drawer.
`drawer`:The resolved drawer when the method succeeds.
`context`:The target-specific drawing context when the method succeeds. | — | `System.ArgumentNullException`:Thrown when editorContext, target, or renderer is null. | -| [`public bool Inno.Editor.Inspection.InspectionDrawerRegistry.TryResolveExact(Inno.Editor.Core.EditorContext editorContext, object target, Inno.Editor.Inspection.SerializedPropertyRenderer renderer, out Inno.Editor.Inspection.IInspectionDrawer? drawer, out Inno.Editor.Inspection.InspectionDrawContext? context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L127) | Resolves only a drawer explicitly registered for the target's exact runtime type.
返回:true when an exact registration exists; inherited and fallback drawers are ignored.
`editorContext`:The shared editor context exposed to the selected drawer.
`target`:The selected object whose exact runtime type determines the drawer.
`renderer`:The serialized property renderer exposed to the selected drawer.
`drawer`:The exact drawer when the method succeeds.
`context`:The target-specific drawing context when the method succeeds. | — | `System.ArgumentNullException`:Thrown when editorContext, target, or renderer is null. | -| [`public sealed class Inno.Editor.Inspection.InspectionDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L16) | Discovers and resolves inspection drawers through the active type catalog. | — | — | - -### `Inno.Editor.Inspection.InspectorAttributeDrawContext` - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public object? Inno.Editor.Inspection.InspectorAttributeDrawContext.GetSiblingValue(string memberName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L99) | Reads a sibling field or property from the current owner, including non-public base members.
返回:The current sibling value.
`memberName`:Exact CLR member name. | — | `System.InvalidOperationException`:Thrown when the sibling is absent, ambiguous, indexed, or unreadable. | -| [`public System.Attribute Inno.Editor.Inspection.InspectorAttributeDrawContext.attribute { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L55) | Gets the attribute currently invoking a drawer. | — | — | -| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawContext.isReadOnly { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L65) | Gets or sets whether the property control is read-only in the Inspector. | — | — | -| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawContext.isVisible { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L60) | Gets or sets whether the complete property row is visible. | — | — | -| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.label { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L70) | Gets or sets the visible property label. | — | — | -| [`public double? Inno.Editor.Inspection.InspectorAttributeDrawContext.maximum { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L85) | Gets or sets an optional inclusive numeric upper bound. | — | — | -| [`public System.Reflection.MemberInfo Inno.Editor.Inspection.InspectorAttributeDrawContext.member { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L40) | Gets the reflected field or property carrying the current attribute. | — | — | -| [`public double? Inno.Editor.Inspection.InspectorAttributeDrawContext.minimum { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L80) | Gets or sets an optional inclusive numeric lower bound. | — | — | -| [`public object Inno.Editor.Inspection.InspectorAttributeDrawContext.owner { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L35) | Gets the object that owns the annotated member. | — | — | -| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L50) | Gets the stable Inspector control path. | — | — | -| [`public Inno.Core.Serialization.SerializedProperty? Inno.Editor.Inspection.InspectorAttributeDrawContext.property { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L45) | Gets the root serialized property when one is available. | — | — | -| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.tooltip { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L75) | Gets or sets optional hover help for the property label. | — | — | -| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L10) | Carries the mutable presentation state of one attribute-annotated Inspector property. | — | — | - -### `Inno.Editor.Inspection.InspectorAttributeDrawerAttribute` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.InspectorAttributeDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L23) | Creates an Inspector attribute drawer registration.
`targetType`:Attribute type handled by the drawer.
`useForChildren`:Whether derived attribute types are accepted.
`priority`:Tie-breaking priority after inheritance distance. | — | — | -| [`public int Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L49) | Gets the registration priority. | — | — | -| [`public System.Type Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L39) | Gets the handled attribute type. | — | — | -| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L44) | Gets whether derived attribute types are accepted. | — | — | -| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L9) | Associates an Inspector attribute drawer with an attribute type. | — | — | - -### `Inno.Editor.Inspection.InspectorAttributeDrawerRegistry` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.InspectorAttributeDrawerRegistry.InspectorAttributeDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, System.Collections.Generic.IEnumerable drawerServices)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L33) | Creates an Inspector attribute drawer registry.
`interactions`:Active editor interaction entry point.
`types`:Host-owned type catalog.
`serialization`:Active serialization registry.
`drawerServices`:Additional constructor-injected drawer services. | — | — | -| [`public void Inno.Editor.Inspection.InspectorAttributeDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L88) | Releases active drawer generations. | — | — | -| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L14) | Discovers generation-aware Inspector attribute drawers and applies them in declaration order. | — | — | - -### `Inno.Editor.Inspection.IPropertyDrawer` - + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Inspection.IPropertyDrawer.Draw(Inno.Editor.Inspection.PropertyDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IPropertyDrawer.cs#L14) | Draws a property through its encapsulated value accessors.
`context`:Property drawing context. | — | — | -| [`public interface Inno.Editor.Inspection.IPropertyDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IPropertyDrawer.cs#L6) | Draws and optionally edits one serialized property value. | — | — | - -### `Inno.Editor.Inspection.PropertyDrawContext` - @@ -395,93 +231,148 @@ EditorScripts 通过显式 `using InnoEditor.Inspection;` 使用裁剪后的 `In - + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.ClearTextState(string key)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L164) | Removes text-edit state for this renderer, inspected owner, and property path.
`key`:Drawer-local state identity within the current property path. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L235) | Draws a nested serialized property.
`property`:Nested property. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(object metadataOwner, Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L246) | Draws a nested serialized property with presentation metadata resolved from its immediate owner.
`metadataOwner`:Object that declares the nested serialized member.
`property`:Nested property. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(string childName, System.Type childType, System.Func getter, System.Action setter, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L203) | Draws a nested value whose setter writes through this property path.
`childName`:The stable child member name appended to this property path.
`childType`:The declared type used to resolve a property drawer.
`getter`:The callback that reads the latest child value.
`setter`:The callback that writes an edited child value.
`readOnly`:Whether the child should be presented without assignment support. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawInlineChild(string childName, System.Type childType, System.Func getter, System.Action setter, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L315) | Draws a nested value inline without adding another label row.
`childName`:The stable child member name appended to this property path.
`childType`:The declared type used to resolve a property drawer.
`getter`:The callback that reads the latest child value.
`setter`:The callback that writes an edited child value.
`readOnly`:Whether the child should be presented without assignment support. | — | — | -| [`public object? Inno.Editor.Inspection.PropertyDrawContext.GetValue()`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L125) | Gets the latest value.
返回:The latest value returned by the serialized property's getter. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.SetTextState(string key, string value)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L153) | Stores text-edit state scoped to this renderer, inspected owner, and property path.
`key`:Drawer-local state identity within the current property path.
`value`:Current neutral text state to retain between frames. | — | — | -| [`public void Inno.Editor.Inspection.PropertyDrawContext.SetValue(object? value)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L172) | Assigns a value when the property is writable.
`value`:New value. | — | — | -| [`public bool Inno.Editor.Inspection.PropertyDrawContext.TryGetTextState(string key, out string? value)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L139) | Tries to read text-edit state scoped to this renderer, inspected owner, and property path.
返回:true when text state exists for the exact owner and property path.
`key`:Drawer-local state identity within the current property path.
`value`:Receives the stored text when the state exists. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Inspection.PropertyDrawContext.editorContext { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L25) | Gets the shared editor context. | — | — | -| [`public bool Inno.Editor.Inspection.PropertyDrawContext.hdrColor { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L75) | Gets whether Color values are unclamped linear floating-point channels. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Inspection.PropertyDrawContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L30) | Gets the active editor interaction entry point. | — | — | -| [`public bool Inno.Editor.Inspection.PropertyDrawContext.isReadOnly { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L55) | Gets whether assignments are disabled. | — | — | -| [`public string Inno.Editor.Inspection.PropertyDrawContext.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L40) | Gets the display label. | — | — | -| [`public double? Inno.Editor.Inspection.PropertyDrawContext.maximum { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L65) | Gets the optional inclusive numeric upper bound supplied by Inspector metadata. | — | — | -| [`public double? Inno.Editor.Inspection.PropertyDrawContext.minimum { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L60) | Gets the optional inclusive numeric lower bound supplied by Inspector metadata. | — | — | -| [`public string Inno.Editor.Inspection.PropertyDrawContext.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L35) | Gets the stable control path. | — | — | -| [`public System.Type Inno.Editor.Inspection.PropertyDrawContext.propertyType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L45) | Gets the declared property type. | — | — | -| [`public string? Inno.Editor.Inspection.PropertyDrawContext.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L70) | Gets optional hover help supplied by Inspector metadata. | — | — | -| [`public Inno.Core.Serialization.PropertyVisibility Inno.Editor.Inspection.PropertyDrawContext.visibility { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L50) | Gets the serialization visibility flags. | — | — | -| [`public sealed class Inno.Editor.Inspection.PropertyDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L12) | Encapsulates one editable property path and its drawing services. | — | — | - -### `Inno.Editor.Inspection.PropertyDrawerAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.PropertyDrawerAttribute.PropertyDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L41) | Creates a property drawer registration.
`targetType`:The declared serialized property type handled by the drawer.
`useForChildren`:Whether the drawer may handle assignable derived property types.
`priority`:The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`:Thrown when targetType is null. | -| [`public int Inno.Editor.Inspection.PropertyDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L24) | Gets the tie-breaking registration priority. | — | — | -| [`public System.Type Inno.Editor.Inspection.PropertyDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L14) | Gets the declared property type handled by the drawer. | — | — | -| [`public bool Inno.Editor.Inspection.PropertyDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L19) | Gets whether assignable derived types are accepted. | — | — | -| [`public sealed class Inno.Editor.Inspection.PropertyDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L9) | Associates a property drawer with a declared property type. | — | — | - -### `Inno.Editor.Inspection.PropertyDrawerRegistry` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.PropertyDrawerRegistry.PropertyDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, System.Collections.Generic.IEnumerable drawerServices)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L37) | Creates a generation-aware property drawer registry.
`interactions`:The active editor interaction entry point available to property drawer constructors.
`types`:The host-owned type catalog that coordinates drawer generations.
`serialization`:The serialization registry available to structured-object drawers.
`drawerServices`:Additional generation-bound services available to feature-specific drawer constructors. | — | `System.ArgumentNullException`:Thrown when interactions is null. | -| [`public void Inno.Editor.Inspection.PropertyDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L67) | Releases every active property drawer snapshot and unregisters the registry from type refreshes. | — | — | -| [`public Inno.Editor.Inspection.IPropertyDrawer Inno.Editor.Inspection.PropertyDrawerRegistry.Resolve(System.Type propertyType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L58) | Resolves the most specific drawer for a declared property type.
返回:The most specific registered drawer, or the unsupported-value fallback.
`propertyType`:The declared serialized property type to resolve. | — | `System.ArgumentNullException`:Thrown when propertyType is null. | -| [`public sealed class Inno.Editor.Inspection.PropertyDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L15) | Discovers and resolves serialized property drawers through the active type catalog. | — | — | - -### `Inno.Editor.Inspection.SerializedPropertyRenderer` - + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Inspection.SerializedPropertyRenderer.SerializedPropertyRenderer(Inno.Editor.Inspection.PropertyDrawerRegistry drawers, Inno.Editor.Inspection.InspectorAttributeDrawerRegistry attributes, Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Inspection.IInspectionPropertyEditService edits, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L54) | Creates a serialized property renderer over one drawer registry and feature-owned edit service.
`drawers`:The property drawer registry used for runtime type resolution.
`attributes`:The generation-aware registry that interprets Inspector presentation attributes.
`interactions`:The active editor interaction entry point.
`edits`:The feature-owned service used to apply and record property changes.
`logs`:The host-owned log router used to report isolated drawer failures. | — | `System.ArgumentNullException`:Thrown when drawers, interactions, or edits is null. | -| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.Draw(Inno.Editor.Core.EditorContext editorContext, object owner, string ownerPath, Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L84) | Draws a root serialized property.
`editorContext`:Shared editor context.
`owner`:The live domain object that owns the root property.
`ownerPath`:Stable owner path.
`property`:Serialized property. | — | — | -| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.DrawDraftProperty(Inno.Editor.Core.EditorContext editorContext, object stateOwner, object valueOwner, string ownerPath, Inno.Core.Serialization.SerializedProperty property, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L195) | Draws a detached serialized property with its attributes and a feature-owned draft transaction.
`editorContext`:Current Editor frame.
`stateOwner`:Stable host-owned presentation state, containing no collectible objects.
`valueOwner`:Invocation-local detached value declaring the property and attributes.
`ownerPath`:Stable document or settings path.
`property`:Detached native serialized property.
`edits`:Draft mutation and shared History boundary.
`readOnly`:Whether source ownership prohibits edits. | — | — | -| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.DrawValue(Inno.Editor.Core.EditorContext editorContext, object owner, string path, string label, System.Type propertyType, System.Func getter, System.Action setter, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly = false, bool hdrColor = false, string? tooltip = null, double? minimum = null, double? maximum = null)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L150) | Draws a declared authoring value with existing property drawers and a feature-owned draft edit service.
`editorContext`:Current Editor frame.
`owner`:Stable owner for text state and edit routing; not necessarily a canonical serialized object.
`path`:Stable unique property path.
`label`:User-facing label.
`propertyType`:Declared value type used to select its registered drawer.
`getter`:Reads the detached value.
`setter`:Changes the detached value within the supplied edit service.
`edits`:Feature-owned transaction boundary, propagated to nested properties.
`readOnly`:Whether assignment is disabled.
`hdrColor`:Whether Color values use unclamped linear floating-point channels.
`tooltip`:Optional authoring description rendered by the shared property tooltip.
`minimum`:Optional inclusive numeric editing bound.
`maximum`:Optional inclusive numeric editing bound. | — | — | -| [`public sealed class Inno.Editor.Inspection.SerializedPropertyRenderer`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L21) | Resolves drawers and renders serialized property paths with isolated error handling. | — | — | +| [`public interface Inno.Editor.Inspection.IInspectorAttributeDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L6) | Extends serialized-property presentation with reusable attribute-driven behavior. | — | — | +| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.Update(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L14) | Updates visibility, interactivity, labeling, help, or numeric constraints before layout.
`context`: Mutable presentation state for the annotated property. | — | — | +| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.DrawBefore(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L24) | Draws a decorator immediately before the property row.
`context`: Current presentation state for the annotated property. | — | — | +| [`void Inno.Editor.Inspection.IInspectorAttributeDrawer.DrawAfter(Inno.Editor.Inspection.InspectorAttributeDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/IInspectorAttributeDrawer.cs#L34) | Draws a decorator immediately after the property row.
`context`: Current presentation state for the annotated property. | — | — | +| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L10) | Carries the mutable presentation state of one attribute-annotated Inspector property. | — | — | +| [`public object Inno.Editor.Inspection.InspectorAttributeDrawContext.owner { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L35) | Gets the object that owns the annotated member. | — | — | +| [`public System.Reflection.MemberInfo Inno.Editor.Inspection.InspectorAttributeDrawContext.member { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L40) | Gets the reflected field or property carrying the current attribute. | — | — | +| [`public Inno.Core.Serialization.SerializedProperty? Inno.Editor.Inspection.InspectorAttributeDrawContext.property { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L45) | Gets the root serialized property when one is available. | — | — | +| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L50) | Gets the stable Inspector control path. | — | — | +| [`public System.Attribute Inno.Editor.Inspection.InspectorAttributeDrawContext.attribute { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L55) | Gets the attribute currently invoking a drawer. | — | — | +| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawContext.isVisible { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L60) | Gets or sets whether the complete property row is visible. | — | — | +| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawContext.isReadOnly { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L65) | Gets or sets whether the property control is read-only in the Inspector. | — | — | +| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.label { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L70) | Gets or sets the visible property label. | — | — | +| [`public string Inno.Editor.Inspection.InspectorAttributeDrawContext.tooltip { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L75) | Gets or sets optional hover help for the property label. | — | — | +| [`public double? Inno.Editor.Inspection.InspectorAttributeDrawContext.minimum { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L80) | Gets or sets an optional inclusive numeric lower bound. | — | — | +| [`public double? Inno.Editor.Inspection.InspectorAttributeDrawContext.maximum { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L85) | Gets or sets an optional inclusive numeric upper bound. | — | — | +| [`public object? Inno.Editor.Inspection.InspectorAttributeDrawContext.GetSiblingValue(string memberName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawContext.cs#L99) | Reads a sibling field or property from the current owner, including non-public base members.
`memberName`: Exact CLR member name. | The current sibling value. | `System.InvalidOperationException`: Thrown when the sibling is absent, ambiguous, indexed, or unreadable. | +| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L9) | Associates an Inspector attribute drawer with an attribute type. | — | — | +| [`public Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.InspectorAttributeDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L23) | Creates an Inspector attribute drawer registration.
`targetType`: Attribute type handled by the drawer.
`useForChildren`: Whether derived attribute types are accepted.
`priority`: Tie-breaking priority after inheritance distance. | — | — | +| [`public System.Type Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L39) | Gets the handled attribute type. | — | — | +| [`public bool Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L44) | Gets whether derived attribute types are accepted. | — | — | +| [`public int Inno.Editor.Inspection.InspectorAttributeDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerAttribute.cs#L49) | Gets the registration priority. | — | — | +| [`public sealed class Inno.Editor.Inspection.InspectorAttributeDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L14) | Discovers generation-aware Inspector attribute drawers and applies them in declaration order. | — | — | +| [`public Inno.Editor.Inspection.InspectorAttributeDrawerRegistry.InspectorAttributeDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, System.Collections.Generic.IEnumerable drawerServices)`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L33) | Creates an Inspector attribute drawer registry.
`interactions`: Active editor interaction entry point.
`types`: Host-owned type catalog.
`serialization`: Active serialization registry.
`drawerServices`: Additional constructor-injected drawer services. | — | — | +| [`public void Inno.Editor.Inspection.InspectorAttributeDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/InspectorAttributeDrawerRegistry.cs#L88) | Releases active drawer generations. | — | — | +| [`public interface Inno.Editor.Inspection.IInspectionIconProvider`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/IInspectionIconProvider.cs#L9) | Resolves a presentation icon for inspected targets owned by another editor feature.
`TTarget`: The target type whose icon can be resolved. | — | — | +| [`string Inno.Editor.Inspection.IInspectionIconProvider.GetIcon(TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/IInspectionIconProvider.cs#L20) | Resolves the presentation icon for one inspected target.
`target`: The target whose icon should be resolved. | The icon glyph registered by the owning feature. | — | +| [`public sealed class Inno.Editor.Inspection.InspectionDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L15) | Provides services and state to an inspector drawer. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Inspection.InspectionDrawContext.editorContext { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L22) | Gets the shared editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Inspection.InspectionDrawContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L27) | Gets the active editor interaction entry point. | — | — | +| [`public object Inno.Editor.Inspection.InspectionDrawContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L32) | Gets the selected target. | — | — | +| [`public Inno.Editor.Inspection.SerializedPropertyRenderer Inno.Editor.Inspection.InspectionDrawContext.properties { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L37) | Gets the serialized property renderer. | — | — | +| [`public bool Inno.Editor.Inspection.InspectionDrawContext.TryDrawInline(object target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L48) | Draws an exact target-specific body inside the caller's existing Inspector card.
`target`: Nested target whose exact registered drawer should render. | when an exact drawer rendered the target. | — | +| [`public bool Inno.Editor.Inspection.InspectionDrawContext.HasProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L75) | Gets whether the target exposes one runtime-visible serialized property.
`propertyName`: Exact serialized property name. | when the property is available. | — | +| [`public T Inno.Editor.Inspection.InspectionDrawContext.GetProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L94) | Reads one runtime-visible serialized property from the target.
`propertyName`: Exact serialized property name.
`T`: Expected property value type. | The current strongly typed property value. | — | +| [`public void Inno.Editor.Inspection.InspectionDrawContext.DrawProperty(string propertyName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L110) | Draws one named property with its ordinary editor, history, and validation behavior.
`propertyName`: Exact serialized property name. | — | — | +| [`public void Inno.Editor.Inspection.InspectionDrawContext.DrawProperties()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawContext.cs#L128) | Draws every runtime-visible serialized property through the shared attribute and property drawer pipeline. | — | — | +| [`public abstract class Inno.Editor.Inspection.InspectionDrawer where TTarget : class : Inno.Editor.Inspection.IInspectionDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L11) | Provides the target-specific header presentation and body drawing behavior for an inspected target.
`TTarget`: The target type inspected by the drawer. | — | — | +| [`public abstract string Inno.Editor.Inspection.InspectionDrawer.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L17) | Gets the icon glyph displayed in the large leading slot of the Inspector target header. | — | — | +| [`protected virtual bool Inno.Editor.Inspection.InspectionDrawer.CanInspect(TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L28) | Determines whether this drawer handles the selected value, in addition to its registered type.
`target`: Current target. The check must not mutate it. | Whether this drawer accepts the target. | — | +| [`protected abstract (string name, System.Action? setter) Inno.Editor.Inspection.InspectionDrawer.BindName(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L45) | Binds the name displayed in the first row of the Inspector target header.
`context`: The active Inspector drawing context.
`target`: The strongly typed target being inspected. | The current target name and a callback that accepts a requested replacement name, or for the callback when the name is read-only. | — | +| [`protected virtual string Inno.Editor.Inspection.InspectionDrawer.GetIcon(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L62) | Resolves the icon glyph displayed for the current target.
`context`: The active Inspector drawing context.
`target`: The strongly typed target being inspected. | The target-specific icon glyph. The default implementation returns . | — | +| [`protected virtual void Inno.Editor.Inspection.InspectionDrawer.DrawHeader(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L80) | Draws target-specific controls in the second row of the Inspector target header.
`context`: The active Inspector drawing context.
`target`: The strongly typed target being inspected. | — | — | +| [`protected abstract void Inno.Editor.Inspection.InspectionDrawer.Draw(Inno.Editor.Inspection.InspectionDrawContext context, TTarget target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L95) | Draws the target-specific Inspector body below the shared target header.
`context`: The active Inspector drawing context.
`target`: The strongly typed target being inspected. | — | — | +| [`protected Inno.Editor.Inspection.InspectionDrawer.InspectionDrawer()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public interface Inno.Editor.Inspection.IInspectionDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L125) | Defines the runtime bridge used to render a discovered inspection drawer. | — | — | +| [`bool Inno.Editor.Inspection.IInspectionDrawer.CanInspect(object target)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L136) | Determines whether a type-compatible selected value can be inspected by this drawer.
`target`: Selected value, never mutated by this query. | Whether this drawer accepts the value; ordinary type-only drawers accept by default. | — | +| [`string Inno.Editor.Inspection.IInspectionDrawer.GetIcon(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L147) | Gets the icon glyph displayed in the Inspector target header.
`context`: The active Inspector drawing context. | The icon glyph for the current target. | — | +| [`(string name, System.Action? setter) Inno.Editor.Inspection.IInspectionDrawer.BindName(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L159) | Binds the target name displayed by the Inspector target header.
`context`: The active Inspector drawing context. | The current target name and a callback that accepts a requested replacement name, or for the callback when the name is read-only. | — | +| [`void Inno.Editor.Inspection.IInspectionDrawer.DrawHeader(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L167) | Draws the target-specific second header row.
`context`: The active Inspector drawing context. | — | — | +| [`void Inno.Editor.Inspection.IInspectionDrawer.Draw(Inno.Editor.Inspection.InspectionDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawer.cs#L175) | Draws the target-specific Inspector body.
`context`: The active Inspector drawing context. | — | — | +| [`public sealed class Inno.Editor.Inspection.InspectionDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L9) | Associates an inspector drawer with a selected target type. | — | — | +| [`public System.Type Inno.Editor.Inspection.InspectionDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L14) | Gets the selected target type handled by the drawer. | — | — | +| [`public bool Inno.Editor.Inspection.InspectionDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L19) | Gets whether derived target types are accepted. | — | — | +| [`public int Inno.Editor.Inspection.InspectionDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L24) | Gets the tie-breaking registration priority. | — | — | +| [`public bool Inno.Editor.Inspection.InspectionDrawerAttribute.conditional { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L29) | Gets whether CanInspect selects a subset of targets, allowing peer conditional registrations at the same priority. | — | — | +| [`public Inno.Editor.Inspection.InspectionDrawerAttribute.InspectionDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0, bool conditional = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerAttribute.cs#L49) | Creates an inspector drawer registration.
`targetType`: The selected object type handled by the drawer.
`useForChildren`: Whether the drawer may handle assignable derived target types.
`priority`: The tie-breaking priority after exactness and inheritance distance.
`conditional`: Whether CanInspect is a target-dependent predicate. Equally specific accepted peers remain an explicit ambiguity error. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public delegate Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory(System.Type drawerType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Creates one discovered inspection drawer through the owning editor composition root.
`drawerType`: The concrete drawer type selected from the active type catalog. | A new drawer instance whose runtime type is . | `System.ArgumentNullException`: Implementations may throw when is . | +| [`public Inno.Editor.Inspection.InspectionDrawerFactory.InspectionDrawerFactory(object @object, nint method)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public virtual Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory.Invoke(System.Type drawerType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Creates one discovered inspection drawer through the owning editor composition root.
`drawerType`: The concrete drawer type selected from the active type catalog. | A new drawer instance whose runtime type is . | `System.ArgumentNullException`: Implementations may throw when is . | +| [`public virtual System.IAsyncResult Inno.Editor.Inspection.InspectionDrawerFactory.BeginInvoke(System.Type drawerType, System.AsyncCallback callback, object @object)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public virtual Inno.Editor.Inspection.IInspectionDrawer Inno.Editor.Inspection.InspectionDrawerFactory.EndInvoke(System.IAsyncResult result)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Inspection.InspectionDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L16) | Discovers and resolves inspection drawers through the active type catalog. | — | — | +| [`public Inno.Editor.Inspection.InspectionDrawerRegistry.InspectionDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Inspection.InspectionDrawerFactory factory, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L41) | Creates a generation-aware inspection drawer registry.
`interactions`: The active editor interaction entry point exposed to draw contexts.
`factory`: The composition-root factory used to construct discovered drawer types and resolve their module-specific dependencies.
`types`: The host-owned type catalog that coordinates drawer generations.
`serialization`: Serialization registry used to expose the selected target's visible properties to custom drawers. | — | `System.ArgumentNullException`: Thrown when or is . | +| [`public bool Inno.Editor.Inspection.InspectionDrawerRegistry.TryResolve(Inno.Editor.Core.EditorContext editorContext, object target, Inno.Editor.Inspection.SerializedPropertyRenderer renderer, out Inno.Editor.Inspection.IInspectionDrawer? drawer, out Inno.Editor.Inspection.InspectionDrawContext? context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L76) | Resolves the most specific registered drawer and creates its drawing context.
`editorContext`: The shared editor context exposed to the selected drawer.
`target`: The selected object whose runtime type determines the drawer.
`renderer`: The serialized property renderer exposed to the selected drawer.
`drawer`: The resolved drawer when the method succeeds.
`context`: The target-specific drawing context when the method succeeds. | when a matching drawer was resolved. | `System.ArgumentNullException`: Thrown when , , or is . | +| [`public bool Inno.Editor.Inspection.InspectionDrawerRegistry.TryResolveExact(Inno.Editor.Core.EditorContext editorContext, object target, Inno.Editor.Inspection.SerializedPropertyRenderer renderer, out Inno.Editor.Inspection.IInspectionDrawer? drawer, out Inno.Editor.Inspection.InspectionDrawContext? context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L127) | Resolves only a drawer explicitly registered for the target's exact runtime type.
`editorContext`: The shared editor context exposed to the selected drawer.
`target`: The selected object whose exact runtime type determines the drawer.
`renderer`: The serialized property renderer exposed to the selected drawer.
`drawer`: The exact drawer when the method succeeds.
`context`: The target-specific drawing context when the method succeeds. | when an exact registration exists; inherited and fallback drawers are ignored. | `System.ArgumentNullException`: Thrown when , , or is . | +| [`public void Inno.Editor.Inspection.InspectionDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/InspectionDrawerRegistry.cs#L161) | Releases every active drawer snapshot and unregisters the registry from type refreshes. | — | — | +| [`public interface Inno.Editor.Inspection.IInspectionPropertyEditService`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs#L7) | Applies one root serialized-property mutation through the history mechanism owned by a feature. | — | — | +| [`bool Inno.Editor.Inspection.IInspectionPropertyEditService.ChangeProperty(object owner, string propertyName, System.Action mutation, string historyName, string? mergeKey, string? componentName)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs#L41) | Applies a property mutation and records it when the owning feature observes a value change.
`owner`: The domain object that owns the root serialized property.
`propertyName`: The exact root serialized member name.
`mutation`: The callback that assigns the requested value.
`historyName`: The user-facing name for the resulting history entry.
`mergeKey`: The current shared gesture key, or null for a discrete edit.
`componentName`: An optional public value component; null replaces the complete root value. | when the value changed and the owning feature recorded the edit; otherwise, . | `System.ArgumentNullException`: Thrown when or is .
`System.ArgumentException`: Thrown when or is empty. | +| [`public interface Inno.Editor.Inspection.IPropertyDrawer`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IPropertyDrawer.cs#L6) | Draws and optionally edits one serialized property value. | — | — | +| [`void Inno.Editor.Inspection.IPropertyDrawer.Draw(Inno.Editor.Inspection.PropertyDrawContext context)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IPropertyDrawer.cs#L14) | Draws a property through its encapsulated value accessors.
`context`: Property drawing context. | — | — | +| [`public sealed class Inno.Editor.Inspection.PropertyDrawContext`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L12) | Encapsulates one editable property path and its drawing services. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Inspection.PropertyDrawContext.editorContext { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L24) | Gets the shared editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Inspection.PropertyDrawContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L28) | Gets the active editor interaction entry point. | — | — | +| [`public string Inno.Editor.Inspection.PropertyDrawContext.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L32) | Gets the stable control path. | — | — | +| [`public string Inno.Editor.Inspection.PropertyDrawContext.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L36) | Gets the display label. | — | — | +| [`public System.Type Inno.Editor.Inspection.PropertyDrawContext.propertyType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L40) | Gets the declared property type. | — | — | +| [`public Inno.Core.Serialization.PropertyVisibility Inno.Editor.Inspection.PropertyDrawContext.visibility { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L44) | Gets the serialization visibility flags. | — | — | +| [`public bool Inno.Editor.Inspection.PropertyDrawContext.isReadOnly { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L48) | Gets whether assignments are disabled. | — | — | +| [`public double? Inno.Editor.Inspection.PropertyDrawContext.minimum { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L52) | Gets the optional inclusive numeric lower bound supplied by Inspector metadata. | — | — | +| [`public double? Inno.Editor.Inspection.PropertyDrawContext.maximum { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L56) | Gets the optional inclusive numeric upper bound supplied by Inspector metadata. | — | — | +| [`public string? Inno.Editor.Inspection.PropertyDrawContext.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L60) | Gets optional hover help supplied by Inspector metadata. | — | — | +| [`public bool Inno.Editor.Inspection.PropertyDrawContext.hdrColor { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L64) | Gets whether Color values are unclamped linear floating-point channels. | — | — | +| [`public object? Inno.Editor.Inspection.PropertyDrawContext.GetValue()`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L114) | Gets the latest value. | The latest value returned by the serialized property's getter. | — | +| [`public bool Inno.Editor.Inspection.PropertyDrawContext.TryGetTextState(string key, out string? value)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L127) | Tries to read text-edit state scoped to this renderer, inspected owner, and property path.
`key`: Drawer-local state identity within the current property path.
`value`: Receives the stored text when the state exists. | when text state exists for the exact owner and property path. | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.SetTextState(string key, string value)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L140) | Stores text-edit state scoped to this renderer, inspected owner, and property path.
`key`: Drawer-local state identity within the current property path.
`value`: Current neutral text state to retain between frames. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.ClearTextState(string key)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L150) | Removes text-edit state for this renderer, inspected owner, and property path.
`key`: Drawer-local state identity within the current property path. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.SetValue(object? value, string? componentName = null)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L160) | Assigns a value when the property is writable.
`value`: New value.
`componentName`: An optional public scalar component, or an Euler axis such as euler.x, for precise batch value edits. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(string childName, System.Type childType, System.Func getter, System.Action setter, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L199) | Draws a nested value whose setter writes through this property path.
`childName`: The stable child member name appended to this property path.
`childType`: The declared type used to resolve a property drawer.
`getter`: The callback that reads the latest child value.
`setter`: The callback that writes an edited child value.
`readOnly`: Whether the child should be presented without assignment support. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L216) | Draws a nested serialized property.
`property`: Nested property. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawChild(object metadataOwner, Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L226) | Draws a nested serialized property with presentation metadata resolved from its immediate owner.
`metadataOwner`: Object that declares the nested serialized member.
`property`: Nested property. | — | — | +| [`public void Inno.Editor.Inspection.PropertyDrawContext.DrawInlineChild(string childName, System.Type childType, System.Func getter, System.Action setter, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs#L295) | Draws a nested value inline without adding another label row.
`childName`: The stable child member name appended to this property path.
`childType`: The declared type used to resolve a property drawer.
`getter`: The callback that reads the latest child value.
`setter`: The callback that writes an edited child value.
`readOnly`: Whether the child should be presented without assignment support. | — | — | +| [`public sealed class Inno.Editor.Inspection.PropertyDrawerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L9) | Associates a property drawer with a declared property type. | — | — | +| [`public System.Type Inno.Editor.Inspection.PropertyDrawerAttribute.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L14) | Gets the declared property type handled by the drawer. | — | — | +| [`public bool Inno.Editor.Inspection.PropertyDrawerAttribute.useForChildren { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L19) | Gets whether assignable derived types are accepted. | — | — | +| [`public int Inno.Editor.Inspection.PropertyDrawerAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L24) | Gets the tie-breaking registration priority. | — | — | +| [`public Inno.Editor.Inspection.PropertyDrawerAttribute.PropertyDrawerAttribute(System.Type targetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerAttribute.cs#L41) | Creates a property drawer registration.
`targetType`: The declared serialized property type handled by the drawer.
`useForChildren`: Whether the drawer may handle assignable derived property types.
`priority`: The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public sealed class Inno.Editor.Inspection.PropertyDrawerRegistry : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L15) | Discovers and resolves serialized property drawers through the active type catalog. | — | — | +| [`public Inno.Editor.Inspection.PropertyDrawerRegistry.PropertyDrawerRegistry(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, System.Collections.Generic.IEnumerable drawerServices)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L37) | Creates a generation-aware property drawer registry.
`interactions`: The active editor interaction entry point available to property drawer constructors.
`types`: The host-owned type catalog that coordinates drawer generations.
`serialization`: The serialization registry available to structured-object drawers.
`drawerServices`: Additional generation-bound services available to feature-specific drawer constructors. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Editor.Inspection.IPropertyDrawer Inno.Editor.Inspection.PropertyDrawerRegistry.Resolve(System.Type propertyType)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L58) | Resolves the most specific drawer for a declared property type.
`propertyType`: The declared serialized property type to resolve. | The most specific registered drawer, or the unsupported-value fallback. | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.Inspection.PropertyDrawerRegistry.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawerRegistry.cs#L67) | Releases every active property drawer snapshot and unregisters the registry from type refreshes. | — | — | +| [`public sealed class Inno.Editor.Inspection.SerializedPropertyRenderer`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L19) | Resolves drawers and renders serialized property paths with isolated error handling. | — | — | +| [`public Inno.Editor.Inspection.SerializedPropertyRenderer.SerializedPropertyRenderer(Inno.Editor.Inspection.PropertyDrawerRegistry drawers, Inno.Editor.Inspection.InspectorAttributeDrawerRegistry attributes, Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Inspection.IInspectionPropertyEditService edits, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L51) | Creates a serialized property renderer over one drawer registry and feature-owned edit service.
`drawers`: The property drawer registry used for runtime type resolution.
`attributes`: The generation-aware registry that interprets Inspector presentation attributes.
`interactions`: The active editor interaction entry point.
`edits`: The feature-owned service used to apply and record property changes.
`logs`: The host-owned log router used to report isolated drawer failures. | — | `System.ArgumentNullException`: Thrown when , , or is . | +| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.Draw(Inno.Editor.Core.EditorContext editorContext, object owner, string ownerPath, Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L81) | Draws a root serialized property.
`editorContext`: Shared editor context.
`owner`: The live domain object that owns the root property.
`ownerPath`: Stable owner path.
`property`: Serialized property. | — | — | +| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.DrawValue(Inno.Editor.Core.EditorContext editorContext, object owner, string path, string label, System.Type propertyType, System.Func getter, System.Action setter, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly = false, bool hdrColor = false, string? tooltip = null, double? minimum = null, double? maximum = null)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L148) | Draws a declared authoring value with existing property drawers and a feature-owned draft edit service.
`editorContext`: Current Editor frame.
`owner`: Stable owner for text state and edit routing; not necessarily a canonical serialized object.
`path`: Stable unique property path.
`label`: User-facing label.
`propertyType`: Declared value type used to select its registered drawer.
`getter`: Reads the detached value.
`setter`: Changes the detached value within the supplied edit service.
`edits`: Feature-owned transaction boundary, propagated to nested properties.
`readOnly`: Whether assignment is disabled.
`hdrColor`: Whether Color values use unclamped linear floating-point channels.
`tooltip`: Optional authoring description rendered by the shared property tooltip.
`minimum`: Optional inclusive numeric editing bound.
`maximum`: Optional inclusive numeric editing bound. | — | — | +| [`public void Inno.Editor.Inspection.SerializedPropertyRenderer.DrawDraftProperty(Inno.Editor.Core.EditorContext editorContext, object stateOwner, object valueOwner, string ownerPath, Inno.Core.Serialization.SerializedProperty property, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly = false)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L210) | Draws a detached serialized property with its attributes and a feature-owned draft transaction.
`editorContext`: Current Editor frame.
`stateOwner`: Stable host-owned presentation state, containing no collectible objects.
`valueOwner`: Invocation-local detached value declaring the property and attributes.
`ownerPath`: Stable document or settings path.
`property`: Detached native serialized property.
`edits`: Draft mutation and shared History boundary.
`readOnly`: Whether source ownership prohibits edits. | — | — | +| [`public (bool isVisible, bool isReadOnly) Inno.Editor.Inspection.SerializedPropertyRenderer.GetPropertyAvailability(object owner, Inno.Core.Serialization.SerializedProperty property)`](../../src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs#L253) | Evaluates the same current-generation presentation attributes used by drawing without creating any UI state.
`owner`: The current object declaring the property's conditional metadata.
`property`: The root serialized property to inspect. | Visibility and editability for this exact owner; callers must combine all selected owners before a batch edit. | — | ## 项目依赖 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Mathematics](../core/Inno.Core.Mathematics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Annotations](Inno.Editor.Annotations.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Core.Mathematics` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Annotations` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Interactions.md b/docs/editor/Inno.Editor.Interactions.md index a0bc5d3c2..cb3d8c50f 100644 --- a/docs/editor/Inno.Editor.Interactions.md +++ b/docs/editor/Inno.Editor.Interactions.md @@ -392,15 +392,31 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 脚本只能看到明确导出的契约,不能看到 Router、Catalog、TypeCache snapshot 或实现侧 `Inno.*`。完全禁止 global using。 -## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 -### `Inno.Editor.Interactions.EditorAction` +## 有序多选与连续编辑 + +EditorSelectionState 保存有序身份、primary、anchor 和变化计数;范围选择使用当前可见排序,Ctrl 切换单项,Shift 扩展范围。EditorSelectionSnapshot 不持有 live objects,CopyTargets 在原身份 domain 重新解析。Missing 仍保留在 snapshot 的 identities 中,不能被当作空选择跳过。 + +EditorEditGesture 是共享 History transaction 的所有者。每次 sample 立即 RecordApplied;Commit 完成一个 Undo,Cancel 回滚。拒绝非 LIFO Commit/Cancel 时不丢失事务。文档 Save/Revert/Apply/Close、命令和扩展切换发出 Core Events 的 EditorOperationPreparingEvent,presentation 在新操作开始前完成手势。该通知不是第二条事件总线。 + +Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重新解析并拒绝 stale generation。纯值参数可以使用不可变值类型,避免无 identity 的临时 command wrapper 被 GC 回收。 + + + +## Host 临时 Identity domain + +`RegisterIdentityDomain` 借用 Core allocator 并返回 owner-thread 路由 lease。它让同一套 Selection、focus、queue 和 DragDrop 解析独立的 Play 世界;不复制对象表,也不取得 allocator 的销毁权。重复登记、错误线程或退休后登记明确失败。Scene workspace 在恢复 Edit 选择成功后释放路由;失败保留 owner 供重试。`IEditorSelectionCoordinator` 是 Host-only SPI,不再作为项目脚本导出;脚本继续使用 `EditorInteractions`。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + @@ -412,51 +428,7 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected void Inno.Editor.Interactions.EditorAction.Activate(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L72) | Activates this action for a target and cancels any operation it previously owned.
`context`:The contextual request that starts the operation. | — | `System.ArgumentNullException`:Thrown when context is null. | -| [`protected void Inno.Editor.Interactions.EditorAction.Cancel()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L95) | Cancels the current operation and returns the action to its idle state. | — | — | -| [`protected void Inno.Editor.Interactions.EditorAction.Complete()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L83) | Completes the current operation and returns the action to its idle state. | — | — | -| [`public void Inno.Editor.Interactions.EditorAction.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L161) | Cancels active work and releases this action instance. | — | — | -| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L121) | Executes the action for the supplied context.
`context`:The editor, area, target, and optional argument for the operation. | — | — | -| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnCancelled()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L144) | Runs after an active operation is cancelled. | — | — | -| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnCompleted()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L137) | Runs after an active operation completes successfully. | — | — | -| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnPresentationLost()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L156) | Runs when this action's active target loses editor presentation focus. | The default behavior cancels the active operation. An action that owns an editable value may override this method to validate and commit that value. The operation is cancelled automatically when an override returns without calling Inno.Editor.Interactions.EditorAction.Complete or Inno.Editor.Interactions.EditorAction.Cancel. | — | -| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L132) | Presents an active action at the current target location.
返回:true when the action replaced the target's normal content; otherwise, false.
`context`:The current presentation area and target. | — | — | -| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L113) | Evaluates the action for the supplied context.
返回:The current presentation and availability state.
`context`:The editor, area, target, and optional argument for the query. | — | — | -| [`public bool Inno.Editor.Interactions.EditorAction.isActive { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L17) | Gets whether the action currently owns an active multi-frame operation. | — | — | -| [`public virtual System.Type? Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L22) | Gets the required target type, or null for a targetless action. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorAction : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L8) | Defines one automatically discovered editor operation and owns its complete multi-frame lifecycle. | — | — | - -### `Inno.Editor.Interactions.EditorAction`1` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L380) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected override sealed void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L411) | Applies the editor action to the supplied interaction context.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L455) | Executes the action for a strongly typed target.
`context`:The contextual request containing the typed target. | — | — | -| [`protected override sealed bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L431) | Presents this action through the current editor interaction surface.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L466) | Presents an active action for a strongly typed target.
返回:true when content was presented; otherwise, false.
`context`:The current presentation request containing the typed target. | — | — | -| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L397) | Evaluates the operation's current availability and presentation state.
返回:The validated editor action state that represents the completed operation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L447) | Evaluates the action for a strongly typed target.
返回:The current presentation and availability state.
`context`:The contextual request containing the typed target. | — | — | -| [`public override sealed System.Type Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L386) | Gets the concrete type handled by this extension implementation. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorAction where TTarget : class : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L380) | Defines a discoverable action whose target must be assignable to a specific reference type. | — | — | - -### `Inno.Editor.Interactions.EditorAction`2` - - + @@ -465,95 +437,51 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L180) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected override sealed void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L210) | Applies the editor action to the supplied interaction context.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L250) | Executes the action for a typed target and argument.
`context`:The typed contextual request. | — | — | -| [`protected override sealed bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L230) | Presents this action through the current editor interaction surface.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L261) | Presents an active action for a typed target and argument.
返回:true when content was presented.
`context`:The typed presentation request. | — | — | -| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L199) | Evaluates the operation's current availability and presentation state.
返回:The validated editor action state that represents the completed operation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L242) | Evaluates the action for a typed target and argument.
返回:The current presentation and availability state.
`context`:The typed contextual request. | — | — | -| [`public override sealed System.Type Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L186) | Gets the concrete type handled by this extension implementation. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorAction where TTarget : class : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L180) | Defines a target action that requires one strongly typed command argument. | — | — | - -### `Inno.Editor.Interactions.EditorActionArgumentContext`1` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public TArgument Inno.Editor.Interactions.EditorActionArgumentContext.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L160) | Gets the strongly typed action argument. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorActionArgumentContext : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L146) | Provides a strongly typed action argument to a targetless editor action. | — | — | - -### `Inno.Editor.Interactions.EditorActionAttribute` - + + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorActionAttribute.EditorActionAttribute(string action, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L23) | Creates an action registration available from every area.
`action`:The stable semantic action name.
`priority`:The tie-breaking priority used after target specificity. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public Inno.Editor.Interactions.EditorActionAttribute.EditorActionAttribute(string action, string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L46) | Creates an action registration restricted to one exact area.
`action`:The stable semantic action name.
`area`:The exact area, or an empty string for every area.
`priority`:The tie-breaking priority. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public string Inno.Editor.Interactions.EditorActionAttribute.action { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L61) | Gets the stable action name. | — | — | -| [`public string Inno.Editor.Interactions.EditorActionAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L66) | Gets the optional exact interaction area. | — | — | -| [`public int Inno.Editor.Interactions.EditorActionAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L71) | Gets the tie-breaking priority. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorActionAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L9) | Registers an editor action for automatic discovery and dispatch. | — | — | - -### `Inno.Editor.Interactions.EditorActionContext` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Editor.Interactions.EditorActionContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L69) | Gets the stable interaction area. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorActionContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L54) | Gets the shared passive editor context. | — | — | -| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorActionContext.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L64) | Gets the transactional history used to record reversible mutations performed by this action. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorActionContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L59) | Gets the active interaction entry point. | — | — | -| [`public object? Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L74) | Gets the contextual action target. | — | — | -| [`public class Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L10) | Provides contextual state to an editor action. | — | — | - -### `Inno.Editor.Interactions.EditorActionContext`1` - - + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public TTarget Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L103) | Gets the strongly typed action target. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorActionContext where TTarget : class : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L88) | Provides a strongly typed target to an editor action implementation. | — | — | - -### `Inno.Editor.Interactions.EditorActionContext`2` - - + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public TArgument Inno.Editor.Interactions.EditorActionContext.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L137) | Gets the strongly typed action argument. | — | — | -| [`public TTarget Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L132) | Gets the strongly typed action target. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorActionContext where TTarget : class : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L115) | Provides a strongly typed target and action argument to an editor action. | — | — | - -### `Inno.Editor.Interactions.EditorActionState` - + + - + + @@ -563,59 +491,37 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorActionState.EditorActionState(bool isVisible, bool isEnabled, bool isChecked = false, string? displayName = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L23) | Creates the contextual presentation state of an editor action.
`isVisible`:Whether the action should be included in the current presentation.
`isEnabled`:Whether the action can execute in the current context.
`isChecked`:Whether the action is currently presented as checked.
`displayName`:An optional contextual label that replaces the static placement label. | — | — | -| [`public bool Inno.Editor.Interactions.EditorActionState.Equals(Inno.Editor.Interactions.EditorActionState other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorActionState.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorActionState.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorActionState.operator ==(Inno.Editor.Interactions.EditorActionState left, Inno.Editor.Interactions.EditorActionState right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorActionState.operator !=(Inno.Editor.Interactions.EditorActionState left, Inno.Editor.Interactions.EditorActionState right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorActionState.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.disabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L63) | Gets a visible but disabled action state. | — | — | -| [`public string? Inno.Editor.Interactions.EditorActionState.displayName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L53) | Gets an optional contextual display name. | — | — | -| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.enabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L58) | Gets a visible and enabled action state. | — | — | -| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.hidden { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L68) | Gets a hidden action state. | — | — | -| [`public bool Inno.Editor.Interactions.EditorActionState.isChecked { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L48) | Gets whether the action is currently checked. | — | — | -| [`public bool Inno.Editor.Interactions.EditorActionState.isEnabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L43) | Gets whether the action can execute. | — | — | -| [`public bool Inno.Editor.Interactions.EditorActionState.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L38) | Gets whether the action should be displayed. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorActionState : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L6) | Describes the current presentation and availability of an editor action. | — | — | - -### `Inno.Editor.Interactions.EditorArgumentAction`1` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorArgumentAction.EditorArgumentAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L283) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract void Inno.Editor.Interactions.EditorArgumentAction.Execute(Inno.Editor.Interactions.EditorActionArgumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L334) | Executes the action for a typed argument.
`context`:The typed contextual request. | — | — | -| [`protected override sealed void Inno.Editor.Interactions.EditorArgumentAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L307) | Applies the editor action to the supplied interaction context.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorArgumentAction.Query(Inno.Editor.Interactions.EditorActionArgumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L326) | Evaluates the action for a typed argument.
返回:The current presentation and availability state.
`context`:The typed contextual request. | — | — | -| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorArgumentAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L296) | Evaluates the operation's current availability and presentation state.
返回:The validated editor action state that represents the completed operation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorArgumentAction : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L283) | Defines a targetless editor action that requires one strongly typed command argument. | — | — | - -### `Inno.Editor.Interactions.EditorDocumentCloseMode` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Cancel = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L14) | Keeps the document open. | — | — | -| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Discard = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L22) | Closes the document and discards unsaved state. | — | — | -| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Save = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L18) | Closes the document after saving. | — | — | -| [`public enum Inno.Editor.Interactions.EditorDocumentCloseMode : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L9) | Selects how a dirty document responds to a close request. | — | — | - -### `Inno.Editor.Interactions.EditorDocumentContext` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -627,23 +533,6 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Interactions.EditorDocumentContext.SetViewParameter(string key, string value)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L76) | Adds or replaces one stable scalar or JSON-formatted view parameter.
`key`:Stable provider-local parameter name.
`value`:Persistent scalar or JSON-formatted value. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDocumentContext.TryGetViewParameter(string key, out string value)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L97) | Tries to read one stable view parameter.
返回:true when the parameter exists.
`key`:Stable provider-local parameter name.
`value`:Receives the stored value. | — | — | -| [`public System.Guid Inno.Editor.Interactions.EditorDocumentContext.assetId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L35) | Gets the persistent asset identity, or an empty value before the source has one. | — | — | -| [`public string Inno.Editor.Interactions.EditorDocumentContext.assetPath { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L40) | Gets the normalized project asset path. | — | — | -| [`public System.Guid Inno.Editor.Interactions.EditorDocumentContext.documentId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L30) | Gets the stable identity of this open source document. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDocumentContext.isDirty { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L55) | Gets whether unsaved source or staged changes exist. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDocumentContext.isProviderAvailable { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L60) | Gets whether a current-generation provider is available. | — | — | -| [`public string Inno.Editor.Interactions.EditorDocumentContext.providerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L45) | Gets the stable provider identity used to recover across extension reload. | — | — | -| [`public string Inno.Editor.Interactions.EditorDocumentContext.title { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L50) | Gets or sets the author-facing source title used by diagnostics and dedicated editors. | — | — | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Editor.Interactions.EditorDocumentContext.viewParameters { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L65) | Gets an immutable snapshot of stable view parameters. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDocumentContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L10) | Stores only stable, reload-safe document identity and provider-owned draft parameters. | — | — | - -### `Inno.Editor.Interactions.EditorDocumentProvider` - @@ -653,71 +542,37 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorDocumentProvider.EditorDocumentProvider()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public virtual bool Inno.Editor.Interactions.EditorDocumentProvider.Apply(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L54) | Applies staged authoring changes and saves their asset source.
返回:true when staged changes are applied.
`context`:Stable document context owned by the host. | — | — | -| [`public abstract bool Inno.Editor.Interactions.EditorDocumentProvider.CanOpen(string assetPath)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L24) | Returns whether this provider can open the supplied project asset path.
返回:true when the path is supported.
`assetPath`:Normalized project asset path. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorDocumentProvider.Close(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L77) | Releases transient provider state after a document closes.
`context`:Stable document context owned by the host. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorDocumentProvider.Open(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L32) | Initializes transient provider state for an opened or restored document.
`context`:Stable document context owned by the host. | — | — | -| [`public virtual bool Inno.Editor.Interactions.EditorDocumentProvider.Revert(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L65) | Discards staged authoring changes and reloads the last saved source.
返回:true when the source is restored.
`context`:Stable document context owned by the host. | — | — | -| [`public abstract bool Inno.Editor.Interactions.EditorDocumentProvider.Save(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L43) | Saves all document changes to its asset source.
返回:true when the source is saved.
`context`:Stable document context owned by the host. | — | — | -| [`public abstract string Inno.Editor.Interactions.EditorDocumentProvider.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L13) | Gets the globally stable provider identifier. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorDocumentProvider`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L8) | Implements one reloadable asset-document kind without owning presentation or source identity. | — | — | - -### `Inno.Editor.Interactions.EditorDragContext` - + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDragContext.EditorDragContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L33) | Creates a managed editor drag request.
`editor`:The shared passive editor context.
`interactions`:The active interaction entry point.
`area`:The interaction area that produced the source.
`data`:The transient source identity and preview label. | — | `System.ArgumentNullException`:Thrown when editor, interactions, or data is null.
`System.ArgumentException`:Thrown when area is empty. | -| [`public string Inno.Editor.Interactions.EditorDragContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L59) | Gets the source interaction area. | — | — | -| [`public Inno.Editor.Interactions.EditorDragData Inno.Editor.Interactions.EditorDragContext.data { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L64) | Gets the managed drag data. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDragContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L49) | Gets the shared passive editor context. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDragContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L54) | Gets the active interaction entry point. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDragContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L10) | Provides contextual state while beginning an editor drag. | — | — | - -### `Inno.Editor.Interactions.EditorDragData` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDragData.EditorDragData(Inno.Core.Identity.IdentityObject source, string label)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L27) | Creates drag data for a registered identity object.
`source`:The registered source object resolved again for each drop query and delivery.
`label`:The human-readable label used by drag previews. | — | `System.ArgumentNullException`:Thrown when source is null.
`System.InvalidOperationException`:Thrown when source is not registered in a live identity domain. | -| [`public string Inno.Editor.Interactions.EditorDragData.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L46) | Gets the drag preview label. | — | — | -| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorDragData.sourceIdentity { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L41) | Gets the domain-qualified transient identity written to the native drag protocol. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDragData`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L10) | Contains the transient identity and preview label for one editor drag operation. | — | — | - -### `Inno.Editor.Interactions.EditorDrop` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorDrop.EditorDrop()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L11) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L43) | Executes a delivered drop after a successful compatibility query.
返回:The observable selection and reveal result of the operation.
`context`:The untyped source, target, surface, and requested placement. | — | — | -| [`public abstract Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L32) | Evaluates whether the current managed source may be dropped on the supplied target.
返回:The compatibility state and standard target visual.
`context`:The untyped source, target, surface, and requested placement. | — | — | -| [`public abstract System.Type Inno.Editor.Interactions.EditorDrop.sourceType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L16) | Gets the accepted drag source type. | — | — | -| [`public abstract System.Type Inno.Editor.Interactions.EditorDrop.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L21) | Gets the accepted drop target type. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorDrop`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L11) | Defines one discoverable typed editor drop operation. | — | — | - -### `Inno.Editor.Interactions.EditorDrop`2` - @@ -726,36 +581,19 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorDrop.EditorDrop()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L55) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public override sealed Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L95) | Validates and applies the current editor drag-and-drop interaction atomically.
返回:The validated editor drop result that represents the completed operation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected abstract Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L123) | Executes a delivered drop for the strongly typed source and target.
返回:The observable selection and reveal result of the operation.
`context`:The typed source, target, surface, and requested placement. | — | — | -| [`public override sealed Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L78) | Evaluates the operation's current availability and presentation state.
返回:The validated editor drop status that represents the completed operation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected abstract Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L112) | Evaluates whether the strongly typed source may be dropped on the strongly typed target.
返回:The compatibility state and standard target visual.
`context`:The typed source, target, surface, and requested placement. | — | — | -| [`public override sealed System.Type Inno.Editor.Interactions.EditorDrop.sourceType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L62) | Gets the concrete type handled by this extension implementation. | — | — | -| [`public override sealed System.Type Inno.Editor.Interactions.EditorDrop.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L67) | Gets the concrete type handled by this extension implementation. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorDrop where TSource : Inno.Core.Identity.IdentityObject where TTarget : class : Inno.Editor.Interactions.EditorDrop`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L55) | Defines a typed drop operation for one managed source and target pair. | — | — | - -### `Inno.Editor.Interactions.EditorDropAttribute` - + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDropAttribute.EditorDropAttribute(int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L17) | Creates a drop registration that can participate on any interaction surface.
`priority`:The tie-breaking priority used after source and target specificity. | — | — | -| [`public Inno.Editor.Interactions.EditorDropAttribute.EditorDropAttribute(string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L31) | Creates a drop registration scoped to an exact interaction area.
`area`:The exact target area handled by this registration, or an empty string for any area.
`priority`:The tie-breaking priority used after source and target specificity. | — | — | -| [`public string Inno.Editor.Interactions.EditorDropAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L42) | Gets the optional exact interaction area. | — | — | -| [`public int Inno.Editor.Interactions.EditorDropAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L47) | Gets the tie-breaking priority. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDropAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L9) | Registers a typed editor drop handler for an optional exact area. | — | — | - -### `Inno.Editor.Interactions.EditorDropContext` - @@ -765,59 +603,11 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDropContext.EditorDropContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, Inno.Editor.Interactions.EditorDragData data, Inno.Core.Identity.IdentityObject source, object target, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L44) | Creates a managed drop request.
`editor`:The shared passive editor context.
`interactions`:The active interaction entry point.
`area`:The target interaction area.
`data`:The active identity-based drag data.
`source`:The live source resolved from data for this operation only.
`target`:The managed target object exposed to typed handlers.
`placement`:The requested position relative to the target. | — | `System.ArgumentNullException`:Thrown when editor, interactions, data, source, or target is null.
`System.ArgumentException`:Thrown when area is empty. | -| [`public string Inno.Editor.Interactions.EditorDropContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L76) | Gets the target interaction area. | — | — | -| [`public Inno.Editor.Interactions.EditorDragData Inno.Editor.Interactions.EditorDropContext.data { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L81) | Gets the active managed drag data. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDropContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L66) | Gets the shared passive editor context. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDropContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L71) | Gets the active interaction entry point. | — | — | -| [`public Inno.Editor.Interactions.EditorDropPlacement Inno.Editor.Interactions.EditorDropContext.placement { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L96) | Gets the requested placement relative to the target. | — | — | -| [`public Inno.Core.Identity.IdentityObject Inno.Editor.Interactions.EditorDropContext.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L86) | Gets the live source resolved for this query or delivery operation. | — | — | -| [`public object Inno.Editor.Interactions.EditorDropContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L91) | Gets the managed drop target. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDropContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L11) | Provides contextual state to an editor drop handler. | — | — | - -### `Inno.Editor.Interactions.EditorDropContext`2` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Editor.Interactions.EditorDropContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L167) | Gets the interaction area. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDropContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L157) | Gets the active editor context. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDropContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L162) | Gets the active interaction entry point. | — | — | -| [`public Inno.Editor.Interactions.EditorDropPlacement Inno.Editor.Interactions.EditorDropContext.placement { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L182) | Gets the requested placement. | — | — | -| [`public TSource Inno.Editor.Interactions.EditorDropContext.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L172) | Gets the typed drag source. | — | — | -| [`public TTarget Inno.Editor.Interactions.EditorDropContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L177) | Gets the typed drop target. | — | — | -| [`public Inno.Editor.Interactions.EditorDropContext Inno.Editor.Interactions.EditorDropContext.untyped { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L152) | Gets the untyped drop context. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorDropContext where TSource : class where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L135) | Provides strongly typed source and target values to a drop operation. | — | — | - -### `Inno.Editor.Interactions.EditorDropPlacement` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorDropPlacement.After = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L26) | Insert after the target. | — | — | -| [`Inno.Editor.Interactions.EditorDropPlacement.Before = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L16) | Insert before the target. | — | — | -| [`Inno.Editor.Interactions.EditorDropPlacement.Into = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L21) | Drop into the target. | — | — | -| [`Inno.Editor.Interactions.EditorDropPlacement.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L11) | No positional placement is requested. | — | — | -| [`public enum Inno.Editor.Interactions.EditorDropPlacement : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L6) | Describes the requested placement relative to an editor drop target. | — | — | - -### `Inno.Editor.Interactions.EditorDropResult` - @@ -831,25 +621,6 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDropResult.EditorDropResult(bool accepted, object? selectionTarget = null, object? revealTarget = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L20) | Creates the observable result of a completed drop operation.
`accepted`:Whether the handler accepted and completed the drop.
`selectionTarget`:An optional object that the originating view should select.
`revealTarget`:An optional object that the originating view should reveal or expand. | — | — | -| [`public static Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDropResult.Accepted(object? selectionTarget = null, object? revealTarget = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L62) | Creates an accepted drop result with optional presentation requests.
返回:An accepted drop result containing the supplied presentation requests.
`selectionTarget`:An optional object that the originating view should select.
`revealTarget`:An optional object that the originating view should reveal or expand. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDropResult.Equals(Inno.Editor.Interactions.EditorDropResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorDropResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorDropResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorDropResult.operator ==(Inno.Editor.Interactions.EditorDropResult left, Inno.Editor.Interactions.EditorDropResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorDropResult.operator !=(Inno.Editor.Interactions.EditorDropResult left, Inno.Editor.Interactions.EditorDropResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorDropResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDropResult.accepted { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L33) | Gets whether the drop was accepted. | — | — | -| [`public static Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDropResult.rejected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L48) | Gets a rejected drop result. | — | — | -| [`public object? Inno.Editor.Interactions.EditorDropResult.revealTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L43) | Gets the optional hierarchy target that should be revealed. | — | — | -| [`public object? Inno.Editor.Interactions.EditorDropResult.selectionTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L38) | Gets the optional target that should become selected. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorDropResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Describes the observable result of a completed editor drop. | — | — | - -### `Inno.Editor.Interactions.EditorDropStatus` - @@ -862,42 +633,21 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorDropStatus.EditorDropStatus(bool canDrop, Inno.Editor.Interactions.EditorDropVisual visual)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L17) | Creates the compatibility and presentation state of a potential drop target.
`canDrop`:Whether the active source may be delivered to the target.
`visual`:The standard visual requested for the target. | — | — | -| [`public static Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDropStatus.Accept(Inno.Editor.Interactions.EditorDropVisual visual = Inno.Editor.Interactions.EditorDropVisual.Highlight)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L49) | Creates an accepted drop status with the requested standard target visual.
返回:An accepted drop status.
`visual`:The standard visual drawn while the compatible source hovers the target. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDropStatus.Equals(Inno.Editor.Interactions.EditorDropStatus other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorDropStatus.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorDropStatus.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorDropStatus.operator ==(Inno.Editor.Interactions.EditorDropStatus left, Inno.Editor.Interactions.EditorDropStatus right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorDropStatus.operator !=(Inno.Editor.Interactions.EditorDropStatus left, Inno.Editor.Interactions.EditorDropStatus right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorDropStatus.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Interactions.EditorDropStatus.canDrop { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L28) | Gets whether the active source may be dropped. | — | — | -| [`public static Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDropStatus.rejected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L38) | Gets an incompatible drop status. | — | — | -| [`public Inno.Editor.Interactions.EditorDropVisual Inno.Editor.Interactions.EditorDropStatus.visual { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L33) | Gets the standard target visual. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorDropStatus : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Describes whether and how a target accepts the active drag. | — | — | - -### `Inno.Editor.Interactions.EditorDropVisual` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorDropVisual.Disabled = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L31) | Draw a disabled target visual. | — | — | -| [`Inno.Editor.Interactions.EditorDropVisual.Highlight = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L16) | Draw a content highlight. | — | — | -| [`Inno.Editor.Interactions.EditorDropVisual.InsertAfter = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L26) | Draw an insertion line after the target. | — | — | -| [`Inno.Editor.Interactions.EditorDropVisual.InsertBefore = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L21) | Draw an insertion line before the target. | — | — | -| [`Inno.Editor.Interactions.EditorDropVisual.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L11) | No visual is drawn. | — | — | -| [`public enum Inno.Editor.Interactions.EditorDropVisual : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L6) | Defines the standard visual used for a compatible drop target. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryAvailability` - + + + + + + + + + @@ -909,95 +659,27 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryAvailability.Available()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L34) | Creates an available transition result.
返回:An availability value that permits the transition. | — | — | -| [`public bool Inno.Editor.Interactions.EditorHistoryAvailability.Equals(Inno.Editor.Interactions.EditorHistoryAvailability other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorHistoryAvailability.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorHistoryAvailability.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorHistoryAvailability.operator ==(Inno.Editor.Interactions.EditorHistoryAvailability left, Inno.Editor.Interactions.EditorHistoryAvailability right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorHistoryAvailability.operator !=(Inno.Editor.Interactions.EditorHistoryAvailability left, Inno.Editor.Interactions.EditorHistoryAvailability right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorHistoryAvailability.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public static Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryAvailability.Unavailable(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L48) | Creates an unavailable transition result.
返回:An availability value that rejects the transition.
`message`:The non-empty diagnostic explaining why the transition is unavailable. | — | `System.ArgumentException`:Thrown when message is empty. | -| [`public bool Inno.Editor.Interactions.EditorHistoryAvailability.isAvailable { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L21) | Gets whether the requested transition is currently available. | — | — | -| [`public string Inno.Editor.Interactions.EditorHistoryAvailability.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L26) | Gets the diagnostic explaining why the transition is unavailable, or an empty string when available. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorHistoryAvailability : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Describes whether a history change can currently move in one direction. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryChange` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorHistoryChange.EditorHistoryChange(string kind, Inno.Editor.Interactions.EditorHistoryPayload payload, string? mergeKey = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L30) | Creates a neutral history change.
`kind`:The stable globally unique handler protocol identifier.
`payload`:The immutable neutral payload interpreted by the handler.
`mergeKey`:An optional stable key used by the handler to merge adjacent changes. | — | `System.ArgumentException`:Thrown when kind is empty.
`System.ArgumentNullException`:Thrown when payload is null. | -| [`public void Inno.Editor.Interactions.EditorHistoryChange.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L73) | Releases the payload storage owned by this history change. | — | — | -| [`public string Inno.Editor.Interactions.EditorHistoryChange.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L44) | Gets the stable globally unique handler protocol identifier. | — | — | -| [`public string? Inno.Editor.Interactions.EditorHistoryChange.mergeKey { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L54) | Gets the optional stable key used to merge adjacent changes to the same logical value. | — | — | -| [`public Inno.Editor.Interactions.EditorHistoryPayload Inno.Editor.Interactions.EditorHistoryChange.payload { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L49) | Gets the immutable neutral payload interpreted by the active handler generation. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryChange : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L8) | Describes one neutral reload-safe mutation interpreted by an attribute-discovered history handler. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryContext` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorHistoryContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L23) | Gets the passive editor context for the active project. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorHistoryContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L28) | Gets the active interaction entry point used for selection and feature coordination. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L10) | Provides current-generation editor services to a history change handler. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryDirection` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorHistoryDirection.Redo = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L16) | Restores the state produced by the committed change. | — | — | -| [`Inno.Editor.Interactions.EditorHistoryDirection.Undo = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L11) | Restores the state that existed before the change was committed. | — | — | -| [`public enum Inno.Editor.Interactions.EditorHistoryDirection : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L6) | Identifies the direction in which an editor history change is being applied. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryHandler` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorHistoryHandler.EditorHistoryHandler()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryHandler.Apply(Inno.Editor.Interactions.EditorHistoryContext context, Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L46) | Atomically applies a neutral history change in the requested direction.
返回:The transition result. A failure must leave the feature in its original state.
`context`:The current-generation editor services.
`change`:The neutral history change.
`direction`:The requested Undo or Redo direction. | — | — | -| [`protected abstract Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryHandler.Query(Inno.Editor.Interactions.EditorHistoryContext context, Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L25) | Determines whether a neutral change can currently transition in the requested direction.
返回:The current transition availability.
`context`:The current-generation editor services.
`change`:The neutral history change.
`direction`:The requested Undo or Redo direction. | — | — | -| [`protected static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryHandler.StateIntegrityFailure(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L85) | Creates a failed result for a transition whose compensation also failed.
返回:A failure that faults the owning history.
`message`:The diagnostic containing both the transition and compensation failures. | — | — | -| [`protected virtual bool Inno.Editor.Interactions.EditorHistoryHandler.TryMerge(Inno.Editor.Interactions.EditorHistoryChange older, Inno.Editor.Interactions.EditorHistoryChange newer, out Inno.Editor.Interactions.EditorHistoryChange? merged)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L67) | Attempts to merge two adjacent changes to the same logical value.
返回:true when merged replaces both input changes.
`older`:The existing older history change.
`newer`:The newly recorded adjacent history change.
`merged`:The independently owned merged change when successful. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorHistoryHandler`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L8) | Interprets neutral editor history payloads for one current-generation feature protocol. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryHandlerAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorHistoryHandlerAttribute.EditorHistoryHandlerAttribute(string kind)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L20) | Creates a history handler registration.
`kind`:The stable globally unique change protocol identifier. | — | `System.ArgumentException`:Thrown when kind is empty. | -| [`public string Inno.Editor.Interactions.EditorHistoryHandlerAttribute.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L29) | Gets the stable globally unique change protocol identifier. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryHandlerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L9) | Registers a stateless editor history handler for one stable change protocol. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryOptions` - @@ -1005,37 +687,12 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorHistoryOptions.EditorHistoryOptions()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public string? Inno.Editor.Interactions.EditorHistoryOptions.cacheDirectory { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L33) | Gets or initializes the optional directory that owns temporary history payloads for this editor session. | — | — | -| [`public int Inno.Editor.Interactions.EditorHistoryOptions.inlinePayloadThreshold { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L28) | Gets or initializes the payload size at which immutable bytes are moved to the temporary disk store. | — | — | -| [`public long Inno.Editor.Interactions.EditorHistoryOptions.maxDiskBytes { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L23) | Gets or initializes the maximum payload bytes retained in the temporary disk store. | — | — | -| [`public int Inno.Editor.Interactions.EditorHistoryOptions.maxEntries { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L13) | Gets or initializes the maximum number of committed top-level entries retained in memory. | — | — | -| [`public long Inno.Editor.Interactions.EditorHistoryOptions.maxResidentBytes { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L18) | Gets or initializes the maximum estimated resident payload bytes retained by the history. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryOptions`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L8) | Configures retention and payload storage for one editor history. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryPayload` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Interactions.EditorHistoryPayload.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L90) | Releases the resident or temporary disk storage owned by this payload. | — | — | -| [`public static Inno.Editor.Interactions.EditorHistoryPayload Inno.Editor.Interactions.EditorHistoryPayload.FromBytes(System.ReadOnlySpan bytes)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L55) | Creates an immutable history payload by copying the supplied bytes.
返回:A new independently owned payload.
`bytes`:The neutral bytes to retain for future Undo and Redo transitions. | — | — | -| [`public byte[] Inno.Editor.Interactions.EditorHistoryPayload.ReadBytes()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L66) | Reads the complete immutable payload into a newly allocated byte array.
返回:A copy of the payload bytes. | — | `System.ObjectDisposedException`:Thrown after the payload has been disposed. | -| [`public bool Inno.Editor.Interactions.EditorHistoryPayload.isStoredOnDisk { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L40) | Gets whether the payload is retained in the temporary disk store instead of resident memory. | — | — | -| [`public long Inno.Editor.Interactions.EditorHistoryPayload.length { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L35) | Gets the number of immutable bytes represented by this payload. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryPayload : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L9) | Owns immutable neutral bytes used by a reload-safe editor history change. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryResult` - @@ -1048,40 +705,63 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public bool Inno.Editor.Interactions.EditorHistoryResult.Equals(Inno.Editor.Interactions.EditorHistoryResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorHistoryResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryResult.Failure(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L59) | Creates a failed history result without changing the owning history stack.
返回:A result representing a rejected or failed transition.
`message`:The non-empty diagnostic explaining why the transition could not complete. | — | `System.ArgumentException`:Thrown when message is empty. | -| [`public override int Inno.Editor.Interactions.EditorHistoryResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorHistoryResult.operator ==(Inno.Editor.Interactions.EditorHistoryResult left, Inno.Editor.Interactions.EditorHistoryResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorHistoryResult.operator !=(Inno.Editor.Interactions.EditorHistoryResult left, Inno.Editor.Interactions.EditorHistoryResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryResult.Success()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L45) | Creates a successful history result.
返回:A result representing a completed transition. | — | — | -| [`public override string Inno.Editor.Interactions.EditorHistoryResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public string Inno.Editor.Interactions.EditorHistoryResult.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L37) | Gets the diagnostic message associated with a failed transition, or an empty string after success. | — | — | -| [`public bool Inno.Editor.Interactions.EditorHistoryResult.statePreserved { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L32) | Gets whether a failed transition restored the domain state that existed before the attempt. | Successful results always preserve a valid state. A failed result with this value set to false faults the owning history because the transition can no longer be retried safely. | — | -| [`public bool Inno.Editor.Interactions.EditorHistoryResult.succeeded { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L23) | Gets whether the requested history transition completed successfully. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorHistoryResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Describes the outcome of applying, undoing, or redoing an editor history operation. | — | — | - -### `Inno.Editor.Interactions.EditorHistoryTransaction` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Interactions.EditorHistoryTransaction.Commit()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L36) | Commits all recorded child operations as one atomic history entry. | — | `System.InvalidOperationException`:Thrown when the transaction is no longer active. | -| [`public void Inno.Editor.Interactions.EditorHistoryTransaction.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L66) | Rolls back an uncommitted transaction before releasing it. | — | — | -| [`public Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryTransaction.Rollback()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L52) | Reverts every operation recorded by the transaction and does not add a history entry.
返回:The result of the atomic rollback attempt. | — | `System.InvalidOperationException`:Thrown when the transaction is no longer active. | -| [`public string Inno.Editor.Interactions.EditorHistoryTransaction.name { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L26) | Gets the user-facing name assigned to the transaction. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorHistoryTransaction : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L8) | Collects several history operations into one atomic undo and redo entry. | — | — | - -### `Inno.Editor.Interactions.EditorInteraction` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1094,36 +774,13 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorInteraction.BeginDrag(Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L205) | Begins a managed drag originating from this area.
返回:The source object's domain-qualified transient identity.
`data`:The managed source data. | — | — | -| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildMenu()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L154) | Builds the complete contextual menu for this area and target.
返回:The immutable resolved menu model. | — | — | -| [`public Inno.Editor.Interactions.EditorToolbarModel Inno.Editor.Interactions.EditorInteraction.BuildToolbar()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L162) | Builds the complete compact toolbar for this area and target.
返回:The immutable resolved toolbar model. | — | — | -| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorInteraction.Drop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L237) | Delivers a managed drop to this target.
返回:The completed drop result.
`identity`:The active domain-qualified runtime identity.
`placement`:The requested placement relative to the target. | — | — | -| [`public void Inno.Editor.Interactions.EditorInteraction.Enqueue(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L109) | Queues an action until the current UI traversal completes.
`action`:The stable action name.
`argument`:An optional action argument. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Execute(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L92) | Executes an action for this area and target.
返回:true when a visible and enabled action executed; otherwise, false.
`action`:The stable action name.
`argument`:An optional action argument. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public void Inno.Editor.Interactions.EditorInteraction.Focus()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L44) | Marks this area and target as the active keyboard context. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.IsActive(string action)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L146) | Gets whether an action owns an active multi-frame operation for this target.
返回:true when the action is active for this exact target; otherwise, false.
`action`:The stable action name. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Present(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L129) | Presents an active action in place of this target's normal content.
返回:true when the action presented content; otherwise, false.
`action`:The stable action name.
`argument`:Optional presentation data supplied by the current view. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorInteraction.Query(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L72) | Queries an action for this area and target.
返回:The current action state.
`action`:The stable action name.
`argument`:An optional action argument. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorInteraction.QueryDrop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L219) | Queries this handle as a drop target.
返回:The drop compatibility and visual state.
`identity`:The active domain-qualified runtime identity.
`placement`:The requested placement relative to the target. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Select()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L52) | Selects this target, or clears selection when the target is null.
返回:true when the built-in selection action executed; otherwise, false. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L194) | Gets the currently active drag identity, if its source remains registered.
返回:true when a live drag source exists; otherwise, false.
`identity`:The active domain-qualified runtime identity when available. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetShortcut(string action, out Inno.Editor.Interactions.HotKeyGesture gesture)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L179) | Resolves the shortcut displayed for an action in this area.
返回:true when a compatible shortcut exists; otherwise, false.
`action`:The stable action name.
`gesture`:The resolved gesture when successful. | — | `System.ArgumentException`:Thrown when action is empty. | -| [`public string Inno.Editor.Interactions.EditorInteraction.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L28) | Gets the stable interaction area. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.isSelected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L38) | Gets whether this handle's target is the current editor selection. | — | — | -| [`public object? Inno.Editor.Interactions.EditorInteraction.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L33) | Gets the optional target represented by this handle. | — | — | -| [`public readonly struct Inno.Editor.Interactions.EditorInteraction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L10) | Provides a lightweight fluent handle for one interaction area and optional target. | — | — | - -### `Inno.Editor.Interactions.EditorInteractionRuntime` - @@ -1139,195 +796,36 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IEnumerable hostServices)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L98) | Creates an interaction runtime with stable host-owned extension services.
`context`:The passive editor context shared with the presentation backend.
`hostServices`:Stable host-owned services that editor extension constructors may request by assignable contract.
`logs`:The host-owned logging router used by editor infrastructure.
`types`:The type catalog that owns editor extension generations. | — | `System.ArgumentNullException`:Thrown when context or hostServices is null. | -| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L71) | Creates an interaction runtime for an existing passive editor context.
`context`:The passive editor context shared with the presentation backend.
`types`:The type catalog that owns editor extension generations.
`logs`:The host-owned logging router used by editor infrastructure. | — | `System.ArgumentNullException`:Thrown when context or logs is null. | -| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(string projectDirectory, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, Inno.Editor.Core.EditorKeyboardPolicy keyboard)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L46) | Creates an interaction runtime for one project.
`projectDirectory`:The project root containing Assets and Library.
`types`:The type catalog that owns editor extension generations.
`logs`:The host-owned logging router used by editor infrastructure.
`keyboard`:The explicit immutable product keyboard conventions. | — | — | -| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L248) | Releases the resources owned by this implementation. | — | `System.AggregateException`:Thrown after all shutdown stages have been attempted when one or more stages failed.
`Inno.Core.Execution.RetirementPendingException`:A stage still owns live work. This runtime retains all remaining stages and rejects new updates. | -| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.Flush()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L194) | Flushes actions queued during the current presentation traversal. | — | — | -| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L232) | Dispatches an unhandled keyboard event through contextual shortcuts.
`keyEvent`:The keyboard event received from the application event stream. | — | — | -| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.PrepareShutdown()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L220) | Freezes automatic extension-state persistence and writes the final state before modules begin shutting down. | This operation is idempotent. After it succeeds, later disposal cannot recapture transient teardown state such as an empty scene workspace. | `System.ObjectDisposedException`:Thrown after this runtime has been disposed. | -| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.SaveState()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L203) | Captures every stateful active module and panel and atomically flushes changed project state to disk. | — | `System.ObjectDisposedException`:Thrown after this runtime has been disposed. | -| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Start()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L162) | Starts value processing after validating the current state. | — | — | -| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L179) | Recomputes owned state from the current validated inputs.
`frame`:The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorInteractionRuntime.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L128) | Gets the active presentation-independent interaction entry point. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractionRuntime.modals { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L145) | Gets active modal extensions in deterministic order. | — | — | -| [`public int Inno.Editor.Interactions.EditorInteractionRuntime.panelCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L157) | Gets the number of active dockable panels. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractionRuntime.panels { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L133) | Gets active dockable panel extensions in deterministic order. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorInteractionRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L17) | Hosts attribute-discovered editor extensions without depending on a presentation backend. | — | — | - -### `Inno.Editor.Interactions.EditorInteractions` - + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L108) | Starts an isolated temporary Undo and Redo branch while retaining the current editing branch.
返回:A scope that restores the retained history branch when disposed. | Disposing the returned scope releases every temporary operation and restores the retained branch. This host-level boundary is intended for transient editor sessions such as Play Mode. | `System.InvalidOperationException`:Thrown during an Undo, Redo, transaction, or another isolated branch. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.ClosePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L284) | Closes one panel in the active extension generation without toggling its current state.
返回:true when an available panel was found.
`panelId`:The stable panel identifier to resolve. | — | `System.ArgumentException`:Thrown when panelId is empty. | -| [`public Inno.Editor.Interactions.EditorInteraction Inno.Editor.Interactions.EditorInteractions.For(string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L137) | Creates a lightweight interaction handle for one area and optional target.
返回:A lightweight interaction handle.
`area`:The stable interaction area.
`target`:The optional object represented by the area. | — | `System.ArgumentException`:Thrown when area is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.OpenPanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L266) | Opens and requests presentation focus for one panel in the active extension generation.
返回:true when an available panel was found.
`panelId`:The stable panel identifier to resolve. | — | `System.ArgumentException`:Thrown when panelId is empty. | -| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L148) | Replaces the editor selection after closing presentations owned by other targets.
`target`:The target to select, or null to clear the selection. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TogglePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L197) | Toggles one panel in the currently active extension generation.
返回:true when an available panel was found and toggled.
`panelId`:The stable panel identifier to resolve. | — | `System.ArgumentException`:Thrown when panelId is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L183) | Gets the active drag identity used to select the matching native payload domain.
返回:true when a live drag source exists; otherwise, false.
`identity`:The active runtime identity when one remains valid. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetDragSource(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? source)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L169) | Resolves the live source for an active runtime identity.
返回:true when the identity is active and resolves to a live source; otherwise, false.
`identity`:The domain-qualified runtime identity carried by the drag protocol.
`source`:The currently registered drag source when resolution succeeds. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetModule(out TModule? module) where TModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L86) | Resolves an active feature module for immediate use in the current Editor callback.
返回:True when the active generation supplies the requested module.
`module`:The started, non-quarantined module, or null when unavailable. | Do not retain the result across callbacks or generation changes. Candidate modules are never exposed. | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.IdentityDomainId domainId, System.Guid persistentId, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L242) | Resolves a persistent identity through one explicitly selected Editor identity domain.
返回:true when the stable identity resolves in the requested domain.
`domainId`:Identity domain that owns the object and any replacement generation.
`persistentId`:Stable object identity to resolve inside that domain.
`target`:Receives the current live object, or null when the domain or object is unavailable. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L216) | Resolves a domain-qualified runtime identity through the Editor's complete identity-domain set.
返回:true when the exact identity resolves in its owning domain.
`identity`:Runtime identity captured from a live Editor target.
`target`:Receives the current live object, or null when its domain or generation is unavailable. | — | — | -| [`public Inno.Editor.Interactions.IEditorDocumentService Inno.Editor.Interactions.EditorInteractions.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L69) | Gets the headless reload-safe document lifetime used by dedicated asset editors and Inspectors. | — | — | -| [`public string Inno.Editor.Interactions.EditorInteractions.focusedArea { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L115) | Gets the area that most recently received keyboard focus. | — | — | -| [`public object? Inno.Editor.Interactions.EditorInteractions.focusedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L120) | Gets the target associated with the focused area. | — | — | -| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorInteractions.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L64) | Gets the transactional Undo and Redo history owned by this editor runtime. | — | — | -| [`public Inno.Editor.Interactions.EditorSelectionState Inno.Editor.Interactions.EditorInteractions.selection { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L57) | Gets the shared read-only editor selection state. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorInteractions : Inno.Editor.Interactions.IEditorSelectionCoordinator, Inno.Editor.Interactions.IEditorHistoryIsolation`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L18) | Provides the single presentation-independent entry point for editor actions, menus, selection, focus, and drag-and-drop. | — | — | - -### `Inno.Editor.Interactions.EditorMenuAttribute` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorMenuAttribute.EditorMenuAttribute(string area, string path, int order = 0, bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L29) | Creates a static placement for the annotated action on an exact menu surface.
`area`:The exact interaction area whose menu receives the action.
`path`:The slash-delimited path used to create parent menus and the leaf label.
`order`:The stable ordering value among sibling placements.
`separatorBefore`:Whether a visual separator should precede the leaf placement. | — | `System.ArgumentException`:Thrown when area or path is empty. | -| [`public string Inno.Editor.Interactions.EditorMenuAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L48) | Gets the menu area. | — | — | -| [`public int Inno.Editor.Interactions.EditorMenuAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L58) | Gets the stable menu ordering value. | — | — | -| [`public string Inno.Editor.Interactions.EditorMenuAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L53) | Gets the slash-delimited menu path. | — | — | -| [`public bool Inno.Editor.Interactions.EditorMenuAttribute.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L63) | Gets whether a separator is rendered before the item. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L9) | Places an editor action at an arbitrary path on a menu surface. | — | — | - -### `Inno.Editor.Interactions.EditorMenuBuilder` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorMenuBuilder.EditorMenuBuilder()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Editor.Interactions.EditorMenuBuilder.Add(string path, string actionId, int order = 0, bool separatorBefore = false, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L57) | Adds a dynamic action placement to the menu currently being constructed.
`path`:The slash-delimited path used to create parent menus and the leaf label.
`actionId`:The stable action identifier invoked by the leaf.
`order`:The stable ordering value among sibling placements.
`separatorBefore`:Whether a visual separator should precede the leaf placement.
`argument`:An optional argument captured for this menu generation. | — | `System.ArgumentException`:Thrown when path or actionId is empty. | -| [`public void Inno.Editor.Interactions.EditorMenuBuilder.AddGroup(string path, int order = 0, bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L26) | Declares presentation for an intermediate menu group without inventing a no-op action.
`path`:The slash-delimited path of the group.
`order`:The stable ordering value among sibling groups and items.
`separatorBefore`:Whether a separator precedes this group at its own level. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuBuilder`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L9) | Collects dynamic menu item placements. | — | — | - -### `Inno.Editor.Interactions.EditorMenuContext` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorMenuContext.EditorMenuContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L33) | Creates a contextual menu request.
`editor`:The shared passive editor context.
`interactions`:The active interaction entry point.
`area`:The exact menu area being requested.
`target`:The optional object the menu operates on. | — | `System.ArgumentNullException`:Thrown when editor or interactions is null.
`System.ArgumentException`:Thrown when area is empty. | -| [`public string Inno.Editor.Interactions.EditorMenuContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L59) | Gets the requested menu area. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorMenuContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L49) | Gets the shared passive editor context. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorMenuContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L54) | Gets the active interaction entry point. | — | — | -| [`public object? Inno.Editor.Interactions.EditorMenuContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L64) | Gets the contextual menu target. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L10) | Provides contextual state while constructing an editor menu. | — | — | - -### `Inno.Editor.Interactions.EditorMenuItem` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Editor.Interactions.EditorMenuItem.actionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L64) | Gets the stable action identifier consumed by a presentation backend. | — | — | -| [`public object? Inno.Editor.Interactions.EditorMenuItem.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L90) | Gets the generation-local action argument consumed by a presentation backend. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorMenuItem.children { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L84) | Gets child menu nodes. | — | — | -| [`public string Inno.Editor.Interactions.EditorMenuItem.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L58) | Gets the visible node label. | — | — | -| [`public int Inno.Editor.Interactions.EditorMenuItem.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L69) | Gets the stable ordering value. | — | — | -| [`public bool Inno.Editor.Interactions.EditorMenuItem.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L74) | Gets whether a separator precedes this node. | — | — | -| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorMenuItem.status { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L79) | Gets the current command presentation state. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuItem`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L11) | Represents one immutable contextual menu node. | — | — | - -### `Inno.Editor.Interactions.EditorMenuModel` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorMenuModel.EditorMenuModel(System.Collections.Generic.IReadOnlyList? items)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L17) | Creates a complete immutable menu tree from resolved root nodes.
`items`:The root nodes in stable display order, or null for an empty menu. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorMenuModel.items { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L25) | Gets root menu nodes in display order. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuModel`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L9) | Contains a complete immutable menu tree. | — | — | - -### `Inno.Editor.Interactions.EditorMenuSource` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorMenuSource.EditorMenuSource()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract void Inno.Editor.Interactions.EditorMenuSource.Build(Inno.Editor.Interactions.EditorMenuContext context, Inno.Editor.Interactions.EditorMenuBuilder builder)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L19) | Adds context-dependent placements for the supplied menu request.
`context`:The requested menu surface, target, and shared editor context.
`builder`:The collector that receives dynamic action placements. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorMenuSource`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L8) | Contributes dynamic entries to one or more editor menu surfaces. | — | — | - -### `Inno.Editor.Interactions.EditorMenuSourceAttribute` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorMenuSourceAttribute.EditorMenuSourceAttribute(string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L23) | Creates a dynamic menu-source registration for an exact interaction surface.
`area`:The exact menu area that receives placements from the source.
`priority`:The ordering priority used when multiple dynamic sources contribute to the surface. | — | `System.ArgumentException`:Thrown when area is empty. | -| [`public string Inno.Editor.Interactions.EditorMenuSourceAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L36) | Gets the contributed menu area. | — | — | -| [`public int Inno.Editor.Interactions.EditorMenuSourceAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L41) | Gets the provider ordering priority. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorMenuSourceAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L9) | Registers a dynamic editor menu source. | — | — | - -### `Inno.Editor.Interactions.EditorModalExtension` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L95) | Safely draws the modal body and quarantines a failing extension instance.
返回:true when the modal completed drawing without being quarantined.
`context`:The active editor context supplied by the presentation backend. | — | `System.ArgumentNullException`:Thrown when context is null. | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.TryGetPresentation(out Inno.Editor.Interactions.EditorModalExtension.Presentation presentation)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L55) | Safely captures generation-local modal presentation values.
返回:true when every presentation value was read successfully.
`presentation`:The captured immutable presentation, or its default value when the modal is quarantined. | — | — | -| [`public string Inno.Editor.Interactions.EditorModalExtension.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L34) | Gets the stable modal identifier. | — | — | -| [`public int Inno.Editor.Interactions.EditorModalExtension.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L44) | Gets the stable modal ordering value. | — | — | -| [`public string Inno.Editor.Interactions.EditorModalExtension.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L39) | Gets the visible modal title. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorModalExtension`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L11) | Describes one active modal extension. | — | — | - -### `Inno.Editor.Interactions.EditorModalExtension.Presentation` - @@ -1343,27 +841,6 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorModalExtension.Presentation.Presentation(bool isVisible, bool blocksInteraction, bool canMove, bool canResize, bool allowScrolling, System.Numerics.Vector2 initialSize, System.Numerics.Vector2 minimumSize)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L142) | Creates an immutable modal presentation snapshot.
`isVisible`:Whether the modal should currently be visible.
`blocksInteraction`:Whether the modal blocks regular editor interaction.
`canMove`:Whether the modal window can be moved.
`canResize`:Whether the modal window can be resized.
`allowScrolling`:Whether the modal window owns overflow scrolling.
`initialSize`:The initial size in unscaled editor units.
`minimumSize`:The minimum size in unscaled editor units. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.Equals(Inno.Editor.Interactions.EditorModalExtension.Presentation other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorModalExtension.Presentation.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorModalExtension.Presentation.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorModalExtension.Presentation.operator ==(Inno.Editor.Interactions.EditorModalExtension.Presentation left, Inno.Editor.Interactions.EditorModalExtension.Presentation right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorModalExtension.Presentation.operator !=(Inno.Editor.Interactions.EditorModalExtension.Presentation left, Inno.Editor.Interactions.EditorModalExtension.Presentation right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorModalExtension.Presentation.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L183) | Gets whether overflow scrolling belongs to the modal window. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.blocksInteraction { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L168) | Gets whether the modal blocks regular editor interaction. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.canMove { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L173) | Gets whether the modal window can be moved. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.canResize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L178) | Gets whether the modal window can be resized. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.Interactions.EditorModalExtension.Presentation.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L188) | Gets the initial size in unscaled editor units. | — | — | -| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L163) | Gets whether the modal should currently be visible. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.Interactions.EditorModalExtension.Presentation.minimumSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L193) | Gets the minimum size in unscaled editor units. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorModalExtension.Presentation : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Stores immutable modal window policy values read from one extension generation. | — | — | - -### `Inno.Editor.Interactions.EditorPanelExtension` - @@ -1372,90 +849,24 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public bool Inno.Editor.Interactions.EditorPanelExtension.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L118) | Safely draws the panel body and quarantines a failing extension instance.
返回:true when the panel completed drawing without being quarantined.
`context`:The active editor context supplied by the presentation backend. | — | `System.ArgumentNullException`:Thrown when context is null. | -| [`public bool Inno.Editor.Interactions.EditorPanelExtension.TakeFocusRequest()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L64) | Consumes a pending request to focus this panel window.
返回:true once for each accepted focus request. | — | — | -| [`public bool Inno.Editor.Interactions.EditorPanelExtension.TryGetWindowPresentation(out bool useWindowPadding, out bool allowScrolling, out System.Numerics.Vector2 initialSize)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L81) | Safely reads the panel window-presentation policy through the active extension boundary.
返回:true when the policy was read without quarantining the panel.
`useWindowPadding`:The requested padding policy, or the safe default when the panel is quarantined.
`allowScrolling`:The requested scrolling policy, or the safe default when the panel is quarantined.
`initialSize`:The preferred first-use floating size, or zero when native sizing is requested. | — | — | -| [`public string Inno.Editor.Interactions.EditorPanelExtension.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L37) | Gets the stable panel identifier. | — | — | -| [`public bool Inno.Editor.Interactions.EditorPanelExtension.isOpen { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L52) | Gets or sets whether this panel is open in the current extension generation. | — | — | -| [`public int Inno.Editor.Interactions.EditorPanelExtension.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L47) | Gets the stable panel ordering value. | — | — | -| [`public string Inno.Editor.Interactions.EditorPanelExtension.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L42) | Gets the visible panel title. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorPanelExtension`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L11) | Describes one active dockable panel extension. | — | — | - -### `Inno.Editor.Interactions.EditorPlanarNavigation` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorPlanarNavigation.EditorPlanarNavigation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Editor.Interactions.EditorPlanarNavigation.Cancel()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L67) | Releases capture when a canvas closes or a gesture is cancelled. | — | — | -| [`public bool Inno.Editor.Interactions.EditorPlanarNavigation.Update(bool hovered, bool primaryPressed, bool middlePressed, bool primaryDown, bool middleDown, bool alt, bool allowAltPrimary = true)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L45) | Advances pointer capture using logical button state rather than window-local release events.
返回:Whether this canvas owns the current pointer gesture.
`hovered`:Whether a new gesture may start in this canvas.
`primaryPressed`:Whether the primary button was pressed this frame.
`middlePressed`:Whether the middle button was pressed this frame.
`primaryDown`:Current global primary button state.
`middleDown`:Current global middle button state.
`alt`:Whether Alt is held when the gesture starts.
`allowAltPrimary`:Whether Alt-primary belongs to pan rather than another navigation mode. | — | — | -| [`public static float Inno.Editor.Interactions.EditorPlanarNavigation.WheelFactor(float wheel, float sensitivity = 0.16)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L84) | Converts wheel input to the common exponential magnification used by Editor canvases.
返回:A finite positive magnification factor; positive wheel magnifies content.
`wheel`:Logical wheel units, independent of framebuffer DPI.
`sensitivity`:Positive exponential sensitivity. | — | `System.ArgumentOutOfRangeException`:Input is non-finite or sensitivity is not positive. | -| [`public static System.Numerics.Vector2 Inno.Editor.Interactions.EditorPlanarNavigation.ZoomOrigin(System.Numerics.Vector2 origin, System.Numerics.Vector2 pivot, float previousScale, float nextScale)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L116) | Changes the screen-space origin so zoom preserves the same content point beneath the cursor.
返回:The origin preserving the pivot's content coordinate.
`origin`:Current logical-pixel content origin.
`pivot`:Logical-pixel cursor position relative to the canvas.
`previousScale`:Previous positive content magnification.
`nextScale`:New positive content magnification after clamping. | — | `System.ArgumentOutOfRangeException`:An input is non-finite or a scale is not positive. | -| [`public bool Inno.Editor.Interactions.EditorPlanarNavigation.isPanning { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L16) | Gets whether this canvas owns a pan gesture, including while the pointer is outside its bounds. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorPlanarNavigation`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L9) | Shares captured planar gestures and mouse-anchored zoom math between scene and graph canvases. | — | — | - -### `Inno.Editor.Interactions.EditorPresentationAction`2` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorPresentationAction.EditorPresentationAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L346) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract bool Inno.Editor.Interactions.EditorPresentationAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L371) | Presents active content using strongly typed presentation data.
返回:true when content was presented.
`context`:The typed presentation request. | — | — | -| [`protected override sealed bool Inno.Editor.Interactions.EditorPresentationAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L358) | Presents this action through the current editor interaction surface.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorPresentationAction where TTarget : class : Inno.Editor.Interactions.EditorAction where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L346) | Defines a target action with a typed presentation-only argument. | — | — | - -### `Inno.Editor.Interactions.EditorSelectionState` - + + + + + + + + + + + + + + - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorSelectionState.EditorSelectionState()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public bool Inno.Editor.Interactions.EditorSelectionState.TryGet(out TTarget? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs#L67) | Tries to read the current target as a requested type.
返回:true when the current target is compatible.
`target`:Typed target when successful. | — | — | -| [`public object? Inno.Editor.Interactions.EditorSelectionState.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs#L16) | Gets the selected target, or null when nothing is selected. | — | — | -| [`public ulong Inno.Editor.Interactions.EditorSelectionState.version { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs#L21) | Gets a monotonically increasing selection change version. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorSelectionState`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs#L9) | Stores editor-wide object selection state. | — | — | - -### `Inno.Editor.Interactions.EditorShortcutAttribute` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorShortcutAttribute.EditorShortcutAttribute(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool primary = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L25) | Creates a shortcut that can be dispatched from any focused interaction surface.
`key`:The non-modifier key that triggers the action.
`modifiers`:The exact additional modifier keys required by the gesture.
`primary`:Whether the platform primary modifier, Command on macOS or Control elsewhere, is required. | — | — | -| [`public Inno.Editor.Interactions.EditorShortcutAttribute.EditorShortcutAttribute(string area, Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool primary = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L49) | Creates a shortcut scoped to an exact interaction area.
`area`:The exact focused area that accepts the shortcut, or an empty string for any area.
`key`:The non-modifier key that triggers the action.
`modifiers`:The exact additional modifier keys required by the gesture.
`primary`:Whether the platform primary modifier, Command on macOS or Control elsewhere, is required. | — | — | -| [`public string Inno.Editor.Interactions.EditorShortcutAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L64) | Gets the optional exact interaction area. | — | — | -| [`public Inno.Core.Input.KeyCode Inno.Editor.Interactions.EditorShortcutAttribute.key { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L69) | Gets the shortcut key. | — | — | -| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorShortcutAttribute.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L74) | Gets additional shortcut modifiers. | — | — | -| [`public bool Inno.Editor.Interactions.EditorShortcutAttribute.primary { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L79) | Gets whether the platform primary modifier is required. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorShortcutAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L11) | Associates an editor action with a keyboard shortcut. | — | — | - -### `Inno.Editor.Interactions.EditorToolbarIcon` - @@ -1463,37 +874,12 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorToolbarIcon.Edit = 5`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L36) | Returns to an editing state. | — | — | -| [`Inno.Editor.Interactions.EditorToolbarIcon.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L11) | No symbol is requested. | — | — | -| [`Inno.Editor.Interactions.EditorToolbarIcon.Pause = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L26) | Pauses an operation or simulation. | — | — | -| [`Inno.Editor.Interactions.EditorToolbarIcon.Play = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L16) | Starts an operation or simulation. | — | — | -| [`Inno.Editor.Interactions.EditorToolbarIcon.Step = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L31) | Advances a paused operation by one step. | — | — | -| [`Inno.Editor.Interactions.EditorToolbarIcon.Stop = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L21) | Stops an operation or simulation. | — | — | -| [`public enum Inno.Editor.Interactions.EditorToolbarIcon : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L6) | Identifies a presentation-independent symbol for an editor toolbar command. | — | — | - -### `Inno.Editor.Interactions.EditorToolbarItem` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Editor.Interactions.EditorToolbarItem.actionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L25) | Gets the stable action dispatched when this item is pressed. | — | — | -| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItem.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L30) | Gets the resolved presentation-independent symbol. | — | — | -| [`public int Inno.Editor.Interactions.EditorToolbarItem.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L40) | Gets the stable ordering value. | — | — | -| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorToolbarItem.status { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L45) | Gets the current action availability and checked state. | — | — | -| [`public string Inno.Editor.Interactions.EditorToolbarItem.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L35) | Gets the resolved contextual tooltip. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorToolbarItem`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L6) | Describes one resolved toolbar command. | — | — | - -### `Inno.Editor.Interactions.EditorToolbarItemAttribute` - @@ -1501,61 +887,20 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorToolbarItemAttribute.EditorToolbarItemAttribute(string area, Inno.Editor.Interactions.EditorToolbarIcon icon, string tooltip, int order = 0, Inno.Editor.Interactions.EditorToolbarIcon activeIcon = Inno.Editor.Interactions.EditorToolbarIcon.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L37) | Creates a toolbar placement for the annotated targetless action.
`area`:The exact interaction area whose toolbar receives the action.
`icon`:The symbol shown while the action is not checked.
`tooltip`:The fallback tooltip shown when the action has no contextual display name.
`order`:The stable ordering value within the toolbar.
`activeIcon`:The symbol shown while the action is checked, or Inno.Editor.Interactions.EditorToolbarIcon.None to retain icon. | — | `System.ArgumentException`:Thrown when area or tooltip is empty, or when icon is Inno.Editor.Interactions.EditorToolbarIcon.None.
`System.ArgumentOutOfRangeException`:Thrown when an icon value is not defined. | -| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItemAttribute.activeIcon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L75) | Gets the optional replacement symbol shown while the action is checked. | — | — | -| [`public string Inno.Editor.Interactions.EditorToolbarItemAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L65) | Gets the exact toolbar interaction area. | — | — | -| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItemAttribute.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L70) | Gets the symbol shown while the action is not checked. | — | — | -| [`public int Inno.Editor.Interactions.EditorToolbarItemAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L85) | Gets the stable ordering value. | — | — | -| [`public string Inno.Editor.Interactions.EditorToolbarItemAttribute.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L80) | Gets the fallback tooltip. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorToolbarItemAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L9) | Places an editor action on a compact toolbar surface. | — | — | - -### `Inno.Editor.Interactions.EditorToolbarModel` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorToolbarModel.items { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarModel.cs#L18) | Gets visible toolbar commands in deterministic display order. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorToolbarModel`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarModel.cs#L8) | Contains an immutable resolved toolbar for one interaction area. | — | — | - -### `Inno.Editor.Interactions.EditorValidationResult` - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorValidationResult.EditorValidationResult(bool isValid, string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Describes whether a requested editor operation is valid and carries a diagnostic when it is rejected.
`isValid`:Whether the requested operation may proceed.
`message`:The user-facing validation diagnostic, or an empty string for a valid result. | — | — | -| [`public void Inno.Editor.Interactions.EditorValidationResult.Deconstruct(out bool isValid, out string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Interactions.EditorValidationResult.Equals(Inno.Editor.Interactions.EditorValidationResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorValidationResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorValidationResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static Inno.Editor.Interactions.EditorValidationResult Inno.Editor.Interactions.EditorValidationResult.Invalid(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L30) | Creates a failed validation result with a user-facing diagnostic.
返回:An invalid result containing the supplied diagnostic.
`message`:The diagnostic that explains why the operation cannot proceed. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorValidationResult.operator ==(Inno.Editor.Interactions.EditorValidationResult left, Inno.Editor.Interactions.EditorValidationResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorValidationResult.operator !=(Inno.Editor.Interactions.EditorValidationResult left, Inno.Editor.Interactions.EditorValidationResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorValidationResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Interactions.EditorValidationResult.isValid { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L13) | Whether the requested operation may proceed. | — | — | -| [`public string Inno.Editor.Interactions.EditorValidationResult.message { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L14) | The user-facing validation diagnostic, or an empty string for a valid result. | — | — | -| [`public static Inno.Editor.Interactions.EditorValidationResult Inno.Editor.Interactions.EditorValidationResult.valid { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L19) | Gets a successful validation result. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorValidationResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Describes whether a requested editor operation is valid and carries a diagnostic when it is rejected.
`isValid`:Whether the requested operation may proceed.
`message`:The user-facing validation diagnostic, or an empty string for a valid result. | — | — | - -### `Inno.Editor.Interactions.EditorViewportCursor` - + + + + + + + + + + + + @@ -1564,20 +909,17 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorViewportCursor.Arrow = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L38) | Uses the normal arrow cursor. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.Crosshair = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L42) | Uses a precise crosshair. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.Hand = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L46) | Uses a hand suitable for panning. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.Hidden = 6`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L62) | Hides the pointer while the tool owns it. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.Move = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L50) | Uses four-direction movement arrows. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.ResizeHorizontal = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L54) | Uses horizontal resizing arrows. | — | — | -| [`Inno.Editor.Interactions.EditorViewportCursor.ResizeVertical = 5`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L58) | Uses vertical resizing arrows. | — | — | -| [`public enum Inno.Editor.Interactions.EditorViewportCursor : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L33) | Selects a presentation-independent cursor requested by a viewport tool. | — | — | - -### `Inno.Editor.Interactions.EditorViewportPointerEvent` - + + + + + + + + + + + @@ -1592,70 +934,9 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorViewportPointerEvent.EditorViewportPointerEvent(int pointerId, Inno.Editor.Interactions.EditorViewportPointerPhase phase, Inno.Core.Mathematics.Vector2 screenPosition, Inno.Core.Mathematics.Vector2 worldPosition, int button, Inno.Core.Input.KeyModifier modifiers)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L134) | Creates one viewport pointer sample.
`pointerId`:Platform pointer identity.
`phase`:Gesture lifecycle phase.
`screenPosition`:Viewport-local pixel position.
`worldPosition`:Corresponding world position.
`button`:Platform button number.
`modifiers`:Active keyboard modifiers. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportPointerEvent.Equals(Inno.Editor.Interactions.EditorViewportPointerEvent other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorViewportPointerEvent.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorViewportPointerEvent.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorViewportPointerEvent.operator ==(Inno.Editor.Interactions.EditorViewportPointerEvent left, Inno.Editor.Interactions.EditorViewportPointerEvent right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorViewportPointerEvent.operator !=(Inno.Editor.Interactions.EditorViewportPointerEvent left, Inno.Editor.Interactions.EditorViewportPointerEvent right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorViewportPointerEvent.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public int Inno.Editor.Interactions.EditorViewportPointerEvent.button { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L173) | Gets the platform button number. | — | — | -| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorViewportPointerEvent.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L178) | Gets active keyboard modifiers. | — | — | -| [`public Inno.Editor.Interactions.EditorViewportPointerPhase Inno.Editor.Interactions.EditorViewportPointerEvent.phase { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L158) | Gets the gesture lifecycle phase. | — | — | -| [`public int Inno.Editor.Interactions.EditorViewportPointerEvent.pointerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L153) | Gets the platform pointer identity. | — | — | -| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportPointerEvent.screenPosition { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L163) | Gets the viewport-local pixel position. | — | — | -| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportPointerEvent.worldPosition { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L168) | Gets the corresponding world position. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorViewportPointerEvent : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Contains one immutable viewport pointer sample in screen and world coordinates. | — | — | - -### `Inno.Editor.Interactions.EditorViewportPointerPhase` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Cancel = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L27) | The platform cancelled the gesture. | — | — | -| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Down = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L15) | The pointer button began a gesture. | — | — | -| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Move = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L19) | The pointer moved or changed hover position. | — | — | -| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Up = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L23) | The pointer button ended a gesture. | — | — | -| [`public enum Inno.Editor.Interactions.EditorViewportPointerPhase : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L10) | Selects the lifecycle phase of one viewport pointer sample. | — | — | - -### `Inno.Editor.Interactions.EditorViewportShortcut` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorViewportShortcut.EditorViewportShortcut(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool repeat = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L82) | Creates one viewport shortcut sample.
`key`:Pressed key.
`modifiers`:Active keyboard modifiers.
`repeat`:Whether the platform generated an auto-repeat press. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportShortcut.Equals(Inno.Editor.Interactions.EditorViewportShortcut other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.EditorViewportShortcut.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.EditorViewportShortcut.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.EditorViewportShortcut.operator ==(Inno.Editor.Interactions.EditorViewportShortcut left, Inno.Editor.Interactions.EditorViewportShortcut right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.EditorViewportShortcut.operator !=(Inno.Editor.Interactions.EditorViewportShortcut left, Inno.Editor.Interactions.EditorViewportShortcut right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Interactions.EditorViewportShortcut.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public Inno.Core.Input.KeyCode Inno.Editor.Interactions.EditorViewportShortcut.key { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L95) | Gets the pressed key. | — | — | -| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorViewportShortcut.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L100) | Gets active keyboard modifiers. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportShortcut.repeat { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L105) | Gets whether the shortcut is an auto-repeat press. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.EditorViewportShortcut : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Contains one presentation-independent shortcut sample for a focused viewport tool. | — | — | - -### `Inno.Editor.Interactions.EditorViewportTool` - + + + @@ -1666,22 +947,6 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Interactions.EditorViewportTool.EditorViewportTool()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L212) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.DrawOverlay(Inno.Editor.Interactions.EditorViewportToolContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L316) | Draws transient gizmos and diagnostics over the viewport.
`context`:Gesture and coordinate context. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerCancel(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L281) | Handles platform cancellation of an active gesture.
`context`:Gesture and coordinate context.
`pointer`:Immutable pointer sample. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerDown(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L233) | Handles a pointer press.
`context`:Gesture and coordinate context.
`pointer`:Immutable pointer sample. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerMove(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L249) | Handles pointer movement or hover.
`context`:Gesture and coordinate context.
`pointer`:Immutable pointer sample. | — | — | -| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerUp(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L265) | Handles a pointer release.
`context`:Gesture and coordinate context.
`pointer`:Immutable pointer sample. | — | — | -| [`public virtual bool Inno.Editor.Interactions.EditorViewportTool.OnShortcut(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportShortcut shortcut)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L302) | Handles a focused viewport shortcut before global action routing.
返回:true when the tool consumed the key.
`context`:Gesture and coordinate context.
`shortcut`:Pressed key sample. | — | — | -| [`public virtual Inno.Editor.Interactions.EditorViewportCursor Inno.Editor.Interactions.EditorViewportTool.cursor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L222) | Gets the cursor requested while this tool is active. | — | — | -| [`public abstract string Inno.Editor.Interactions.EditorViewportTool.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L217) | Gets the globally stable tool identity. | — | — | -| [`public abstract class Inno.Editor.Interactions.EditorViewportTool`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L212) | Defines one presentation-independent, reloadable viewport editing tool. | — | — | - -### `Inno.Editor.Interactions.EditorViewportToolContext` - @@ -1692,22 +957,6 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Interactions.EditorViewportToolContext.BeginHistoryGesture(string name)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L68) | Begins the only history transaction allowed for the current captured gesture.
`name`:User-facing Undo operation name. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolContext.CapturePointer(int pointerId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L45) | Captures one pointer until explicit release or cancellation.
`pointerId`:Platform pointer identity. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolContext.CompleteHistoryGesture(bool commit)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L84) | Commits or rolls back the current gesture transaction exactly once.
`commit`:true to commit; otherwise, roll back. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolContext.ReleasePointer()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L55) | Releases the currently captured pointer. | — | — | -| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportToolContext.ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L106) | Converts viewport-local pixel coordinates to world coordinates.
返回:World coordinates.
`screenPosition`:Viewport-local pixel coordinates. | — | — | -| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportToolContext.WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L117) | Converts world coordinates to viewport-local pixel coordinates.
返回:Viewport-local pixel coordinates.
`worldPosition`:World coordinates. | — | — | -| [`public int? Inno.Editor.Interactions.EditorViewportToolContext.capturedPointerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L32) | Gets the captured pointer identity, or null. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportToolContext.hasHistoryGesture { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L37) | Gets whether a history transaction is active for the current gesture. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportToolContext.hasPointerCapture { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L27) | Gets whether this tool owns a pointer. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorViewportToolContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L9) | Coordinates pointer capture, transforms, and one history transaction for a viewport gesture. | — | — | - -### `Inno.Editor.Interactions.EditorViewportToolSession` - @@ -1718,157 +967,576 @@ Scene 内容历史由独立的 [Inno.Editor.Scene](Inno.Editor.Scene.md) 实现 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.EditorViewportToolSession.EditorViewportToolSession(Inno.Editor.Interactions.IEditorHistory history, Inno.Editor.Interactions.IEditorViewportCoordinateConverter coordinates)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L23) | Creates a viewport tool session.
`history`:History stack used by gesture transactions.
`coordinates`:Current viewport coordinate converter. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolSession.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L115) | Cancels active gesture state and deactivates the tool. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolSession.DrawOverlay()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L106) | Draws the active tool overlay. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportToolSession.HandlePointer(Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L64) | Routes one immutable pointer sample.
返回:true when an active tool accepted the sample.
`pointer`:Pointer sample with precomputed world coordinates. | — | — | -| [`public bool Inno.Editor.Interactions.EditorViewportToolSession.HandleShortcut(Inno.Editor.Interactions.EditorViewportShortcut shortcut)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L97) | Routes one focused keyboard shortcut.
返回:true when consumed.
`shortcut`:Pressed key sample. | — | — | -| [`public void Inno.Editor.Interactions.EditorViewportToolSession.SetTool(Inno.Editor.Interactions.EditorViewportTool? tool)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L46) | Activates a tool after cancelling any current gesture.
`tool`:Tool to activate, or null to deactivate. | — | — | -| [`public Inno.Editor.Interactions.EditorViewportCursor Inno.Editor.Interactions.EditorViewportToolSession.cursor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L38) | Gets the requested cursor for the active tool. | — | — | -| [`public Inno.Editor.Interactions.EditorViewportTool? Inno.Editor.Interactions.EditorViewportToolSession.tool { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L33) | Gets the active tool, or null. | — | — | -| [`public sealed class Inno.Editor.Interactions.EditorViewportToolSession : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L8) | Routes focused viewport input to one active tool while enforcing pointer-capture ownership. | — | — | - -### `Inno.Editor.Interactions.HotKeyGesture` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Interactions.HotKeyGesture.HotKeyGesture(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, string superModifierLabel = "Super")`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L31) | Creates a keyboard gesture with an exact set of modifier keys after symbolic-key normalization.
`key`:The non-modifier key in the gesture.
`modifiers`:The modifier keys required by the gesture.
`superModifierLabel`:The display name of the Super modifier; the neutral name is used for explicitly constructed gestures. | — | `System.ArgumentException`:The modifier label is empty. | -| [`public bool Inno.Editor.Interactions.HotKeyGesture.Equals(Inno.Editor.Interactions.HotKeyGesture other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Interactions.HotKeyGesture.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Interactions.HotKeyGesture.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Interactions.HotKeyGesture.operator ==(Inno.Editor.Interactions.HotKeyGesture left, Inno.Editor.Interactions.HotKeyGesture right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Interactions.HotKeyGesture.operator !=(Inno.Editor.Interactions.HotKeyGesture left, Inno.Editor.Interactions.HotKeyGesture right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static Inno.Editor.Interactions.HotKeyGesture Inno.Editor.Interactions.HotKeyGesture.Primary(Inno.Core.Input.KeyCode key, Inno.Editor.Core.EditorKeyboardPolicy keyboard, Inno.Core.Input.KeyModifier additionalModifiers = Inno.Core.Input.KeyModifier.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L67) | Creates a gesture that includes the product-selected primary modifier.
返回:A product-resolved keyboard gesture.
`key`:The non-modifier key in the gesture.
`keyboard`:The product-selected immutable keyboard policy.
`additionalModifiers`:Additional modifiers combined with the product-selected primary modifier. | — | — | -| [`public override string Inno.Editor.Interactions.HotKeyGesture.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L82) | Formats the gesture as a human-readable editor menu shortcut label.
返回:A product-resolved textual representation of the gesture. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorAction : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L8) | Defines one automatically discovered editor operation and owns its complete multi-frame lifecycle. | — | — | +| [`public bool Inno.Editor.Interactions.EditorAction.isActive { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L17) | Gets whether the action currently owns an active multi-frame operation. | — | — | +| [`public virtual System.Type? Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L21) | Gets the required target type, or for a targetless action. | — | — | +| [`protected object? Inno.Editor.Interactions.EditorAction.activeTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L71) | Resolves the active target afresh for this callback; null means the identity is unavailable. | — | — | +| [`protected Inno.Editor.Core.EditorContext? Inno.Editor.Interactions.EditorAction.activeEditorContext { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L75) | Gets the host-owned passive context of the active operation, or null after completion. | — | — | +| [`protected void Inno.Editor.Interactions.EditorAction.Activate(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L86) | Activates this action for a target and cancels any operation it previously owned.
`context`: The contextual request that starts the operation. | — | `System.ArgumentNullException`: Thrown when is . | +| [`protected void Inno.Editor.Interactions.EditorAction.Complete()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L99) | Completes the current operation and returns the action to its idle state. | — | — | +| [`protected void Inno.Editor.Interactions.EditorAction.Cancel()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L113) | Cancels the current operation and returns the action to its idle state. | — | — | +| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L133) | Evaluates the action for the supplied context.
`context`: The editor, area, target, and optional argument for the query. | The current presentation and availability state. | — | +| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L140) | Executes the action for the supplied context.
`context`: The editor, area, target, and optional argument for the operation. | — | — | +| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L150) | Presents an active action at the current target location.
`context`: The current presentation area and target. | when the action replaced the target's normal content; otherwise, . | — | +| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnCompleted()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L154) | Runs after an active operation completes successfully. | — | — | +| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnCancelled()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L161) | Runs after an active operation is cancelled. | — | — | +| [`protected virtual void Inno.Editor.Interactions.EditorAction.OnPresentationLost()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L173) | Runs when this action's active target loses editor presentation focus. | — | — | +| [`public void Inno.Editor.Interactions.EditorAction.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L177) | Cancels active work and releases this action instance. | — | — | +| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorAction : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L196) | Defines a target action that requires one strongly typed command argument.
`TTarget`: The target type accepted by the action.
`TArgument`: The command argument type accepted by the action. | — | — | +| [`public override sealed System.Type Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L201) | Gets the concrete type handled by this extension implementation. | — | — | +| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L213) | Evaluates the operation's current availability and presentation state.
`context`: The context that supplies state and services for this operation. | The validated editor action state that represents the completed operation. | — | +| [`protected override sealed void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L223) | Applies the editor action to the supplied interaction context.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override sealed bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L242) | Presents this action through the current editor interaction surface.
`context`: The context that supplies state and services for this operation. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L252) | Evaluates the action for a typed target and argument.
`context`: The typed contextual request. | The current presentation and availability state. | — | +| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L259) | Executes the action for a typed target and argument.
`context`: The typed contextual request. | — | — | +| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L269) | Presents an active action for a typed target and argument.
`context`: The typed presentation request. | when content was presented. | — | +| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L196) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorArgumentAction : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L291) | Defines a targetless editor action that requires one strongly typed command argument.
`TArgument`: The command argument type accepted by the action. | — | — | +| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorArgumentAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L304) | Evaluates the operation's current availability and presentation state.
`context`: The context that supplies state and services for this operation. | The validated editor action state that represents the completed operation. | — | +| [`protected override sealed void Inno.Editor.Interactions.EditorArgumentAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L312) | Applies the editor action to the supplied interaction context.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorArgumentAction.Query(Inno.Editor.Interactions.EditorActionArgumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L331) | Evaluates the action for a typed argument.
`context`: The typed contextual request. | The current presentation and availability state. | — | +| [`protected abstract void Inno.Editor.Interactions.EditorArgumentAction.Execute(Inno.Editor.Interactions.EditorActionArgumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L338) | Executes the action for a typed argument.
`context`: The typed contextual request. | — | — | +| [`protected Inno.Editor.Interactions.EditorArgumentAction.EditorArgumentAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L291) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorPresentationAction : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L350) | Defines a target action with a typed presentation-only argument.
`TTarget`: The target type accepted by the action.
`TPresentation`: The presentation data type. | — | — | +| [`protected override sealed bool Inno.Editor.Interactions.EditorPresentationAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L361) | Presents this action through the current editor interaction surface.
`context`: The context that supplies state and services for this operation. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`protected abstract bool Inno.Editor.Interactions.EditorPresentationAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L372) | Presents active content using strongly typed presentation data.
`context`: The typed presentation request. | when content was presented. | — | +| [`protected Inno.Editor.Interactions.EditorPresentationAction.EditorPresentationAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L350) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorAction : Inno.Editor.Interactions.EditorAction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L381) | Defines a discoverable action whose target must be assignable to a specific reference type.
`TTarget`: The target type accepted by the action. | — | — | +| [`public override sealed System.Type Inno.Editor.Interactions.EditorAction.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L386) | Gets the concrete type handled by this extension implementation. | — | — | +| [`protected override sealed Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L397) | Evaluates the operation's current availability and presentation state.
`context`: The context that supplies state and services for this operation. | The validated editor action state that represents the completed operation. | — | +| [`protected override sealed void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L409) | Applies the editor action to the supplied interaction context.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override sealed bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L429) | Presents this action through the current editor interaction surface.
`context`: The context that supplies state and services for this operation. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`protected virtual Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorAction.Query(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L444) | Evaluates the action for a strongly typed target.
`context`: The contextual request containing the typed target. | The current presentation and availability state. | — | +| [`protected abstract void Inno.Editor.Interactions.EditorAction.Execute(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L451) | Executes the action for a strongly typed target.
`context`: The contextual request containing the typed target. | — | — | +| [`protected virtual bool Inno.Editor.Interactions.EditorAction.Present(Inno.Editor.Interactions.EditorActionContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L461) | Presents an active action for a strongly typed target.
`context`: The current presentation request containing the typed target. | when content was presented; otherwise, . | — | +| [`protected Inno.Editor.Interactions.EditorAction.EditorAction()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs#L381) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorActionAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L9) | Registers an editor action for automatic discovery and dispatch. | — | — | +| [`public Inno.Editor.Interactions.EditorActionAttribute.EditorActionAttribute(string action, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L23) | Creates an action registration available from every area.
`action`: The stable semantic action name.
`priority`: The tie-breaking priority used after target specificity. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Interactions.EditorActionAttribute.EditorActionAttribute(string action, string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L46) | Creates an action registration restricted to one exact area.
`action`: The stable semantic action name.
`area`: The exact area, or an empty string for every area.
`priority`: The tie-breaking priority. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Interactions.EditorActionAttribute.action { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L61) | Gets the stable action name. | — | — | +| [`public string Inno.Editor.Interactions.EditorActionAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L66) | Gets the optional exact interaction area. | — | — | +| [`public int Inno.Editor.Interactions.EditorActionAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionAttribute.cs#L71) | Gets the tie-breaking priority. | — | — | +| [`public class Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L8) | Provides contextual state to an editor action. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorActionContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L52) | Gets the shared passive editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorActionContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L56) | Gets the active interaction entry point. | — | — | +| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorActionContext.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L60) | Gets the transactional history used to record reversible mutations performed by this action. | — | — | +| [`public string Inno.Editor.Interactions.EditorActionContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L64) | Gets the stable interaction area. | — | — | +| [`public object? Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L68) | Gets the contextual action target. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorActionContext : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L81) | Provides a strongly typed target to an editor action implementation.
`TTarget`: The target type required by the action. | — | — | +| [`public TTarget Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L94) | Gets the strongly typed action target. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorActionContext : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L106) | Provides a strongly typed target and action argument to an editor action.
`TTarget`: The target type required by the action.
`TArgument`: The action argument type required by the action. | — | — | +| [`public TTarget Inno.Editor.Interactions.EditorActionContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L121) | Gets the strongly typed action target. | — | — | +| [`public TArgument Inno.Editor.Interactions.EditorActionContext.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L125) | Gets the strongly typed action argument. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorActionArgumentContext : Inno.Editor.Interactions.EditorActionContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L134) | Provides a strongly typed action argument to a targetless editor action.
`TArgument`: The action argument type required by the action. | — | — | +| [`public TArgument Inno.Editor.Interactions.EditorActionArgumentContext.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs#L147) | Gets the strongly typed action argument. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorActionState : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Describes the current presentation and availability of an editor action. | — | — | +| [`public Inno.Editor.Interactions.EditorActionState.EditorActionState(bool isVisible, bool isEnabled, bool isChecked = false, string? displayName = null, string? unavailableReason = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L25) | Creates the contextual presentation state of an editor action.
`isVisible`: Whether the action should be included in the current presentation.
`isEnabled`: Whether the action can execute in the current context.
`isChecked`: Whether the action is currently presented as checked.
`displayName`: An optional contextual label that replaces the static placement label.
`unavailableReason`: A concise explanation shown when an otherwise visible action cannot execute. | — | — | +| [`public bool Inno.Editor.Interactions.EditorActionState.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L42) | Gets whether the action should be displayed. | — | — | +| [`public bool Inno.Editor.Interactions.EditorActionState.isEnabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L46) | Gets whether the action can execute. | — | — | +| [`public bool Inno.Editor.Interactions.EditorActionState.isChecked { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L50) | Gets whether the action is currently checked. | — | — | +| [`public string? Inno.Editor.Interactions.EditorActionState.displayName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L54) | Gets an optional contextual display name. | — | — | +| [`public string? Inno.Editor.Interactions.EditorActionState.unavailableReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L58) | Gets the current execution barrier, or null when no specific explanation is available. | — | — | +| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.enabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L62) | Gets a visible and enabled action state. | — | — | +| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.disabled { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L66) | Gets a visible but disabled action state. | — | — | +| [`public static Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorActionState.hidden { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L70) | Gets a hidden action state. | — | — | +| [`public override string Inno.Editor.Interactions.EditorActionState.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorActionState.operator !=(Inno.Editor.Interactions.EditorActionState left, Inno.Editor.Interactions.EditorActionState right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorActionState.operator ==(Inno.Editor.Interactions.EditorActionState left, Inno.Editor.Interactions.EditorActionState right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorActionState.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorActionState.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorActionState.Equals(Inno.Editor.Interactions.EditorActionState other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs#L5) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Interactions.EditorShortcutAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L11) | Associates an editor action with a keyboard shortcut. | — | — | +| [`public Inno.Editor.Interactions.EditorShortcutAttribute.EditorShortcutAttribute(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool primary = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L25) | Creates a shortcut that can be dispatched from any focused interaction surface.
`key`: The non-modifier key that triggers the action.
`modifiers`: The exact additional modifier keys required by the gesture.
`primary`: Whether the platform primary modifier, Command on macOS or Control elsewhere, is required. | — | — | +| [`public Inno.Editor.Interactions.EditorShortcutAttribute.EditorShortcutAttribute(string area, Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool primary = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L49) | Creates a shortcut scoped to an exact interaction area.
`area`: The exact focused area that accepts the shortcut, or an empty string for any area.
`key`: The non-modifier key that triggers the action.
`modifiers`: The exact additional modifier keys required by the gesture.
`primary`: Whether the platform primary modifier, Command on macOS or Control elsewhere, is required. | — | — | +| [`public string Inno.Editor.Interactions.EditorShortcutAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L64) | Gets the optional exact interaction area. | — | — | +| [`public Inno.Core.Input.KeyCode Inno.Editor.Interactions.EditorShortcutAttribute.key { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L69) | Gets the shortcut key. | — | — | +| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorShortcutAttribute.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L74) | Gets additional shortcut modifiers. | — | — | +| [`public bool Inno.Editor.Interactions.EditorShortcutAttribute.primary { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorShortcutAttribute.cs#L79) | Gets whether the platform primary modifier is required. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorValidationResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Describes whether a requested editor operation is valid and carries a diagnostic when it is rejected.
`isValid`: Whether the requested operation may proceed.
`message`: The user-facing validation diagnostic, or an empty string for a valid result. | — | — | +| [`public Inno.Editor.Interactions.EditorValidationResult.EditorValidationResult(bool isValid, string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Describes whether a requested editor operation is valid and carries a diagnostic when it is rejected.
`isValid`: Whether the requested operation may proceed.
`message`: The user-facing validation diagnostic, or an empty string for a valid result. | — | — | +| [`public bool Inno.Editor.Interactions.EditorValidationResult.isValid { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L13) | Whether the requested operation may proceed. | — | — | +| [`public string Inno.Editor.Interactions.EditorValidationResult.message { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L14) | The user-facing validation diagnostic, or an empty string for a valid result. | — | — | +| [`public static Inno.Editor.Interactions.EditorValidationResult Inno.Editor.Interactions.EditorValidationResult.valid { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L19) | Gets a successful validation result. | — | — | +| [`public static Inno.Editor.Interactions.EditorValidationResult Inno.Editor.Interactions.EditorValidationResult.Invalid(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L30) | Creates a failed validation result with a user-facing diagnostic.
`message`: The diagnostic that explains why the operation cannot proceed. | An invalid result containing the supplied diagnostic. | — | +| [`public override string Inno.Editor.Interactions.EditorValidationResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorValidationResult.operator !=(Inno.Editor.Interactions.EditorValidationResult left, Inno.Editor.Interactions.EditorValidationResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorValidationResult.operator ==(Inno.Editor.Interactions.EditorValidationResult left, Inno.Editor.Interactions.EditorValidationResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorValidationResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorValidationResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorValidationResult.Equals(Inno.Editor.Interactions.EditorValidationResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Interactions.EditorValidationResult.Deconstruct(out bool isValid, out string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorValidationResult.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.HotKeyGesture : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Describes one keyboard gesture used to invoke an editor command. | — | — | +| [`public Inno.Editor.Interactions.HotKeyGesture.HotKeyGesture(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, string superModifierLabel = "Super")`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L31) | Creates a keyboard gesture with an exact set of modifier keys after symbolic-key normalization.
`key`: The non-modifier key in the gesture.
`modifiers`: The modifier keys required by the gesture.
`superModifierLabel`: The display name of the Super modifier; the neutral name is used for explicitly constructed gestures. | — | `System.ArgumentException`: The modifier label is empty. | | [`public Inno.Core.Input.KeyCode Inno.Editor.Interactions.HotKeyGesture.key { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L45) | Gets the main key. | — | — | | [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.HotKeyGesture.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L50) | Gets the required modifiers. | — | — | -| [`public readonly record struct Inno.Editor.Interactions.HotKeyGesture : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Describes one keyboard gesture used to invoke an editor command. | — | — | - -### `Inno.Editor.Interactions.IEditorDocumentService` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.Apply(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L122) | Applies staged changes through the current provider.
返回:true when changes are applied.
`documentId`:Stable tab identity. | — | — | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.Close(System.Guid documentId, Inno.Editor.Interactions.EditorDocumentCloseMode mode)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L147) | Closes one document using an explicit dirty-document decision.
返回:true when the document closed.
`documentId`:Stable tab identity.
`mode`:Explicit close behavior. | — | — | -| [`Inno.Editor.Interactions.EditorDocumentContext Inno.Editor.Interactions.IEditorDocumentService.Open(string assetPath, System.Guid assetId = default(System.Guid))`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L58) | Opens a document or returns its existing single instance.
返回:The stable document context.
`assetPath`:Project asset path.
`assetId`:Persistent asset identity when available. | — | — | -| [`System.IDisposable Inno.Editor.Interactions.IEditorDocumentService.RegisterProvider(Inno.Editor.Interactions.EditorDocumentProvider provider)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L44) | Registers one current-generation document provider.
返回:A lease that detaches only this provider instance.
`provider`:Provider instance to register. | — | — | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.Revert(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L133) | Reverts staged changes through the current provider.
返回:true when changes are reverted.
`documentId`:Stable tab identity. | — | — | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.Save(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L103) | Saves one open document through its current provider.
返回:true when the document is saved.
`documentId`:Stable tab identity. | — | — | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.SaveAll()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L111) | Saves every dirty open document.
返回:true when every dirty document is saved. | — | — | -| [`void Inno.Editor.Interactions.IEditorDocumentService.SetDirty(System.Guid documentId, bool isDirty = true)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L89) | Updates a document's unsaved state without saving, discarding or changing its History.
`documentId`:Stable tab identity.
`isDirty`:Whether the provider's current draft differs from its saved baseline. | — | — | -| [`bool Inno.Editor.Interactions.IEditorDocumentService.UpdateAssetPath(System.Guid documentId, string assetPath)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L75) | Updates an open document's source location after an identity-preserving asset move, without changing history or focus.
返回:Whether the document exists.
`documentId`:Existing document identity.
`assetPath`:Current authoritative source location for the same persistent asset. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.IEditorDocumentService.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L33) | Gets the currently owned source documents. | — | — | +| [`public static Inno.Editor.Interactions.HotKeyGesture Inno.Editor.Interactions.HotKeyGesture.Primary(Inno.Core.Input.KeyCode key, Inno.Editor.Core.EditorKeyboardPolicy keyboard, Inno.Core.Input.KeyModifier additionalModifiers = Inno.Core.Input.KeyModifier.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L67) | Creates a gesture that includes the product-selected primary modifier.
`key`: The non-modifier key in the gesture.
`keyboard`: The product-selected immutable keyboard policy.
`additionalModifiers`: Additional modifiers combined with the product-selected primary modifier. | A product-resolved keyboard gesture. | — | +| [`public override string Inno.Editor.Interactions.HotKeyGesture.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L82) | Formats the gesture as a human-readable editor menu shortcut label. | A product-resolved textual representation of the gesture. | — | +| [`public static bool Inno.Editor.Interactions.HotKeyGesture.operator !=(Inno.Editor.Interactions.HotKeyGesture left, Inno.Editor.Interactions.HotKeyGesture right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.HotKeyGesture.operator ==(Inno.Editor.Interactions.HotKeyGesture left, Inno.Editor.Interactions.HotKeyGesture right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.HotKeyGesture.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.HotKeyGesture.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.HotKeyGesture.Equals(Inno.Editor.Interactions.HotKeyGesture other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs#L12) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Interactions.EditorDocumentContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L10) | Stores only stable, reload-safe document identity and provider-owned draft parameters. | — | — | +| [`public System.Guid Inno.Editor.Interactions.EditorDocumentContext.documentId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L30) | Gets the stable identity of this open source document. | — | — | +| [`public System.Guid Inno.Editor.Interactions.EditorDocumentContext.assetId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L35) | Gets the persistent asset identity, or an empty value before the source has one. | — | — | +| [`public string Inno.Editor.Interactions.EditorDocumentContext.assetPath { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L40) | Gets the normalized project asset path. | — | — | +| [`public string Inno.Editor.Interactions.EditorDocumentContext.providerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L45) | Gets the stable provider identity used to recover across extension reload. | — | — | +| [`public string Inno.Editor.Interactions.EditorDocumentContext.title { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L50) | Gets or sets the author-facing source title used by diagnostics and dedicated editors. | — | — | +| [`public bool Inno.Editor.Interactions.EditorDocumentContext.isDirty { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L55) | Gets whether unsaved source or staged changes exist. | — | — | +| [`public bool Inno.Editor.Interactions.EditorDocumentContext.isProviderAvailable { get; internal set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L60) | Gets whether a current-generation provider is available. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Editor.Interactions.EditorDocumentContext.viewParameters { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L65) | Gets an immutable snapshot of stable view parameters. | — | — | +| [`public void Inno.Editor.Interactions.EditorDocumentContext.SetViewParameter(string key, string value)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L76) | Adds or replaces one stable scalar or JSON-formatted view parameter.
`key`: Stable provider-local parameter name.
`value`: Persistent scalar or JSON-formatted value. | — | — | +| [`public bool Inno.Editor.Interactions.EditorDocumentContext.TryGetViewParameter(string key, out string value)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentContext.cs#L97) | Tries to read one stable view parameter.
`key`: Stable provider-local parameter name.
`value`: Receives the stored value. | when the parameter exists. | — | +| [`public abstract class Inno.Editor.Interactions.EditorDocumentProvider`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L8) | Implements one reloadable asset-document kind without owning presentation or source identity. | — | — | +| [`public abstract string Inno.Editor.Interactions.EditorDocumentProvider.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L13) | Gets the globally stable provider identifier. | — | — | +| [`public abstract bool Inno.Editor.Interactions.EditorDocumentProvider.CanOpen(string assetPath)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L24) | Returns whether this provider can open the supplied project asset path.
`assetPath`: Normalized project asset path. | when the path is supported. | — | +| [`public virtual void Inno.Editor.Interactions.EditorDocumentProvider.Open(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L32) | Initializes transient provider state for an opened or restored document.
`context`: Stable document context owned by the host. | — | — | +| [`public abstract bool Inno.Editor.Interactions.EditorDocumentProvider.Save(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L43) | Saves all document changes to its asset source.
`context`: Stable document context owned by the host. | when the source is saved. | — | +| [`public virtual bool Inno.Editor.Interactions.EditorDocumentProvider.Apply(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L54) | Applies staged authoring changes and saves their asset source.
`context`: Stable document context owned by the host. | when staged changes are applied. | — | +| [`public virtual bool Inno.Editor.Interactions.EditorDocumentProvider.Revert(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L65) | Discards staged authoring changes and reloads the last saved source.
`context`: Stable document context owned by the host. | when the source is restored. | — | +| [`public virtual void Inno.Editor.Interactions.EditorDocumentProvider.Close(Inno.Editor.Interactions.EditorDocumentContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L77) | Releases transient provider state after a document closes.
`context`: Stable document context owned by the host. | — | — | +| [`protected Inno.Editor.Interactions.EditorDocumentProvider.EditorDocumentProvider()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentProvider.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public enum Inno.Editor.Interactions.EditorDocumentCloseMode : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L9) | Selects how a dirty document responds to a close request. | — | — | +| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Cancel = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L14) | Keeps the document open. | — | — | +| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Save = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L18) | Closes the document after saving. | — | — | +| [`Inno.Editor.Interactions.EditorDocumentCloseMode.Discard = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L22) | Closes the document and discards unsaved state. | — | — | | [`public interface Inno.Editor.Interactions.IEditorDocumentService`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L28) | Owns single-instance editor documents independently from reloadable providers and presentation. | — | — | - -### `Inno.Editor.Interactions.IEditorHistory` - - - - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Interactions.EditorHistoryTransaction Inno.Editor.Interactions.IEditorHistory.BeginTransaction(string name)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L67) | Begins an atomic group of neutral history operations.
返回:A transaction that must be committed or rolled back.
`name`:The user-facing grouped operation name. | — | — | -| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Execute(string name, Inno.Editor.Interactions.EditorHistoryChange change)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L81) | Applies and records a neutral change through its current-generation handler.
返回:The result of applying the change in the Redo direction.
`name`:The user-facing operation name.
`change`:The independently owned neutral change. | — | — | -| [`void Inno.Editor.Interactions.IEditorHistory.RecordApplied(string name, Inno.Editor.Interactions.EditorHistoryChange change)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L95) | Records a neutral change whose domain mutation is already applied.
`name`:The user-facing operation name.
`change`:The independently owned neutral change. | — | — | -| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Redo()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L114) | Attempts to reapply the newest reverted operation.
返回:The transition result. A state-preserving failure remains available for retry. | — | — | -| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Undo()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L106) | Attempts to restore the state preceding the newest committed operation.
返回:The transition result. A state-preserving failure remains available for retry. | — | — | -| [`bool Inno.Editor.Interactions.IEditorHistory.canRedo { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L16) | Gets whether a Redo transition is currently available. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.IEditorDocumentService.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L33) | Gets the currently owned source documents. | — | — | +| [`System.IDisposable Inno.Editor.Interactions.IEditorDocumentService.RegisterProvider(Inno.Editor.Interactions.EditorDocumentProvider provider)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L44) | Registers one current-generation document provider.
`provider`: Provider instance to register. | A lease that detaches only this provider instance. | — | +| [`Inno.Editor.Interactions.EditorDocumentContext Inno.Editor.Interactions.IEditorDocumentService.Open(string assetPath, System.Guid assetId = default(System.Guid))`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L58) | Opens a document or returns its existing single instance.
`assetPath`: Project asset path.
`assetId`: Persistent asset identity when available. | The stable document context. | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.UpdateAssetPath(System.Guid documentId, string assetPath)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L75) | Updates an open document's source location after an identity-preserving asset move, without changing history or focus.
`documentId`: Existing document identity.
`assetPath`: Current authoritative source location for the same persistent asset. | Whether the document exists. | — | +| [`void Inno.Editor.Interactions.IEditorDocumentService.SetDirty(System.Guid documentId, bool isDirty = true)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L89) | Updates a document's unsaved state without saving, discarding or changing its History.
`documentId`: Stable tab identity.
`isDirty`: Whether the provider's current draft differs from its saved baseline. | — | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.Save(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L103) | Saves one open document through its current provider.
`documentId`: Stable tab identity. | when the document is saved. | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.SaveAll()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L111) | Saves every dirty open document. | when every dirty document is saved. | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.Apply(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L122) | Applies staged changes through the current provider.
`documentId`: Stable tab identity. | when changes are applied. | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.Revert(System.Guid documentId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L133) | Reverts staged changes through the current provider.
`documentId`: Stable tab identity. | when changes are reverted. | — | +| [`bool Inno.Editor.Interactions.IEditorDocumentService.Close(System.Guid documentId, Inno.Editor.Interactions.EditorDocumentCloseMode mode)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Documents/IEditorDocumentService.cs#L147) | Closes one document using an explicit dirty-document decision.
`documentId`: Stable tab identity.
`mode`: Explicit close behavior. | when the document closed. | — | +| [`public sealed class Inno.Editor.Interactions.EditorDragContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L10) | Provides contextual state while beginning an editor drag. | — | — | +| [`public Inno.Editor.Interactions.EditorDragContext.EditorDragContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L33) | Creates a managed editor drag request.
`editor`: The shared passive editor context.
`interactions`: The active interaction entry point.
`area`: The interaction area that produced the source.
`data`: The transient source identity and preview label. | — | `System.ArgumentNullException`: Thrown when , , or is .
`System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDragContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L49) | Gets the shared passive editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDragContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L54) | Gets the active interaction entry point. | — | — | +| [`public string Inno.Editor.Interactions.EditorDragContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L59) | Gets the source interaction area. | — | — | +| [`public Inno.Editor.Interactions.EditorDragData Inno.Editor.Interactions.EditorDragContext.data { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragContext.cs#L64) | Gets the managed drag data. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorDragData`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L10) | Contains the transient identity and preview label for one editor drag operation. | — | — | +| [`public Inno.Editor.Interactions.EditorDragData.EditorDragData(Inno.Core.Identity.IdentityObject source, string label)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L27) | Creates drag data for a registered identity object.
`source`: The registered source object resolved again for each drop query and delivery.
`label`: The human-readable label used by drag previews. | — | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when is not registered in a live identity domain. | +| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorDragData.sourceIdentity { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L41) | Gets the domain-qualified transient identity written to the native drag protocol. | — | — | +| [`public string Inno.Editor.Interactions.EditorDragData.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDragData.cs#L46) | Gets the drag preview label. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorDrop`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L11) | Defines one discoverable typed editor drop operation. | — | — | +| [`public abstract System.Type Inno.Editor.Interactions.EditorDrop.sourceType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L16) | Gets the accepted drag source type. | — | — | +| [`public abstract System.Type Inno.Editor.Interactions.EditorDrop.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L21) | Gets the accepted drop target type. | — | — | +| [`public abstract Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L32) | Evaluates whether the current managed source may be dropped on the supplied target.
`context`: The untyped source, target, surface, and requested placement. | The compatibility state and standard target visual. | — | +| [`public abstract Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L43) | Executes a delivered drop after a successful compatibility query.
`context`: The untyped source, target, surface, and requested placement. | The observable selection and reveal result of the operation. | — | +| [`protected Inno.Editor.Interactions.EditorDrop.EditorDrop()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorDrop where TSource : Inno.Core.Identity.IdentityObject where TTarget : class : Inno.Editor.Interactions.EditorDrop`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L55) | Defines a typed drop operation for one managed source and target pair.
`TSource`: The managed drag-source type accepted by the operation.
`TTarget`: The managed drop-target type accepted by the operation. | — | — | +| [`public override sealed System.Type Inno.Editor.Interactions.EditorDrop.sourceType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L62) | Gets the concrete type handled by this extension implementation. | — | — | +| [`public override sealed System.Type Inno.Editor.Interactions.EditorDrop.targetType { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L67) | Gets the concrete type handled by this extension implementation. | — | — | +| [`public override sealed Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L78) | Evaluates the operation's current availability and presentation state.
`context`: The context that supplies state and services for this operation. | The validated editor drop status that represents the completed operation. | — | +| [`public override sealed Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L95) | Validates and applies the current editor drag-and-drop interaction atomically.
`context`: The context that supplies state and services for this operation. | The validated editor drop result that represents the completed operation. | — | +| [`protected abstract Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDrop.Query(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L112) | Evaluates whether the strongly typed source may be dropped on the strongly typed target.
`context`: The typed source, target, surface, and requested placement. | The compatibility state and standard target visual. | — | +| [`protected abstract Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDrop.Drop(Inno.Editor.Interactions.EditorDropContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L123) | Executes a delivered drop for the strongly typed source and target.
`context`: The typed source, target, surface, and requested placement. | The observable selection and reveal result of the operation. | — | +| [`protected Inno.Editor.Interactions.EditorDrop.EditorDrop()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L55) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorDropContext where TSource : class where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L135) | Provides strongly typed source and target values to a drop operation.
`TSource`: The managed drag-source type.
`TTarget`: The managed drop-target type. | — | — | +| [`public Inno.Editor.Interactions.EditorDropContext Inno.Editor.Interactions.EditorDropContext.untyped { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L152) | Gets the untyped drop context. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDropContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L157) | Gets the active editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDropContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L162) | Gets the active interaction entry point. | — | — | +| [`public string Inno.Editor.Interactions.EditorDropContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L167) | Gets the interaction area. | — | — | +| [`public TSource Inno.Editor.Interactions.EditorDropContext.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L172) | Gets the typed drag source. | — | — | +| [`public TTarget Inno.Editor.Interactions.EditorDropContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L177) | Gets the typed drop target. | — | — | +| [`public Inno.Editor.Interactions.EditorDropPlacement Inno.Editor.Interactions.EditorDropContext.placement { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDrop.cs#L182) | Gets the requested placement. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorDropAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L9) | Registers a typed editor drop handler for an optional exact area. | — | — | +| [`public Inno.Editor.Interactions.EditorDropAttribute.EditorDropAttribute(int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L17) | Creates a drop registration that can participate on any interaction surface.
`priority`: The tie-breaking priority used after source and target specificity. | — | — | +| [`public Inno.Editor.Interactions.EditorDropAttribute.EditorDropAttribute(string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L31) | Creates a drop registration scoped to an exact interaction area.
`area`: The exact target area handled by this registration, or an empty string for any area.
`priority`: The tie-breaking priority used after source and target specificity. | — | — | +| [`public string Inno.Editor.Interactions.EditorDropAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L42) | Gets the optional exact interaction area. | — | — | +| [`public int Inno.Editor.Interactions.EditorDropAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropAttribute.cs#L47) | Gets the tie-breaking priority. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorDropContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L11) | Provides contextual state to an editor drop handler. | — | — | +| [`public Inno.Editor.Interactions.EditorDropContext.EditorDropContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, Inno.Editor.Interactions.EditorDragData data, Inno.Core.Identity.IdentityObject source, object target, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L44) | Creates a managed drop request.
`editor`: The shared passive editor context.
`interactions`: The active interaction entry point.
`area`: The target interaction area.
`data`: The active identity-based drag data.
`source`: The live source resolved from for this operation only.
`target`: The managed target object exposed to typed handlers.
`placement`: The requested position relative to the target. | — | `System.ArgumentNullException`: Thrown when , , , , or is .
`System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorDropContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L66) | Gets the shared passive editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorDropContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L71) | Gets the active interaction entry point. | — | — | +| [`public string Inno.Editor.Interactions.EditorDropContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L76) | Gets the target interaction area. | — | — | +| [`public Inno.Editor.Interactions.EditorDragData Inno.Editor.Interactions.EditorDropContext.data { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L81) | Gets the active managed drag data. | — | — | +| [`public Inno.Core.Identity.IdentityObject Inno.Editor.Interactions.EditorDropContext.source { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L86) | Gets the live source resolved for this query or delivery operation. | — | — | +| [`public object Inno.Editor.Interactions.EditorDropContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L91) | Gets the managed drop target. | — | — | +| [`public Inno.Editor.Interactions.EditorDropPlacement Inno.Editor.Interactions.EditorDropContext.placement { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropContext.cs#L96) | Gets the requested placement relative to the target. | — | — | +| [`public enum Inno.Editor.Interactions.EditorDropPlacement : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L6) | Describes the requested placement relative to an editor drop target. | — | — | +| [`Inno.Editor.Interactions.EditorDropPlacement.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L11) | No positional placement is requested. | — | — | +| [`Inno.Editor.Interactions.EditorDropPlacement.Before = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L16) | Insert before the target. | — | — | +| [`Inno.Editor.Interactions.EditorDropPlacement.Into = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L21) | Drop into the target. | — | — | +| [`Inno.Editor.Interactions.EditorDropPlacement.After = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropPlacement.cs#L26) | Insert after the target. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorDropResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Describes the observable result of a completed editor drop. | — | — | +| [`public Inno.Editor.Interactions.EditorDropResult.EditorDropResult(bool accepted, object? selectionTarget = null, object? revealTarget = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L20) | Creates the observable result of a completed drop operation.
`accepted`: Whether the handler accepted and completed the drop.
`selectionTarget`: An optional object that the originating view should select.
`revealTarget`: An optional object that the originating view should reveal or expand. | — | — | +| [`public bool Inno.Editor.Interactions.EditorDropResult.accepted { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L33) | Gets whether the drop was accepted. | — | — | +| [`public object? Inno.Editor.Interactions.EditorDropResult.selectionTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L38) | Gets the optional target that should become selected. | — | — | +| [`public object? Inno.Editor.Interactions.EditorDropResult.revealTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L43) | Gets the optional hierarchy target that should be revealed. | — | — | +| [`public static Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDropResult.rejected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L48) | Gets a rejected drop result. | — | — | +| [`public static Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorDropResult.Accepted(object? selectionTarget = null, object? revealTarget = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L62) | Creates an accepted drop result with optional presentation requests.
`selectionTarget`: An optional object that the originating view should select.
`revealTarget`: An optional object that the originating view should reveal or expand. | An accepted drop result containing the supplied presentation requests. | — | +| [`public override string Inno.Editor.Interactions.EditorDropResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorDropResult.operator !=(Inno.Editor.Interactions.EditorDropResult left, Inno.Editor.Interactions.EditorDropResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorDropResult.operator ==(Inno.Editor.Interactions.EditorDropResult left, Inno.Editor.Interactions.EditorDropResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorDropResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorDropResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorDropResult.Equals(Inno.Editor.Interactions.EditorDropResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropResult.cs#L6) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorDropStatus : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Describes whether and how a target accepts the active drag. | — | — | +| [`public Inno.Editor.Interactions.EditorDropStatus.EditorDropStatus(bool canDrop, Inno.Editor.Interactions.EditorDropVisual visual)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L17) | Creates the compatibility and presentation state of a potential drop target.
`canDrop`: Whether the active source may be delivered to the target.
`visual`: The standard visual requested for the target. | — | — | +| [`public bool Inno.Editor.Interactions.EditorDropStatus.canDrop { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L28) | Gets whether the active source may be dropped. | — | — | +| [`public Inno.Editor.Interactions.EditorDropVisual Inno.Editor.Interactions.EditorDropStatus.visual { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L33) | Gets the standard target visual. | — | — | +| [`public static Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDropStatus.rejected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L38) | Gets an incompatible drop status. | — | — | +| [`public static Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorDropStatus.Accept(Inno.Editor.Interactions.EditorDropVisual visual = Inno.Editor.Interactions.EditorDropVisual.Highlight)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L49) | Creates an accepted drop status with the requested standard target visual.
`visual`: The standard visual drawn while the compatible source hovers the target. | An accepted drop status. | — | +| [`public override string Inno.Editor.Interactions.EditorDropStatus.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorDropStatus.operator !=(Inno.Editor.Interactions.EditorDropStatus left, Inno.Editor.Interactions.EditorDropStatus right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorDropStatus.operator ==(Inno.Editor.Interactions.EditorDropStatus left, Inno.Editor.Interactions.EditorDropStatus right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorDropStatus.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorDropStatus.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorDropStatus.Equals(Inno.Editor.Interactions.EditorDropStatus other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropStatus.cs#L6) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public enum Inno.Editor.Interactions.EditorDropVisual : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L6) | Defines the standard visual used for a compatible drop target. | — | — | +| [`Inno.Editor.Interactions.EditorDropVisual.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L11) | No visual is drawn. | — | — | +| [`Inno.Editor.Interactions.EditorDropVisual.Highlight = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L16) | Draw a content highlight. | — | — | +| [`Inno.Editor.Interactions.EditorDropVisual.InsertBefore = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L21) | Draw an insertion line before the target. | — | — | +| [`Inno.Editor.Interactions.EditorDropVisual.InsertAfter = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L26) | Draw an insertion line after the target. | — | — | +| [`Inno.Editor.Interactions.EditorDropVisual.Disabled = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/DragDrop/EditorDropVisual.cs#L31) | Draw a disabled target visual. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorEditGesture : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L7) | Owns one continuous edit transaction without retaining edited objects, setters or extension instances. | — | — | +| [`public Inno.Editor.Interactions.EditorEditGesture.EditorEditGesture(Inno.Editor.Interactions.IEditorHistory history, string name)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L25) | Begins an edit whose updates use the existing domain mutation and History protocols.
`history`: The authoritative History for the current editor session.
`name`: The operation name displayed by Undo and Redo after a successful commit. | — | `System.ArgumentNullException`: The History owner is null.
`System.InvalidOperationException`: History cannot begin a transaction in its current state. | +| [`public string Inno.Editor.Interactions.EditorEditGesture.mergeKey { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L37) | Gets the transient merge boundary shared only by updates within this gesture. | — | — | +| [`public bool Inno.Editor.Interactions.EditorEditGesture.isActive { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L41) | Gets whether this gesture still owns an active transaction, including a rejected completion. | — | — | +| [`public void Inno.Editor.Interactions.EditorEditGesture.Commit()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L49) | Commits the edit exactly once; rejected completion leaves the transaction available for retry. | — | `System.InvalidOperationException`: The gesture is finished or a nested transaction remains active. | +| [`public Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorEditGesture.Cancel()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L66) | Attempts to restore the state before the gesture without discarding a failed rollback. | The rollback outcome; a state-preserving failure keeps this gesture active for retry. | `System.InvalidOperationException`: The gesture is finished or a nested transaction remains active. | +| [`public void Inno.Editor.Interactions.EditorEditGesture.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs#L85) | Cancels an unfinished gesture and releases its ownership only after rollback succeeds. | — | `System.InvalidOperationException`: Rollback is blocked or a nested transaction remains active. | +| [`public sealed class Inno.Editor.Interactions.EditorOperationPreparingEvent : Inno.Core.Events.Event`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs#L10) | Synchronously closes transient edits before command execution or an extension-handler generation switch. | — | — | +| [`public Inno.Editor.Interactions.EditorOperationPreparingEvent.EditorOperationPreparingEvent()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorHistoryAvailability : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Describes whether a history change can currently move in one direction. | — | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryAvailability.isAvailable { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L21) | Gets whether the requested transition is currently available. | — | — | +| [`public string Inno.Editor.Interactions.EditorHistoryAvailability.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L26) | Gets the diagnostic explaining why the transition is unavailable, or an empty string when available. | — | — | +| [`public static Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryAvailability.Available()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L34) | Creates an available transition result. | An availability value that permits the transition. | — | +| [`public static Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryAvailability.Unavailable(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L48) | Creates an unavailable transition result.
`message`: The non-empty diagnostic explaining why the transition is unavailable. | An availability value that rejects the transition. | `System.ArgumentException`: Thrown when is empty. | +| [`public override string Inno.Editor.Interactions.EditorHistoryAvailability.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorHistoryAvailability.operator !=(Inno.Editor.Interactions.EditorHistoryAvailability left, Inno.Editor.Interactions.EditorHistoryAvailability right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorHistoryAvailability.operator ==(Inno.Editor.Interactions.EditorHistoryAvailability left, Inno.Editor.Interactions.EditorHistoryAvailability right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorHistoryAvailability.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorHistoryAvailability.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryAvailability.Equals(Inno.Editor.Interactions.EditorHistoryAvailability other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryAvailability.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryChange : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L8) | Describes one neutral reload-safe mutation interpreted by an attribute-discovered history handler. | — | — | +| [`public Inno.Editor.Interactions.EditorHistoryChange.EditorHistoryChange(string kind, Inno.Editor.Interactions.EditorHistoryPayload payload, string? mergeKey = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L30) | Creates a neutral history change.
`kind`: The stable globally unique handler protocol identifier.
`payload`: The immutable neutral payload interpreted by the handler.
`mergeKey`: An optional stable key used by the handler to merge adjacent changes. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public string Inno.Editor.Interactions.EditorHistoryChange.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L44) | Gets the stable globally unique handler protocol identifier. | — | — | +| [`public Inno.Editor.Interactions.EditorHistoryPayload Inno.Editor.Interactions.EditorHistoryChange.payload { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L49) | Gets the immutable neutral payload interpreted by the active handler generation. | — | — | +| [`public string? Inno.Editor.Interactions.EditorHistoryChange.mergeKey { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L54) | Gets the optional stable key used to merge adjacent changes to the same logical value. | — | — | +| [`public void Inno.Editor.Interactions.EditorHistoryChange.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryChange.cs#L73) | Releases the payload storage owned by this history change. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L10) | Provides current-generation editor services to a history change handler. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorHistoryContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L23) | Gets the passive editor context for the active project. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorHistoryContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryContext.cs#L28) | Gets the active interaction entry point used for selection and feature coordination. | — | — | +| [`public enum Inno.Editor.Interactions.EditorHistoryDirection : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L6) | Identifies the direction in which an editor history change is being applied. | — | — | +| [`Inno.Editor.Interactions.EditorHistoryDirection.Undo = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L11) | Restores the state that existed before the change was committed. | — | — | +| [`Inno.Editor.Interactions.EditorHistoryDirection.Redo = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryDirection.cs#L16) | Restores the state produced by the committed change. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorHistoryHandler`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L8) | Interprets neutral editor history payloads for one current-generation feature protocol. | — | — | +| [`protected abstract Inno.Editor.Interactions.EditorHistoryAvailability Inno.Editor.Interactions.EditorHistoryHandler.Query(Inno.Editor.Interactions.EditorHistoryContext context, Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L25) | Determines whether a neutral change can currently transition in the requested direction.
`context`: The current-generation editor services.
`change`: The neutral history change.
`direction`: The requested Undo or Redo direction. | The current transition availability. | — | +| [`protected abstract Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryHandler.Apply(Inno.Editor.Interactions.EditorHistoryContext context, Inno.Editor.Interactions.EditorHistoryChange change, Inno.Editor.Interactions.EditorHistoryDirection direction)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L46) | Atomically applies a neutral history change in the requested direction.
`context`: The current-generation editor services.
`change`: The neutral history change.
`direction`: The requested Undo or Redo direction. | The transition result. A failure must leave the feature in its original state. | — | +| [`protected virtual bool Inno.Editor.Interactions.EditorHistoryHandler.TryMerge(Inno.Editor.Interactions.EditorHistoryChange older, Inno.Editor.Interactions.EditorHistoryChange newer, out Inno.Editor.Interactions.EditorHistoryChange? merged)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L67) | Attempts to merge two adjacent changes to the same logical value.
`older`: The existing older history change.
`newer`: The newly recorded adjacent history change.
`merged`: The independently owned merged change when successful. | when replaces both input changes. | — | +| [`protected static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryHandler.StateIntegrityFailure(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L85) | Creates a failed result for a transition whose compensation also failed.
`message`: The diagnostic containing both the transition and compensation failures. | A failure that faults the owning history. | — | +| [`protected Inno.Editor.Interactions.EditorHistoryHandler.EditorHistoryHandler()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandler.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryHandlerAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L9) | Registers a stateless editor history handler for one stable change protocol. | — | — | +| [`public Inno.Editor.Interactions.EditorHistoryHandlerAttribute.EditorHistoryHandlerAttribute(string kind)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L20) | Creates a history handler registration.
`kind`: The stable globally unique change protocol identifier. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Interactions.EditorHistoryHandlerAttribute.kind { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryHandlerAttribute.cs#L29) | Gets the stable globally unique change protocol identifier. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryOptions`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L8) | Configures retention and payload storage for one editor history. | — | — | +| [`public int Inno.Editor.Interactions.EditorHistoryOptions.maxEntries { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L13) | Gets or initializes the maximum number of committed top-level entries retained in memory. | — | — | +| [`public long Inno.Editor.Interactions.EditorHistoryOptions.maxResidentBytes { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L18) | Gets or initializes the maximum estimated resident payload bytes retained by the history. | — | — | +| [`public long Inno.Editor.Interactions.EditorHistoryOptions.maxDiskBytes { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L23) | Gets or initializes the maximum payload bytes retained in the temporary disk store. | — | — | +| [`public int Inno.Editor.Interactions.EditorHistoryOptions.inlinePayloadThreshold { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L28) | Gets or initializes the payload size at which immutable bytes are moved to the temporary disk store. | — | — | +| [`public string? Inno.Editor.Interactions.EditorHistoryOptions.cacheDirectory { get; init; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L33) | Gets or initializes the optional directory that owns temporary history payloads for this editor session. | — | — | +| [`public Inno.Editor.Interactions.EditorHistoryOptions.EditorHistoryOptions()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryOptions.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryPayload : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L9) | Owns immutable neutral bytes used by a reload-safe editor history change. | — | — | +| [`public long Inno.Editor.Interactions.EditorHistoryPayload.length { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L35) | Gets the number of immutable bytes represented by this payload. | — | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryPayload.isStoredOnDisk { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L40) | Gets whether the payload is retained in the temporary disk store instead of resident memory. | — | — | +| [`public static Inno.Editor.Interactions.EditorHistoryPayload Inno.Editor.Interactions.EditorHistoryPayload.FromBytes(System.ReadOnlySpan bytes)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L55) | Creates an immutable history payload by copying the supplied bytes.
`bytes`: The neutral bytes to retain for future Undo and Redo transitions. | A new independently owned payload. | — | +| [`public byte[] Inno.Editor.Interactions.EditorHistoryPayload.ReadBytes()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L66) | Reads the complete immutable payload into a newly allocated byte array. | A copy of the payload bytes. | `System.ObjectDisposedException`: Thrown after the payload has been disposed. | +| [`public void Inno.Editor.Interactions.EditorHistoryPayload.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryPayload.cs#L90) | Releases the resident or temporary disk storage owned by this payload. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorHistoryResult : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Describes the outcome of applying, undoing, or redoing an editor history operation. | — | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryResult.succeeded { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L23) | Gets whether the requested history transition completed successfully. | — | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryResult.statePreserved { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L32) | Gets whether a failed transition restored the domain state that existed before the attempt. | — | — | +| [`public string Inno.Editor.Interactions.EditorHistoryResult.message { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L37) | Gets the diagnostic message associated with a failed transition, or an empty string after success. | — | — | +| [`public static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryResult.Success()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L45) | Creates a successful history result. | A result representing a completed transition. | — | +| [`public static Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryResult.Failure(string message)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L59) | Creates a failed history result without changing the owning history stack.
`message`: The non-empty diagnostic explaining why the transition could not complete. | A result representing a rejected or failed transition. | `System.ArgumentException`: Thrown when is empty. | +| [`public override string Inno.Editor.Interactions.EditorHistoryResult.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorHistoryResult.operator !=(Inno.Editor.Interactions.EditorHistoryResult left, Inno.Editor.Interactions.EditorHistoryResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorHistoryResult.operator ==(Inno.Editor.Interactions.EditorHistoryResult left, Inno.Editor.Interactions.EditorHistoryResult right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorHistoryResult.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorHistoryResult.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorHistoryResult.Equals(Inno.Editor.Interactions.EditorHistoryResult other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryResult.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Interactions.EditorHistoryTransaction : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L8) | Collects several history operations into one atomic undo and redo entry. | — | — | +| [`public string Inno.Editor.Interactions.EditorHistoryTransaction.name { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L26) | Gets the user-facing name assigned to the transaction. | — | — | +| [`public void Inno.Editor.Interactions.EditorHistoryTransaction.Commit()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L36) | Commits all recorded child operations as one atomic history entry. | — | `System.InvalidOperationException`: Thrown when the transaction is no longer active. | +| [`public Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.EditorHistoryTransaction.Rollback()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L52) | Reverts every operation recorded by the transaction and does not add a history entry. | The result of the atomic rollback attempt. | `System.InvalidOperationException`: Thrown when the transaction is no longer active. | +| [`public void Inno.Editor.Interactions.EditorHistoryTransaction.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistoryTransaction.cs#L66) | Rolls back an uncommitted transaction before releasing it. | — | — | +| [`public interface Inno.Editor.Interactions.IEditorHistory`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L6) | Exposes reload-safe Undo and Redo operations based exclusively on neutral history changes. | — | — | | [`bool Inno.Editor.Interactions.IEditorHistory.canUndo { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L11) | Gets whether an Undo transition is currently available. | — | — | -| [`long Inno.Editor.Interactions.IEditorHistory.diskBytes { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L56) | Gets the temporary disk payload bytes retained by committed entries. | — | — | -| [`string? Inno.Editor.Interactions.IEditorHistory.faultReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L46) | Gets the diagnostic that faulted this history, or null. | — | — | +| [`bool Inno.Editor.Interactions.IEditorHistory.canRedo { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L16) | Gets whether a Redo transition is currently available. | — | — | | [`bool Inno.Editor.Interactions.IEditorHistory.isFaulted { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L21) | Gets whether a failed compensation left the domain state indeterminate. | — | — | -| [`string? Inno.Editor.Interactions.IEditorHistory.redoName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L31) | Gets the next Redo operation name, or null. | — | — | -| [`string? Inno.Editor.Interactions.IEditorHistory.redoUnavailableReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L41) | Gets why the next Redo entry is unavailable, or null. | — | — | +| [`string? Inno.Editor.Interactions.IEditorHistory.undoName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L26) | Gets the next Undo operation name, or . | — | — | +| [`string? Inno.Editor.Interactions.IEditorHistory.redoName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L31) | Gets the next Redo operation name, or . | — | — | +| [`string? Inno.Editor.Interactions.IEditorHistory.undoUnavailableReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L36) | Gets why the next Undo entry is unavailable, or . | — | — | +| [`string? Inno.Editor.Interactions.IEditorHistory.redoUnavailableReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L41) | Gets why the next Redo entry is unavailable, or . | — | — | +| [`string? Inno.Editor.Interactions.IEditorHistory.faultReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L46) | Gets the diagnostic that faulted this history, or . | — | — | | [`long Inno.Editor.Interactions.IEditorHistory.residentBytes { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L51) | Gets the resident payload bytes retained by committed entries. | — | — | -| [`string? Inno.Editor.Interactions.IEditorHistory.undoName { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L26) | Gets the next Undo operation name, or null. | — | — | -| [`string? Inno.Editor.Interactions.IEditorHistory.undoUnavailableReason { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L36) | Gets why the next Undo entry is unavailable, or null. | — | — | -| [`public interface Inno.Editor.Interactions.IEditorHistory`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L6) | Exposes reload-safe Undo and Redo operations based exclusively on neutral history changes. | — | — | - -### `Inno.Editor.Interactions.IEditorHistoryIsolation` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`System.IDisposable Inno.Editor.Interactions.IEditorHistoryIsolation.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistoryIsolation.cs#L19) | Starts an independently disposable history branch while retaining the current editing branch.
返回:A lease that releases the temporary branch and restores the retained branch when disposed. | — | `System.InvalidOperationException`:Thrown while another history transition, transaction, or isolated branch is active. | +| [`long Inno.Editor.Interactions.IEditorHistory.diskBytes { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L56) | Gets the temporary disk payload bytes retained by committed entries. | — | — | +| [`Inno.Editor.Interactions.EditorHistoryTransaction Inno.Editor.Interactions.IEditorHistory.BeginTransaction(string name)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L67) | Begins an atomic group of neutral history operations.
`name`: The user-facing grouped operation name. | A transaction that must be committed or rolled back. | — | +| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Execute(string name, Inno.Editor.Interactions.EditorHistoryChange change)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L81) | Applies and records a neutral change through its current-generation handler.
`name`: The user-facing operation name.
`change`: The independently owned neutral change. | The result of applying the change in the Redo direction. | — | +| [`void Inno.Editor.Interactions.IEditorHistory.RecordApplied(string name, Inno.Editor.Interactions.EditorHistoryChange change)`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L95) | Records a neutral change whose domain mutation is already applied.
`name`: The user-facing operation name.
`change`: The independently owned neutral change. | — | — | +| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Undo()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L106) | Attempts to restore the state preceding the newest committed operation. | The transition result. A state-preserving failure remains available for retry. | — | +| [`Inno.Editor.Interactions.EditorHistoryResult Inno.Editor.Interactions.IEditorHistory.Redo()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistory.cs#L114) | Attempts to reapply the newest reverted operation. | The transition result. A state-preserving failure remains available for retry. | — | | [`public interface Inno.Editor.Interactions.IEditorHistoryIsolation`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistoryIsolation.cs#L8) | Creates temporary history branches for transient editor workflows without exposing history internals. | — | — | - -### `Inno.Editor.Interactions.IEditorSelectionCoordinator` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Interactions.IEditorSelectionCoordinator.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L19) | Replaces the current target after closing presentations owned by another target.
`target`:The new target, or null to clear selection. | — | — | -| [`object? Inno.Editor.Interactions.IEditorSelectionCoordinator.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L11) | Gets the currently selected target, or null when selection is empty. | — | — | -| [`public interface Inno.Editor.Interactions.IEditorSelectionCoordinator`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L6) | Provides the narrow selection boundary used by editor features that replace object instances. | — | — | - -### `Inno.Editor.Interactions.IEditorViewportCoordinateConverter` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.IEditorViewportCoordinateConverter.ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L195) | Converts viewport-local pixel coordinates to world coordinates.
返回:World coordinates.
`screenPosition`:Viewport-local pixel coordinates. | — | — | -| [`Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.IEditorViewportCoordinateConverter.WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L206) | Converts world coordinates to viewport-local pixel coordinates.
返回:Viewport-local pixel coordinates.
`worldPosition`:World coordinates. | — | — | +| [`System.IDisposable Inno.Editor.Interactions.IEditorHistoryIsolation.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/History/IEditorHistoryIsolation.cs#L19) | Starts an independently disposable history branch while retaining the current editing branch. | A lease that releases the temporary branch and restores the retained branch when disposed. | `System.InvalidOperationException`: Thrown while another history transition, transaction, or isolated branch is active. | +| [`public sealed class Inno.Editor.Interactions.EditorMenuAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L9) | Places an editor action at an arbitrary path on a menu surface. | — | — | +| [`public Inno.Editor.Interactions.EditorMenuAttribute.EditorMenuAttribute(string area, string path, int order = 0, bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L29) | Creates a static placement for the annotated action on an exact menu surface.
`area`: The exact interaction area whose menu receives the action.
`path`: The slash-delimited path used to create parent menus and the leaf label.
`order`: The stable ordering value among sibling placements.
`separatorBefore`: Whether a visual separator should precede the leaf placement. | — | `System.ArgumentException`: Thrown when or is empty. | +| [`public string Inno.Editor.Interactions.EditorMenuAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L48) | Gets the menu area. | — | — | +| [`public string Inno.Editor.Interactions.EditorMenuAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L53) | Gets the slash-delimited menu path. | — | — | +| [`public int Inno.Editor.Interactions.EditorMenuAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L58) | Gets the stable menu ordering value. | — | — | +| [`public bool Inno.Editor.Interactions.EditorMenuAttribute.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuAttribute.cs#L63) | Gets whether a separator is rendered before the item. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorMenuBuilder`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L9) | Collects dynamic menu item placements. | — | — | +| [`public void Inno.Editor.Interactions.EditorMenuBuilder.AddGroup(string path, int order = 0, bool separatorBefore = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L26) | Declares presentation for an intermediate menu group without inventing a no-op action.
`path`: The slash-delimited path of the group.
`order`: The stable ordering value among sibling groups and items.
`separatorBefore`: Whether a separator precedes this group at its own level. | — | — | +| [`public void Inno.Editor.Interactions.EditorMenuBuilder.Add(string path, string actionId, int order = 0, bool separatorBefore = false, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L57) | Adds a dynamic action placement to the menu currently being constructed.
`path`: The slash-delimited path used to create parent menus and the leaf label.
`actionId`: The stable action identifier invoked by the leaf.
`order`: The stable ordering value among sibling placements.
`separatorBefore`: Whether a visual separator should precede the leaf placement.
`argument`: An optional argument captured for this menu generation. | — | `System.ArgumentException`: Thrown when or is empty. | +| [`public Inno.Editor.Interactions.EditorMenuBuilder.EditorMenuBuilder()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuBuilder.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorMenuContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L10) | Provides contextual state while constructing an editor menu. | — | — | +| [`public Inno.Editor.Interactions.EditorMenuContext.EditorMenuContext(Inno.Editor.Core.EditorContext editor, Inno.Editor.Interactions.EditorInteractions interactions, string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L33) | Creates a contextual menu request.
`editor`: The shared passive editor context.
`interactions`: The active interaction entry point.
`area`: The exact menu area being requested.
`target`: The optional object the menu operates on. | — | `System.ArgumentNullException`: Thrown when or is .
`System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Interactions.EditorMenuContext.editor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L49) | Gets the shared passive editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorMenuContext.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L54) | Gets the active interaction entry point. | — | — | +| [`public string Inno.Editor.Interactions.EditorMenuContext.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L59) | Gets the requested menu area. | — | — | +| [`public object? Inno.Editor.Interactions.EditorMenuContext.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuContext.cs#L64) | Gets the contextual menu target. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorMenuItem`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L11) | Represents one immutable contextual menu node. | — | — | +| [`public string Inno.Editor.Interactions.EditorMenuItem.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L58) | Gets the visible node label. | — | — | +| [`public string Inno.Editor.Interactions.EditorMenuItem.actionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L64) | Gets the stable action identifier consumed by a presentation backend. | — | — | +| [`public int Inno.Editor.Interactions.EditorMenuItem.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L69) | Gets the stable ordering value. | — | — | +| [`public bool Inno.Editor.Interactions.EditorMenuItem.separatorBefore { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L74) | Gets whether a separator precedes this node. | — | — | +| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorMenuItem.status { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L79) | Gets the current command presentation state. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorMenuItem.children { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L84) | Gets child menu nodes. | — | — | +| [`public object? Inno.Editor.Interactions.EditorMenuItem.argument { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuItem.cs#L90) | Gets the generation-local action argument consumed by a presentation backend. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorMenuModel`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L9) | Contains a complete immutable menu tree. | — | — | +| [`public Inno.Editor.Interactions.EditorMenuModel.EditorMenuModel(System.Collections.Generic.IReadOnlyList? items)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L17) | Creates a complete immutable menu tree from resolved root nodes.
`items`: The root nodes in stable display order, or for an empty menu. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorMenuModel.items { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuModel.cs#L25) | Gets root menu nodes in display order. | — | — | +| [`public abstract class Inno.Editor.Interactions.EditorMenuSource`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L8) | Contributes dynamic entries to one or more editor menu surfaces. | — | — | +| [`public abstract void Inno.Editor.Interactions.EditorMenuSource.Build(Inno.Editor.Interactions.EditorMenuContext context, Inno.Editor.Interactions.EditorMenuBuilder builder)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L19) | Adds context-dependent placements for the supplied menu request.
`context`: The requested menu surface, target, and shared editor context.
`builder`: The collector that receives dynamic action placements. | — | — | +| [`protected Inno.Editor.Interactions.EditorMenuSource.EditorMenuSource()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSource.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorMenuSourceAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L9) | Registers a dynamic editor menu source. | — | — | +| [`public Inno.Editor.Interactions.EditorMenuSourceAttribute.EditorMenuSourceAttribute(string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L23) | Creates a dynamic menu-source registration for an exact interaction surface.
`area`: The exact menu area that receives placements from the source.
`priority`: The ordering priority used when multiple dynamic sources contribute to the surface. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public string Inno.Editor.Interactions.EditorMenuSourceAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L36) | Gets the contributed menu area. | — | — | +| [`public int Inno.Editor.Interactions.EditorMenuSourceAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L41) | Gets the provider ordering priority. | — | — | +| [`public readonly struct Inno.Editor.Interactions.EditorInteraction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L8) | Provides a lightweight fluent handle for one interaction area and optional target. | — | — | +| [`public string Inno.Editor.Interactions.EditorInteraction.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L25) | Gets the stable interaction area. | — | — | +| [`public object? Inno.Editor.Interactions.EditorInteraction.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L29) | Gets the optional target represented by this handle. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.isSelected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L33) | Gets whether this handle's target is the current editor selection. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteraction.Focus()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L38) | Marks this area and target as the active keyboard context. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Select()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L45) | Selects this target, or clears selection when the target is . | when the built-in selection action executed; otherwise, . | — | +| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorInteraction.Query(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L61) | Queries an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | The current action state. | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Execute(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L80) | Executes an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | when a visible and enabled action executed; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public void Inno.Editor.Interactions.EditorInteraction.Enqueue(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L96) | Queues an action until the current UI traversal completes.
`action`: The stable action name.
`argument`: An optional action argument. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Present(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L115) | Presents an active action in place of this target's normal content.
`action`: The stable action name.
`argument`: Optional presentation data supplied by the current view. | when the action presented content; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.IsActive(string action)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L131) | Gets whether an action owns an active multi-frame operation for this target.
`action`: The stable action name. | when the action is active for this exact target; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildMenu()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L138) | Builds the complete contextual menu for this area and target. | The immutable resolved menu model. | — | +| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildCommands()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L145) | Builds searchable commands from this area's menus and applicable unplaced Actions. | Current command states and neutral placement arguments; execution revalidates their targets. | — | +| [`public Inno.Editor.Interactions.EditorToolbarModel Inno.Editor.Interactions.EditorInteraction.BuildToolbar()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L152) | Builds the complete compact toolbar for this area and target. | The immutable resolved toolbar model. | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetShortcut(string action, out Inno.Editor.Interactions.HotKeyGesture gesture)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L168) | Resolves the shortcut displayed for an action in this area.
`action`: The stable action name.
`gesture`: The resolved gesture when successful. | when a compatible shortcut exists; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L181) | Gets the currently active drag identity, if its source remains registered.
`identity`: The active domain-qualified runtime identity when available. | when a live drag source exists; otherwise, . | — | +| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorInteraction.BeginDrag(Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L191) | Begins a managed drag originating from this area.
`data`: The managed source data. | The source object's domain-qualified transient identity. | — | +| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorInteraction.QueryDrop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L204) | Queries this handle as a drop target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The drop compatibility and visual state. | — | +| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorInteraction.Drop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L220) | Delivers a managed drop to this target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The completed drop result. | — | +| [`public sealed class Inno.Editor.Interactions.EditorInteractionRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L17) | Hosts attribute-discovered editor extensions without depending on a presentation backend. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(string projectDirectory, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, Inno.Editor.Core.EditorKeyboardPolicy keyboard)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L46) | Creates an interaction runtime for one project.
`projectDirectory`: The project root containing Assets and Library.
`types`: The type catalog that owns editor extension generations.
`logs`: The host-owned logging router used by editor infrastructure.
`keyboard`: The explicit immutable product keyboard conventions. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L71) | Creates an interaction runtime for an existing passive editor context.
`context`: The passive editor context shared with the presentation backend.
`types`: The type catalog that owns editor extension generations.
`logs`: The host-owned logging router used by editor infrastructure. | — | `System.ArgumentNullException`: Thrown when or is . | +| [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IEnumerable hostServices)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L98) | Creates an interaction runtime with stable host-owned extension services.
`context`: The passive editor context shared with the presentation backend.
`hostServices`: Stable host-owned services that editor extension constructors may request by assignable contract.
`logs`: The host-owned logging router used by editor infrastructure.
`types`: The type catalog that owns editor extension generations. | — | `System.ArgumentNullException`: Thrown when or is . | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Interactions.EditorInteractionRuntime.interactions { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L128) | Gets the active presentation-independent interaction entry point. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractionRuntime.panels { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L133) | Gets active dockable panel extensions in deterministic order. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractionRuntime.modals { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L145) | Gets active modal extensions in deterministic order. | — | — | +| [`public int Inno.Editor.Interactions.EditorInteractionRuntime.panelCount { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L157) | Gets the number of active dockable panels. | — | — | +| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Start()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L162) | Starts value processing after validating the current state. | — | — | +| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L179) | Recomputes owned state from the current validated inputs.
`frame`: The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.Flush()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L194) | Flushes actions queued during the current presentation traversal. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.SaveState()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L203) | Captures every stateful active module and panel and atomically flushes changed project state to disk. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | +| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.PrepareShutdown()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L220) | Freezes automatic extension-state persistence and writes the final state before modules begin shutting down. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | +| [`public void Inno.Editor.Interactions.EditorInteractionRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L232) | Dispatches an unhandled keyboard event through contextual shortcuts.
`keyEvent`: The keyboard event received from the application event stream. | — | — | +| [`public override void Inno.Editor.Interactions.EditorInteractionRuntime.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L248) | Releases the resources owned by this implementation. | — | `System.AggregateException`: Thrown after all shutdown stages have been attempted when one or more stages failed.
`Inno.Core.Execution.RetirementPendingException`: A stage still owns live work. This runtime retains all remaining stages and rejects new updates. | +| [`public sealed class Inno.Editor.Interactions.EditorInteractions : Inno.Editor.Interactions.IEditorSelectionCoordinator, Inno.Editor.Interactions.IEditorHistoryIsolation`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L15) | Provides the single presentation-independent entry point for editor actions, menus, selection, focus, and drag-and-drop. | — | — | +| [`public Inno.Core.Events.EventDispatcher Inno.Editor.Interactions.EditorInteractions.events { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L57) | Gets synchronous Editor lifecycle notifications implemented by the Core event dispatcher. | — | — | +| [`public Inno.Editor.Interactions.EditorSelectionState Inno.Editor.Interactions.EditorInteractions.selection { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L63) | Gets the shared read-only editor selection state. | — | — | +| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorInteractions.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L69) | Gets the transactional Undo and Redo history owned by this editor runtime. | — | — | +| [`public Inno.Editor.Interactions.IEditorDocumentService Inno.Editor.Interactions.EditorInteractions.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L73) | Gets the headless reload-safe document lifetime used by dedicated asset editors and Inspectors. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetModule(out TModule? module) where TModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L90) | Resolves an active feature module for immediate use in the current Editor callback.
`module`: The started, non-quarantined module, or null when unavailable.
`TModule`: The feature module contract to resolve. | True when the active generation supplies the requested module. | — | +| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L113) | Starts an isolated temporary Undo and Redo branch while retaining the current editing branch. | A scope that restores the retained history branch when disposed. | `System.InvalidOperationException`: Thrown during an Undo, Redo, transaction, or another isolated branch. | +| [`public string Inno.Editor.Interactions.EditorInteractions.focusedArea { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L118) | Gets the area that most recently received keyboard focus. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractions.GetPanelDeclarations()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L126) | Gets immutable discovery metadata for the active, available panels. | Current panel declarations; the snapshot contains no panel or extension instances. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetPanelState(string panelId, out bool isOpen)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L139) | Reads whether one available panel is currently open.
`panelId`: The stable panel identity.
`isOpen`: Receives the open state, or false when the panel is unavailable. | True when the current generation contains the requested available panel. | — | +| [`public object? Inno.Editor.Interactions.EditorInteractions.focusedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L151) | Gets the target associated with the focused area. | — | — | +| [`public Inno.Editor.Interactions.EditorInteraction Inno.Editor.Interactions.EditorInteractions.For(string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L168) | Creates a lightweight interaction handle for one area and optional target.
`area`: The stable interaction area.
`target`: The optional object represented by the area. | A lightweight interaction handle. | `System.ArgumentException`: Thrown when is empty. | +| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L178) | Replaces the editor selection after closing presentations owned by other targets.
`target`: The target to select, or to clear the selection. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(System.Collections.Generic.IEnumerable targets, Inno.Core.Identity.IdentityObject? anchor)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L203) | Replaces the ordered selection; duplicates are removed and the final target becomes primary.
`targets`: Registered objects resolved for the current callback.
`anchor`: The range anchor, or null to use the first selected target. | — | `System.ArgumentNullException`: The target collection or one member is null.
`System.InvalidOperationException`: A target has no live identity registration. | +| [`public void Inno.Editor.Interactions.EditorInteractions.ToggleSelection(Inno.Core.Identity.IdentityObject target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L221) | Adds or removes a member while preserving the existing range anchor.
`target`: A currently registered selection target. | — | `System.ArgumentNullException`: The target is null. | +| [`public void Inno.Editor.Interactions.EditorInteractions.SelectRange(System.Collections.Generic.IReadOnlyList orderedTargets, Inno.Core.Identity.IdentityObject target, bool additive = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L250) | Selects the inclusive visible range between the current anchor and the clicked target.
`orderedTargets`: Targets in the current visible sort and filter order.
`target`: The clicked range endpoint, which becomes primary.
`additive`: Whether to preserve existing members outside the selected range. | — | `System.ArgumentNullException`: The visible targets or endpoint is null.
`System.ArgumentException`: The endpoint is not in the visible target order. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetDragSource(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? source)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L303) | Resolves the live source for an active runtime identity.
`identity`: The domain-qualified runtime identity carried by the drag protocol.
`source`: The currently registered drag source when resolution succeeds. | when the identity is active and resolves to a live source; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L316) | Gets the active drag identity used to select the matching native payload domain.
`identity`: The active runtime identity when one remains valid. | when a live drag source exists; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TogglePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L329) | Toggles one panel in the currently active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found and toggled. | `System.ArgumentException`: Thrown when is empty. | +| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.RegisterIdentityDomain(Inno.Core.Identity.IdentityAllocator allocator)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L354) | Makes a temporary Core identity domain available to selection, focus and drag resolution.
`allocator`: The borrowed allocator owned by the temporary runtime session. | A routing lease; retire it after restoring Edit selection and before releasing the session. | `System.ArgumentNullException`: The allocator is null.
`System.InvalidOperationException`: The domain is already registered or the caller is not the owner thread.
`System.ObjectDisposedException`: The interaction runtime has retired its identity routing. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L377) | Resolves a domain-qualified runtime identity through the Editor's complete identity-domain set.
`identity`: Runtime identity captured from a live Editor target.
`target`: Receives the current live object, or when its domain or generation is unavailable. | when the exact identity resolves in its owning domain. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.IdentityDomainId domainId, System.Guid persistentId, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L401) | Resolves a persistent identity through one explicitly selected Editor identity domain.
`domainId`: Identity domain that owns the object and any replacement generation.
`persistentId`: Stable object identity to resolve inside that domain.
`target`: Receives the current live object, or when the domain or object is unavailable. | when the stable identity resolves in the requested domain. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.OpenPanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L422) | Opens and requests presentation focus for one panel in the active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.ClosePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L440) | Closes one panel in the active extension generation without toggling its current state.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | +| [`public sealed class Inno.Editor.Interactions.EditorModalExtension`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L11) | Describes one active modal extension. | — | — | +| [`public string Inno.Editor.Interactions.EditorModalExtension.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L34) | Gets the stable modal identifier. | — | — | +| [`public string Inno.Editor.Interactions.EditorModalExtension.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L39) | Gets the visible modal title. | — | — | +| [`public int Inno.Editor.Interactions.EditorModalExtension.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L44) | Gets the stable modal ordering value. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.TryGetPresentation(out Inno.Editor.Interactions.EditorModalExtension.Presentation presentation)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L55) | Safely captures generation-local modal presentation values.
`presentation`: The captured immutable presentation, or its default value when the modal is quarantined. | when every presentation value was read successfully. | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L95) | Safely draws the modal body and quarantines a failing extension instance.
`context`: The active editor context supplied by the presentation backend. | when the modal completed drawing without being quarantined. | `System.ArgumentNullException`: Thrown when is . | +| [`public readonly record struct Inno.Editor.Interactions.EditorModalExtension.Presentation : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Stores immutable modal window policy values read from one extension generation. | — | — | +| [`public Inno.Editor.Interactions.EditorModalExtension.Presentation.Presentation(bool isVisible, bool blocksInteraction, bool canMove, bool canResize, bool allowScrolling, System.Numerics.Vector2 initialSize, System.Numerics.Vector2 minimumSize)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L142) | Creates an immutable modal presentation snapshot.
`isVisible`: Whether the modal should currently be visible.
`blocksInteraction`: Whether the modal blocks regular editor interaction.
`canMove`: Whether the modal window can be moved.
`canResize`: Whether the modal window can be resized.
`allowScrolling`: Whether the modal window owns overflow scrolling.
`initialSize`: The initial size in unscaled editor units.
`minimumSize`: The minimum size in unscaled editor units. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.isVisible { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L163) | Gets whether the modal should currently be visible. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.blocksInteraction { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L168) | Gets whether the modal blocks regular editor interaction. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.canMove { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L173) | Gets whether the modal window can be moved. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.canResize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L178) | Gets whether the modal window can be resized. | — | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.allowScrolling { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L183) | Gets whether overflow scrolling belongs to the modal window. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Interactions.EditorModalExtension.Presentation.initialSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L188) | Gets the initial size in unscaled editor units. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Interactions.EditorModalExtension.Presentation.minimumSize { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L193) | Gets the minimum size in unscaled editor units. | — | — | +| [`public override string Inno.Editor.Interactions.EditorModalExtension.Presentation.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorModalExtension.Presentation.operator !=(Inno.Editor.Interactions.EditorModalExtension.Presentation left, Inno.Editor.Interactions.EditorModalExtension.Presentation right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorModalExtension.Presentation.operator ==(Inno.Editor.Interactions.EditorModalExtension.Presentation left, Inno.Editor.Interactions.EditorModalExtension.Presentation right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorModalExtension.Presentation.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorModalExtension.Presentation.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorModalExtension.Presentation.Equals(Inno.Editor.Interactions.EditorModalExtension.Presentation other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L116) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Interactions.EditorPanelExtension`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L11) | Describes one active dockable panel extension. | — | — | +| [`public string Inno.Editor.Interactions.EditorPanelExtension.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L37) | Gets the stable panel identifier. | — | — | +| [`public string Inno.Editor.Interactions.EditorPanelExtension.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L42) | Gets the visible panel title. | — | — | +| [`public int Inno.Editor.Interactions.EditorPanelExtension.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L47) | Gets the stable panel ordering value. | — | — | +| [`public bool Inno.Editor.Interactions.EditorPanelExtension.isOpen { get; set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L52) | Gets or sets whether this panel is open in the current extension generation. | — | — | +| [`public bool Inno.Editor.Interactions.EditorPanelExtension.TakeFocusRequest()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L64) | Consumes a pending request to focus this panel window. | once for each accepted focus request. | — | +| [`public bool Inno.Editor.Interactions.EditorPanelExtension.TryGetWindowPresentation(out bool useWindowPadding, out bool allowScrolling, out System.Numerics.Vector2 initialSize)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L81) | Safely reads the panel window-presentation policy through the active extension boundary.
`useWindowPadding`: The requested padding policy, or the safe default when the panel is quarantined.
`allowScrolling`: The requested scrolling policy, or the safe default when the panel is quarantined.
`initialSize`: The preferred first-use floating size, or zero when native sizing is requested. | when the policy was read without quarantining the panel. | — | +| [`public bool Inno.Editor.Interactions.EditorPanelExtension.Draw(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorPanelExtension.cs#L118) | Safely draws the panel body and quarantines a failing extension instance.
`context`: The active editor context supplied by the presentation backend. | when the panel completed drawing without being quarantined. | `System.ArgumentNullException`: Thrown when is . | +| [`public interface Inno.Editor.Interactions.IEditorSelectionCoordinator`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L10) | Coordinates host-owned selection and the Core identity lifetime of temporary presentations. | — | — | +| [`System.IDisposable Inno.Editor.Interactions.IEditorSelectionCoordinator.RegisterIdentityDomain(Inno.Core.Identity.IdentityAllocator allocator)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L31) | Registers the Core identity domain used by a temporary scene presentation.
`allocator`: The borrowed allocator owned by that presentation's runtime session. | A routing lease released after restoring the preceding selection. | `System.ArgumentNullException`: The allocator is null.
`System.InvalidOperationException`: The domain is already active or the caller is not the owner thread.
`System.ObjectDisposedException`: The coordinator has retired. | +| [`object? Inno.Editor.Interactions.IEditorSelectionCoordinator.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L36) | Gets the currently selected target, or when selection is empty. | — | — | +| [`void Inno.Editor.Interactions.IEditorSelectionCoordinator.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs#L44) | Replaces the current target after closing presentations owned by another target.
`target`: The new target, or to clear selection. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorSelectionSnapshot`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L10) | Captures ordered selection identities without retaining any domain object or extension instance. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorSelectionSnapshot.identities { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L25) | Gets unique selection identities in order; the final identity is the primary selection. | — | — | +| [`public Inno.Core.Identity.RuntimeIdentity? Inno.Editor.Interactions.EditorSelectionSnapshot.anchor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L29) | Gets the range anchor identity, or null when there is no registered anchor. | — | — | +| [`public int Inno.Editor.Interactions.EditorSelectionSnapshot.CopyTargets(Inno.Editor.Interactions.EditorInteractions interactions, System.Collections.Generic.ICollection destination) where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L46) | Resolves this immutable selection in its original domains, including same-identity recovery after a generation change.
`interactions`: The active session supplying the original identity domains.
`destination`: A temporary caller-owned collection to clear and fill.
`TTarget`: The target type accepted by the current presenter. | The number of currently available compatible targets; missing members remain represented by this snapshot. | — | +| [`public Inno.Core.Identity.RuntimeIdentity? Inno.Editor.Interactions.EditorSelectionSnapshot.primary { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L64) | Gets the primary identity, or null when this snapshot contains no registered targets. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorSelectionState`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L10) | Owns ordered transient selection through domain-qualified identities instead of retaining live objects. | — | — | +| [`public object? Inno.Editor.Interactions.EditorSelectionState.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L24) | Gets the current primary target for this callback, or null when unavailable or unselected. | — | — | +| [`public int Inno.Editor.Interactions.EditorSelectionState.count { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L28) | Gets the number of selected identities, including temporarily unavailable members. | — | — | +| [`public object? Inno.Editor.Interactions.EditorSelectionState.anchor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L32) | Gets the current range anchor, or null when the anchor is unavailable. | — | — | +| [`public ulong Inno.Editor.Interactions.EditorSelectionState.version { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L36) | Gets the selection revision, incremented only when membership, order or resolved generation changes. | — | — | +| [`public bool Inno.Editor.Interactions.EditorSelectionState.Contains(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L47) | Checks membership by the target's owning identity domain and generation.
`target`: The immediate target to inspect. | True when the target is a member of this selection. | — | +| [`public int Inno.Editor.Interactions.EditorSelectionState.CopyTargets(System.Collections.Generic.ICollection destination) where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L75) | Copies currently available selected targets into a caller-owned temporary collection.
`destination`: The collection to clear and fill; do not retain live entries across callbacks.
`TTarget`: The target type accepted by the caller. | The number of targets resolved in this callback. | `System.ArgumentNullException`: The destination is null. | +| [`public Inno.Editor.Interactions.EditorSelectionSnapshot Inno.Editor.Interactions.EditorSelectionState.Capture()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L95) | Captures the ordered runtime identities without retaining their resolved objects. | An immutable callback-independent snapshot; resolve identities afresh before using targets. | — | +| [`public bool Inno.Editor.Interactions.EditorSelectionState.TryGet(out TTarget? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L114) | Resolves the primary selected target as the requested type for immediate use.
`target`: Receives the available primary target, or null.
`TTarget`: The expected target type. | True when the current primary target has the requested type. | — | +| [`public enum Inno.Editor.Interactions.EditorToolbarIcon : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L6) | Identifies a presentation-independent symbol for an editor toolbar command. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L11) | No symbol is requested. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.Play = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L16) | Starts an operation or simulation. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.Stop = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L21) | Stops an operation or simulation. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.Pause = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L26) | Pauses an operation or simulation. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.Step = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L31) | Advances a paused operation by one step. | — | — | +| [`Inno.Editor.Interactions.EditorToolbarIcon.Edit = 5`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L36) | Returns to an editing state. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorToolbarItem`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L6) | Describes one resolved toolbar command. | — | — | +| [`public string Inno.Editor.Interactions.EditorToolbarItem.actionId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L25) | Gets the stable action dispatched when this item is pressed. | — | — | +| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItem.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L30) | Gets the resolved presentation-independent symbol. | — | — | +| [`public string Inno.Editor.Interactions.EditorToolbarItem.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L35) | Gets the resolved contextual tooltip. | — | — | +| [`public int Inno.Editor.Interactions.EditorToolbarItem.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L40) | Gets the stable ordering value. | — | — | +| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorToolbarItem.status { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItem.cs#L45) | Gets the current action availability and checked state. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorToolbarItemAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L9) | Places an editor action on a compact toolbar surface. | — | — | +| [`public Inno.Editor.Interactions.EditorToolbarItemAttribute.EditorToolbarItemAttribute(string area, Inno.Editor.Interactions.EditorToolbarIcon icon, string tooltip, int order = 0, Inno.Editor.Interactions.EditorToolbarIcon activeIcon = Inno.Editor.Interactions.EditorToolbarIcon.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L37) | Creates a toolbar placement for the annotated targetless action.
`area`: The exact interaction area whose toolbar receives the action.
`icon`: The symbol shown while the action is not checked.
`tooltip`: The fallback tooltip shown when the action has no contextual display name.
`order`: The stable ordering value within the toolbar.
`activeIcon`: The symbol shown while the action is checked, or to retain . | — | `System.ArgumentException`: Thrown when or is empty, or when is .
`System.ArgumentOutOfRangeException`: Thrown when an icon value is not defined. | +| [`public string Inno.Editor.Interactions.EditorToolbarItemAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L65) | Gets the exact toolbar interaction area. | — | — | +| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItemAttribute.icon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L70) | Gets the symbol shown while the action is not checked. | — | — | +| [`public Inno.Editor.Interactions.EditorToolbarIcon Inno.Editor.Interactions.EditorToolbarItemAttribute.activeIcon { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L75) | Gets the optional replacement symbol shown while the action is checked. | — | — | +| [`public string Inno.Editor.Interactions.EditorToolbarItemAttribute.tooltip { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L80) | Gets the fallback tooltip. | — | — | +| [`public int Inno.Editor.Interactions.EditorToolbarItemAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarItemAttribute.cs#L85) | Gets the stable ordering value. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorToolbarModel`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarModel.cs#L8) | Contains an immutable resolved toolbar for one interaction area. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorToolbarModel.items { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarModel.cs#L18) | Gets visible toolbar commands in deterministic display order. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorPlanarNavigation`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L9) | Shares captured planar gestures and mouse-anchored zoom math between scene and graph canvases. | — | — | +| [`public bool Inno.Editor.Interactions.EditorPlanarNavigation.isPanning { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L16) | Gets whether this canvas owns a pan gesture, including while the pointer is outside its bounds. | — | — | +| [`public bool Inno.Editor.Interactions.EditorPlanarNavigation.Update(bool hovered, bool primaryPressed, bool middlePressed, bool primaryDown, bool middleDown, bool alt, bool allowAltPrimary = true)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L45) | Advances pointer capture using logical button state rather than window-local release events.
`hovered`: Whether a new gesture may start in this canvas.
`primaryPressed`: Whether the primary button was pressed this frame.
`middlePressed`: Whether the middle button was pressed this frame.
`primaryDown`: Current global primary button state.
`middleDown`: Current global middle button state.
`alt`: Whether Alt is held when the gesture starts.
`allowAltPrimary`: Whether Alt-primary belongs to pan rather than another navigation mode. | Whether this canvas owns the current pointer gesture. | — | +| [`public void Inno.Editor.Interactions.EditorPlanarNavigation.Cancel()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L67) | Releases capture when a canvas closes or a gesture is cancelled. | — | — | +| [`public static float Inno.Editor.Interactions.EditorPlanarNavigation.WheelFactor(float wheel, float sensitivity = 0.16)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L84) | Converts wheel input to the common exponential magnification used by Editor canvases.
`wheel`: Logical wheel units, independent of framebuffer DPI.
`sensitivity`: Positive exponential sensitivity. | A finite positive magnification factor; positive wheel magnifies content. | `System.ArgumentOutOfRangeException`: Input is non-finite or sensitivity is not positive. | +| [`public static System.Numerics.Vector2 Inno.Editor.Interactions.EditorPlanarNavigation.ZoomOrigin(System.Numerics.Vector2 origin, System.Numerics.Vector2 pivot, float previousScale, float nextScale)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L116) | Changes the screen-space origin so zoom preserves the same content point beneath the cursor.
`origin`: Current logical-pixel content origin.
`pivot`: Logical-pixel cursor position relative to the canvas.
`previousScale`: Previous positive content magnification.
`nextScale`: New positive content magnification after clamping. | The origin preserving the pivot's content coordinate. | `System.ArgumentOutOfRangeException`: An input is non-finite or a scale is not positive. | +| [`public Inno.Editor.Interactions.EditorPlanarNavigation.EditorPlanarNavigation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorPlanarNavigation.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public enum Inno.Editor.Interactions.EditorViewportPointerPhase : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L10) | Selects the lifecycle phase of one viewport pointer sample. | — | — | +| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Down = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L15) | The pointer button began a gesture. | — | — | +| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Move = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L19) | The pointer moved or changed hover position. | — | — | +| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Up = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L23) | The pointer button ended a gesture. | — | — | +| [`Inno.Editor.Interactions.EditorViewportPointerPhase.Cancel = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L27) | The platform cancelled the gesture. | — | — | +| [`public enum Inno.Editor.Interactions.EditorViewportCursor : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L33) | Selects a presentation-independent cursor requested by a viewport tool. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.Arrow = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L38) | Uses the normal arrow cursor. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.Crosshair = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L42) | Uses a precise crosshair. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.Hand = 2`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L46) | Uses a hand suitable for panning. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.Move = 3`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L50) | Uses four-direction movement arrows. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.ResizeHorizontal = 4`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L54) | Uses horizontal resizing arrows. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.ResizeVertical = 5`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L58) | Uses vertical resizing arrows. | — | — | +| [`Inno.Editor.Interactions.EditorViewportCursor.Hidden = 6`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L62) | Hides the pointer while the tool owns it. | — | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorViewportShortcut : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Contains one presentation-independent shortcut sample for a focused viewport tool. | — | — | +| [`public Inno.Editor.Interactions.EditorViewportShortcut.EditorViewportShortcut(Inno.Core.Input.KeyCode key, Inno.Core.Input.KeyModifier modifiers = Inno.Core.Input.KeyModifier.None, bool repeat = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L82) | Creates one viewport shortcut sample.
`key`: Pressed key.
`modifiers`: Active keyboard modifiers.
`repeat`: Whether the platform generated an auto-repeat press. | — | — | +| [`public Inno.Core.Input.KeyCode Inno.Editor.Interactions.EditorViewportShortcut.key { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L95) | Gets the pressed key. | — | — | +| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorViewportShortcut.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L100) | Gets active keyboard modifiers. | — | — | +| [`public bool Inno.Editor.Interactions.EditorViewportShortcut.repeat { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L105) | Gets whether the shortcut is an auto-repeat press. | — | — | +| [`public override string Inno.Editor.Interactions.EditorViewportShortcut.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorViewportShortcut.operator !=(Inno.Editor.Interactions.EditorViewportShortcut left, Inno.Editor.Interactions.EditorViewportShortcut right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorViewportShortcut.operator ==(Inno.Editor.Interactions.EditorViewportShortcut left, Inno.Editor.Interactions.EditorViewportShortcut right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorViewportShortcut.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorViewportShortcut.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorViewportShortcut.Equals(Inno.Editor.Interactions.EditorViewportShortcut other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L68) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly record struct Inno.Editor.Interactions.EditorViewportPointerEvent : System.IEquatable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Contains one immutable viewport pointer sample in screen and world coordinates. | — | — | +| [`public Inno.Editor.Interactions.EditorViewportPointerEvent.EditorViewportPointerEvent(int pointerId, Inno.Editor.Interactions.EditorViewportPointerPhase phase, Inno.Core.Mathematics.Vector2 screenPosition, Inno.Core.Mathematics.Vector2 worldPosition, int button, Inno.Core.Input.KeyModifier modifiers)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L134) | Creates one viewport pointer sample.
`pointerId`: Platform pointer identity.
`phase`: Gesture lifecycle phase.
`screenPosition`: Viewport-local pixel position.
`worldPosition`: Corresponding world position.
`button`: Platform button number.
`modifiers`: Active keyboard modifiers. | — | — | +| [`public int Inno.Editor.Interactions.EditorViewportPointerEvent.pointerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L153) | Gets the platform pointer identity. | — | — | +| [`public Inno.Editor.Interactions.EditorViewportPointerPhase Inno.Editor.Interactions.EditorViewportPointerEvent.phase { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L158) | Gets the gesture lifecycle phase. | — | — | +| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportPointerEvent.screenPosition { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L163) | Gets the viewport-local pixel position. | — | — | +| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportPointerEvent.worldPosition { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L168) | Gets the corresponding world position. | — | — | +| [`public int Inno.Editor.Interactions.EditorViewportPointerEvent.button { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L173) | Gets the platform button number. | — | — | +| [`public Inno.Core.Input.KeyModifier Inno.Editor.Interactions.EditorViewportPointerEvent.modifiers { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L178) | Gets active keyboard modifiers. | — | — | +| [`public override string Inno.Editor.Interactions.EditorViewportPointerEvent.ToString()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Interactions.EditorViewportPointerEvent.operator !=(Inno.Editor.Interactions.EditorViewportPointerEvent left, Inno.Editor.Interactions.EditorViewportPointerEvent right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Interactions.EditorViewportPointerEvent.operator ==(Inno.Editor.Interactions.EditorViewportPointerEvent left, Inno.Editor.Interactions.EditorViewportPointerEvent right)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Interactions.EditorViewportPointerEvent.GetHashCode()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Interactions.EditorViewportPointerEvent.Equals(object obj)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorViewportPointerEvent.Equals(Inno.Editor.Interactions.EditorViewportPointerEvent other)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L111) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | | [`public interface Inno.Editor.Interactions.IEditorViewportCoordinateConverter`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L184) | Converts coordinates for one viewport camera without exposing its presentation backend. | — | — | +| [`Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.IEditorViewportCoordinateConverter.ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L195) | Converts viewport-local pixel coordinates to world coordinates.
`screenPosition`: Viewport-local pixel coordinates. | World coordinates. | — | +| [`Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.IEditorViewportCoordinateConverter.WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L206) | Converts world coordinates to viewport-local pixel coordinates.
`worldPosition`: World coordinates. | Viewport-local pixel coordinates. | — | +| [`public abstract class Inno.Editor.Interactions.EditorViewportTool`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L212) | Defines one presentation-independent, reloadable viewport editing tool. | — | — | +| [`public abstract string Inno.Editor.Interactions.EditorViewportTool.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L217) | Gets the globally stable tool identity. | — | — | +| [`public virtual Inno.Editor.Interactions.EditorViewportCursor Inno.Editor.Interactions.EditorViewportTool.cursor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L222) | Gets the cursor requested while this tool is active. | — | — | +| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerDown(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L233) | Handles a pointer press.
`context`: Gesture and coordinate context.
`pointer`: Immutable pointer sample. | — | — | +| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerMove(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L249) | Handles pointer movement or hover.
`context`: Gesture and coordinate context.
`pointer`: Immutable pointer sample. | — | — | +| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerUp(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L265) | Handles a pointer release.
`context`: Gesture and coordinate context.
`pointer`: Immutable pointer sample. | — | — | +| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.OnPointerCancel(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L281) | Handles platform cancellation of an active gesture.
`context`: Gesture and coordinate context.
`pointer`: Immutable pointer sample. | — | — | +| [`public virtual bool Inno.Editor.Interactions.EditorViewportTool.OnShortcut(Inno.Editor.Interactions.EditorViewportToolContext context, Inno.Editor.Interactions.EditorViewportShortcut shortcut)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L302) | Handles a focused viewport shortcut before global action routing.
`context`: Gesture and coordinate context.
`shortcut`: Pressed key sample. | when the tool consumed the key. | — | +| [`public virtual void Inno.Editor.Interactions.EditorViewportTool.DrawOverlay(Inno.Editor.Interactions.EditorViewportToolContext context)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L316) | Draws transient gizmos and diagnostics over the viewport.
`context`: Gesture and coordinate context. | — | — | +| [`protected Inno.Editor.Interactions.EditorViewportTool.EditorViewportTool()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportTool.cs#L212) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Interactions.EditorViewportToolContext`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L8) | Coordinates pointer capture, transforms, and one history transaction for a viewport gesture. | — | — | +| [`public bool Inno.Editor.Interactions.EditorViewportToolContext.hasPointerCapture { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L25) | Gets whether this tool owns a pointer. | — | — | +| [`public int? Inno.Editor.Interactions.EditorViewportToolContext.capturedPointerId { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L29) | Gets the captured pointer identity, or . | — | — | +| [`public bool Inno.Editor.Interactions.EditorViewportToolContext.hasHistoryGesture { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L33) | Gets whether a history transaction is active for the current gesture. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolContext.CapturePointer(int pointerId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L41) | Captures one pointer until explicit release or cancellation.
`pointerId`: Platform pointer identity. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolContext.ReleasePointer()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L51) | Releases the currently captured pointer. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolContext.BeginHistoryGesture(string name)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L64) | Begins the only history transaction allowed for the current captured gesture.
`name`: User-facing Undo operation name. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolContext.CompleteHistoryGesture(bool commit)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L80) | Commits or rolls back the current gesture transaction exactly once.
`commit`: to commit; otherwise, roll back. | — | — | +| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportToolContext.ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L107) | Converts viewport-local pixel coordinates to world coordinates.
`screenPosition`: Viewport-local pixel coordinates. | World coordinates. | — | +| [`public Inno.Core.Mathematics.Vector2 Inno.Editor.Interactions.EditorViewportToolContext.WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs#L117) | Converts world coordinates to viewport-local pixel coordinates.
`worldPosition`: World coordinates. | Viewport-local pixel coordinates. | — | +| [`public sealed class Inno.Editor.Interactions.EditorViewportToolSession : System.IDisposable`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L8) | Routes focused viewport input to one active tool while enforcing pointer-capture ownership. | — | — | +| [`public Inno.Editor.Interactions.EditorViewportToolSession.EditorViewportToolSession(Inno.Editor.Interactions.IEditorHistory history, Inno.Editor.Interactions.IEditorViewportCoordinateConverter coordinates)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L23) | Creates a viewport tool session.
`history`: History stack used by gesture transactions.
`coordinates`: Current viewport coordinate converter. | — | — | +| [`public Inno.Editor.Interactions.EditorViewportTool? Inno.Editor.Interactions.EditorViewportToolSession.tool { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L33) | Gets the active tool, or . | — | — | +| [`public Inno.Editor.Interactions.EditorViewportCursor Inno.Editor.Interactions.EditorViewportToolSession.cursor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L38) | Gets the requested cursor for the active tool. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolSession.SetTool(Inno.Editor.Interactions.EditorViewportTool? tool)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L46) | Activates a tool after cancelling any current gesture.
`tool`: Tool to activate, or to deactivate. | — | — | +| [`public bool Inno.Editor.Interactions.EditorViewportToolSession.HandlePointer(Inno.Editor.Interactions.EditorViewportPointerEvent pointer)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L64) | Routes one immutable pointer sample.
`pointer`: Pointer sample with precomputed world coordinates. | when an active tool accepted the sample. | — | +| [`public bool Inno.Editor.Interactions.EditorViewportToolSession.HandleShortcut(Inno.Editor.Interactions.EditorViewportShortcut shortcut)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L97) | Routes one focused keyboard shortcut.
`shortcut`: Pressed key sample. | when consumed. | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolSession.DrawOverlay()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L106) | Draws the active tool overlay. | — | — | +| [`public void Inno.Editor.Interactions.EditorViewportToolSession.Dispose()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolSession.cs#L115) | Cancels active gesture state and deactivates the tool. | — | — | ## 项目依赖 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):公开引用边界由实际签名核对。 -- [Inno.Core.Events](../core/Inno.Core.Events.md):公开引用边界由实际签名核对。 -- [Inno.Core.Input](../core/Inno.Core.Input.md):公开引用边界由实际签名核对。 -- [Inno.Core.Mathematics](../core/Inno.Core.Mathematics.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Core.Identity` | 公开签名依赖 | +| `Inno.Core.Events` | 公开签名依赖 | +| `Inno.Core.Input` | 公开签名依赖 | +| `Inno.Core.Mathematics` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.FileBrowser.md b/docs/editor/Inno.Editor.Panel.FileBrowser.md index fb6f59ce7..816a99a66 100644 --- a/docs/editor/Inno.Editor.Panel.FileBrowser.md +++ b/docs/editor/Inno.Editor.Panel.FileBrowser.md @@ -36,8 +36,8 @@ Import Sample 的 Action 只启动资产层事务,`Samples/` 中的 feature Mo | `AssetEditorContext` | 当前 `EditorContext`、interactions、路径、Asset 信息和实例。 | | `AssetFileEntry` | EditorScript 可检查的源文件身份、路径、扩展名和只读状态。 | | `AssetIconAttribute` | 按 imported Asset 类型或 source extension 配置 Tree/List/Grid 共用图标;glyph 使用 `InnoEditor.ImGui.ImGuiIcon`。 | -| `AssetEditorModule.GetIcon` | 为其他 Editor presentation 解析完全相同的 Asset 图标。 | -| `AssetEditorModule.CreateSource` / `BeginCreatedSourceRename` | 让其他 Editor feature 复用原子 source 创建,并在用户发起创建时接入统一选择与 inline Rename。 | +| `AssetEditingModule.GetIcon` | 为其他 Editor presentation 解析完全相同的 Asset 图标。 | +| `AssetEditingModule.CreateSource` / `BeginCreatedSourceRename` | 让其他 Editor feature 复用原子 source 创建,并在用户发起创建时接入统一选择与 inline Rename。 | ## 为新 Asset 添加双击与右键行为 @@ -45,7 +45,8 @@ Import Sample 的 Action 只启动资产层事务,`Samples/` 中的 feature Mo using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; [AssetEditor(typeof(TextAsset), useForChildren: true, priority: 100)] public sealed class TextAssetEditor : AssetEditor @@ -59,7 +60,7 @@ public sealed class TextAssetEditor : AssetEditor Asset Rename/Delete 的物理事务始终由 `AssetPipeline` 执行。AssetEditor 只能验证以及接收提交后的通知,不能自行移动 source/meta/artifact,因此外部文件变化与 Editor 操作拥有同一身份规则。 -Create Asset、Create Folder、Import Sample、Rename、Move 与 Delete 都接入共享中立 Undo/Redo。每个 Asset 创建模板以及 Shader Editor 的创建、复制、折叠为 Subgraph 流程都必须通过 `AssetEditorModule.CreateSource`,并记录一个 `Create Asset` 历史项。该历史项保存完整 source 与 `.imeta`;Undo 删除已创建资产,Redo 恢复同一 persistent ID,而不是创建新的对象身份。Rename/Move 只记录 source/target path;Delete 与 Sample Import 同样把 source、目录结构和 `.imeta` 编码进 History payload。大 payload 自动落到 `/Library/Editor/History`,Undo 先在临时目录完整验证 archive,再提交回 Asset root 并 `Rescan`,因此恢复失败不会留下半个目录。目标发生外部冲突时操作失败并留在原栈,绝不覆盖新文件。Asset Browser selection 仅在文件系统事务成功后 best-effort 更新,通知异常不改变 History 结果。 +Create Asset、Create Folder、Import Sample、Rename、Move 与 Delete 都接入共享中立 Undo/Redo。每个 Asset 创建模板以及 Shader Editor 的创建、复制、折叠为 Subgraph 流程都必须通过 `AssetEditingModule.CreateSource`,并记录一个 `Create Asset` 历史项。该历史项保存完整 source 与 `.imeta`;Undo 删除已创建资产,Redo 恢复同一 persistent ID,而不是创建新的对象身份。Rename/Move 只记录 source/target path;Delete 与 Sample Import 同样把 source、目录结构和 `.imeta` 编码进 History payload。大 payload 自动落到 `/Library/Editor/History`,Undo 先在临时目录完整验证 archive,再提交回 Asset root 并 `Rescan`,因此恢复失败不会留下半个目录。目标发生外部冲突时操作失败并留在原栈,绝不覆盖新文件。Asset Browser selection 仅在文件系统事务成功后 best-effort 更新,通知异常不改变 History 结果。 ## 文件与目录移动 @@ -103,7 +104,8 @@ Material 与 Render Pipeline 已分别由自己的 feature 贡献模板。其他 ```csharp using Inno.Assets; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; [AssetIcon(typeof(TextAsset), "T", useForChildren: true, priority: 100)] [AssetIcon(".txt", "T")] @@ -120,7 +122,7 @@ internal static class TextAssetIcons 内建 Text、Binary、Scene、Prefab 和 Scripting 图标全部在 `BuiltInAssetIcons` 上使用 extension overload 声明,没有基于具体 Asset CLR 类型的引用。图标发现不依赖具体 Asset 类型项目;FileBrowser 的公开资产 API 仍声明 `Inno.Assets` 依赖,Sample module 通过实现依赖 `Inno.Editor.Scripting` 查询编译状态。内部 `AssetIconRegistry` 扫描当前 TypeCache snapshot 中的声明类型。EditorScripts 热重载时,新增或修改声明会随候选代际原子生效;移除声明或整个容器类型后,Registry 会释放旧映射并恢复优先级较低的内建声明,没有匹配时则使用通用 File icon。 -`AssetEditorModule.GetIcon(entry)` 是唯一对外 presentation resolver,同时通过 `IInspectionIconProvider` 向 Inspection 基础设施提供同一个规则。File Browser 的三种视图与 Asset Inspection Header 都调用该入口,不复制 extension switch,也不各自持有 Registry snapshot。Registry 先按类型/extension 选中声明;若 declaration 字符串是已注册 Settings path,就直接读取其中的 `value`,否则把它当作 literal glyph。Settings 基础项目不提供 icon resolver。 +`AssetEditingModule.GetIcon(entry)` 是唯一对外 presentation resolver,同时通过 `IInspectionIconProvider` 向 Inspection 基础设施提供同一个规则。File Browser 的三种视图与 Asset Inspection Header 都调用该入口,不复制 extension switch,也不各自持有 Registry snapshot。Registry 先按类型/extension 选中声明;若 declaration 字符串是已注册 Settings path,就直接读取其中的 `value`,否则把它当作 literal glyph。Settings 基础项目不提供 icon resolver。 ## Rename 与打开 @@ -146,217 +148,30 @@ EditorScripts 使用 `InnoEditor.Assets` 扩展 AssetEditor、声明 AssetIcon/A ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Panel.FileBrowser.AssetBrowserRoot` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Panel.FileBrowser.AssetBrowserRoot.Assets = 0`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L21) | The writable project Assets authoring root. | — | — | -| [`Inno.Editor.Panel.FileBrowser.AssetBrowserRoot.Plugins = 1`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L26) | The read-only Plugins installation root. | — | — | -| [`public enum Inno.Editor.Panel.FileBrowser.AssetBrowserRoot : int`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L16) | Identifies the authoring or installed-content root displayed by the Asset Browser. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetBrowserState` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string? Inno.Editor.Panel.FileBrowser.AssetBrowserState.GetSelectedPath(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L88) | Gets the selected asset path when the editor-wide target belongs to the Asset Browser.
返回:The normalized source-relative path, or null when another target type is selected.
`context`:The shared editor context containing the global selection. | — | `System.ArgumentNullException`:Thrown when context is null. | -| [`public void Inno.Editor.Panel.FileBrowser.AssetBrowserState.Select(Inno.Editor.Core.EditorContext context, string? relativePath)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L171) | Selects an asset path through the editor-wide selection state, or clears Asset selection.
`context`:The shared editor context whose selection should be updated.
`relativePath`:The source-relative path to select, or an empty value to clear selection. | — | `System.ArgumentNullException`:Thrown when context is null. | -| [`public void Inno.Editor.Panel.FileBrowser.AssetBrowserState.SetCurrentDirectory(string relativePath)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L142) | Sets the current Asset Browser directory and infers its root from the isolated source identity.
`relativePath`:The isolated source-relative directory path, or an empty value for the currently displayed root. | — | — | -| [`public void Inno.Editor.Panel.FileBrowser.AssetBrowserState.SetRoot(Inno.Editor.Panel.FileBrowser.AssetBrowserRoot value)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L134) | Switches the displayed root while preserving the last directory visited in each root.
`value`:The authoring or installed-content root to display. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetBrowserState.currentDirectory { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L67) | Gets the current directory inside Inno.Editor.Panel.FileBrowser.AssetBrowserState.root. An empty value identifies that root's overview. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetBrowserState.projectDirectory { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L74) | Gets the most recently visited writable project directory, independently of the displayed root. | — | — | -| [`public Inno.Editor.Panel.FileBrowser.AssetBrowserRoot Inno.Editor.Panel.FileBrowser.AssetBrowserState.root { get; private set; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L62) | Gets the root currently displayed by the Asset Browser. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetBrowserState`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs#L32) | Stores asset browser navigation independently from global object selection. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetEditor` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Panel.FileBrowser.AssetEditor.AssetEditor()`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public virtual bool Inno.Editor.Panel.FileBrowser.AssetEditor.CanOpen(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L19) | Gets whether the fallback asset editor can open the supplied source entry.
返回:true when Inno.Editor.Panel.FileBrowser.AssetEditor.Open(Inno.Editor.Panel.FileBrowser.AssetEditorContext) can handle the entry; otherwise, false.
`context`:The immutable source and catalog snapshot for the operation. | — | — | -| [`public virtual bool Inno.Editor.Panel.FileBrowser.AssetEditor.CanStartDrag(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L97) | Gets whether the supplied entry can begin a managed editor drag operation.
返回:true when drag data can be created; otherwise, false.
`context`:The immutable source and catalog snapshot for the entry. | — | — | -| [`public virtual Inno.Editor.Interactions.EditorDragData Inno.Editor.Panel.FileBrowser.AssetEditor.CreateDragData(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L108) | Creates the managed source, preview label, and validity predicate for an asset drag.
返回:The managed drag data published by the Asset Browser.
`context`:The immutable source and catalog snapshot for the entry. | — | — | -| [`public virtual void Inno.Editor.Panel.FileBrowser.AssetEditor.OnDeleted(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L84) | Runs after an asset deletion transaction commits successfully.
`context`:The immutable snapshot captured before the entry was deleted. | — | — | -| [`public virtual void Inno.Editor.Panel.FileBrowser.AssetEditor.OnRenamed(Inno.Editor.Panel.FileBrowser.AssetEditorContext context, string oldPath, string newPath)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L60) | Runs after an asset move transaction commits successfully.
`context`:The immutable snapshot captured before the move.
`oldPath`:The previous source-relative path.
`newPath`:The committed source-relative destination path. | — | — | -| [`public virtual void Inno.Editor.Panel.FileBrowser.AssetEditor.Open(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L27) | Opens the supplied asset entry when no more specific typed open action handled it.
`context`:The immutable source and catalog snapshot for the operation. | — | — | -| [`public virtual Inno.Editor.Panel.FileBrowser.AssetOperationValidation Inno.Editor.Panel.FileBrowser.AssetEditor.ValidateDelete(Inno.Editor.Panel.FileBrowser.AssetEditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L76) | Validates a requested asset deletion before the AssetPipeline transaction begins.
返回:A valid result when the transaction may proceed, or a diagnostic describing the rejection.
`context`:The immutable snapshot of the entry that would be deleted. | — | — | -| [`public virtual Inno.Editor.Panel.FileBrowser.AssetOperationValidation Inno.Editor.Panel.FileBrowser.AssetEditor.ValidateRename(Inno.Editor.Panel.FileBrowser.AssetEditorContext context, string targetPath)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L43) | Validates a requested asset move before the AssetPipeline transaction begins.
返回:A valid result when the transaction may proceed, or a diagnostic describing the rejection.
`context`:The immutable snapshot of the source entry before the move.
`targetPath`:The normalized source-relative destination path. | — | — | -| [`public abstract class Inno.Editor.Panel.FileBrowser.AssetEditor`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs#L8) | Customizes editor interactions for one imported asset type. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetEditorAttribute` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.FileBrowser.AssetEditorAttribute.AssetEditorAttribute(System.Type assetType, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs#L26) | Creates an asset-editor registration for an imported runtime asset type.
`assetType`:The imported asset type handled by the editor.
`useForChildren`:Whether the editor may also handle assignable derived asset types.
`priority`:The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`:Thrown when assetType is null. | -| [`public System.Type Inno.Editor.Panel.FileBrowser.AssetEditorAttribute.assetType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs#L39) | Gets the imported asset type handled by the editor. | — | — | -| [`public int Inno.Editor.Panel.FileBrowser.AssetEditorAttribute.priority { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs#L49) | Gets the tie-breaking priority. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetEditorAttribute.useForChildren { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs#L44) | Gets whether derived asset types are accepted. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetEditorAttribute : System.Attribute`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs#L9) | Associates an asset editor with an imported asset type. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetEditorContext` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Type? Inno.Editor.Panel.FileBrowser.AssetEditorContext.assetType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L100) | Gets the resolved imported asset type when available. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Panel.FileBrowser.AssetEditorContext.editorContext { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L70) | Gets the shared editor context. | — | — | -| [`public Inno.Assets.AssetInfo? Inno.Editor.Panel.FileBrowser.AssetEditorContext.info { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L95) | Gets the cataloged asset information when available. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Panel.FileBrowser.AssetEditorContext.interactions { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L75) | Gets the active editor interaction entry point. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetEditorContext.isDirectory { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L90) | Gets whether the source represents a directory. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetEditorContext.name { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L85) | Gets the final source path segment. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetEditorContext.relativePath { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L80) | Gets the source-relative path. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetEditorContext`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs#L12) | Provides an immutable snapshot for an asset editor operation. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetEditorModule` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.FileBrowser.AssetEditorModule.AssetEditorModule(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Editor.Settings.EditorSettings settings, Inno.Assets.Pipeline.AssetPipeline pipeline, Inno.Plugins.Authoring.PluginEnvironment plugins, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L61) | Creates the Asset Browser feature module.
`interactions`:The active editor interaction entry point.
`settings`:The project Settings service that owns semantic icon values.
`pipeline`:The isolated authoring asset pipeline used by browser operations.
`plugins`:The active Plugin catalog that authoritatively identifies Plugin-owned source mounts.
`types`:The active type catalog used to build extension registry snapshots.
`logs`:The application log router used for Asset Browser operation diagnostics. | — | `System.ArgumentNullException`:Thrown when any required service is null. | -| [`public void Inno.Editor.Panel.FileBrowser.AssetEditorModule.BeginCreatedSourceRename(Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs#L22) | Selects a newly created source and starts its shared inline rename interaction.
`entry`:The newly indexed writable source. | — | — | -| [`protected override void Inno.Editor.Panel.FileBrowser.AssetEditorModule.Capture(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L93) | Captures an immutable snapshot of the current observable state.
`state`:The lifecycle or domain state applied by this operation. | — | — | -| [`public Inno.Assets.Pipeline.AssetFileEntry Inno.Editor.Panel.FileBrowser.AssetEditorModule.CreateSource(Inno.Assets.AssetPath path, System.ReadOnlySpan bytes)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs#L48) | Creates a native or ordinary-text asset source as one recoverable shared-history operation.
返回:The indexed source identity, even when its first import reports a source error.
`path`:A new file in an existing writable asset directory.
`bytes`:Complete source bytes supplied by the feature's native writer. | — | `System.IO.IOException`:The source already exists, has no existing parent, or cannot be installed.
`System.InvalidOperationException`:The source mount is unavailable/read-only or history cannot retain the change. | -| [`public string Inno.Editor.Panel.FileBrowser.AssetEditorModule.GetIcon(Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L542) | Resolves the presentation icon registered for an asset type or source extension.
返回:The most specific registered icon, or the built-in directory or file icon when no registration matches the entry.
`entry`:The source entry whose presentation icon should be resolved. | — | `System.ArgumentNullException`:Thrown when entry is null. | -| [`protected override void Inno.Editor.Panel.FileBrowser.AssetEditorModule.OnDispose()`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L582) | Releases resources retained by this feature after it has stopped. | — | — | -| [`protected override void Inno.Editor.Panel.FileBrowser.AssetEditorModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L563) | Initializes this feature when its owning runtime becomes active.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Panel.FileBrowser.AssetEditorModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L574) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Panel.FileBrowser.AssetEditorModule.Restore(Inno.Editor.Core.EditorState state)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L106) | Restores the supplied snapshot while preserving current invariants.
`state`:The lifecycle or domain state applied by this operation. | — | — | -| [`public Inno.Editor.Panel.FileBrowser.AssetBrowserState Inno.Editor.Panel.FileBrowser.AssetEditorModule.browser { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs#L85) | Gets shared Asset Browser navigation and selection state. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetEditorModule : Inno.Editor.Core.EditorModule, Inno.Editor.Inspection.IInspectionIconProvider`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs#L14) | Handles asset creation actions in the Editor file browser. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetIconAttribute` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.FileBrowser.AssetIconAttribute.AssetIconAttribute(string extension, string icon, int priority = 0)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L71) | Creates an icon declaration for files ending with a source extension.
`extension`:The simple or compound file extension. A leading period is optional and matching is case-insensitive.
`icon`:The Editor icon glyph to render.
`priority`:The tie-breaking priority after extension specificity. | — | `System.ArgumentException`:Thrown when extension is empty or contains a path separator, or when icon is empty. | -| [`public Inno.Editor.Panel.FileBrowser.AssetIconAttribute.AssetIconAttribute(System.Type assetType, string icon, bool useForChildren = false, int priority = 0)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L43) | Creates an icon declaration using a glyph from the Editor icon catalog.
`assetType`:The imported asset type represented by the icon.
`icon`:The Editor icon glyph to render.
`useForChildren`:Whether the declaration may also represent derived asset types.
`priority`:The tie-breaking priority after exactness and inheritance distance. | — | `System.ArgumentNullException`:Thrown when assetType is null.
`System.ArgumentException`:Thrown when icon is empty. | -| [`public System.Type? Inno.Editor.Panel.FileBrowser.AssetIconAttribute.assetType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L85) | Gets the imported asset type represented by this declaration, or null for an extension declaration. | — | — | -| [`public string? Inno.Editor.Panel.FileBrowser.AssetIconAttribute.extension { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L91) | Gets the normalized source extension represented by this declaration, or null for a type declaration. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetIconAttribute.icon { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L96) | Gets the Editor icon glyph to render. | — | — | -| [`public int Inno.Editor.Panel.FileBrowser.AssetIconAttribute.priority { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L107) | Gets the tie-breaking priority after target specificity. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetIconAttribute.useForChildren { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L102) | Gets whether a type declaration may also represent derived asset types. Extension declarations always return false. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetIconAttribute : System.Attribute`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs#L20) | Declares the icon used by the Asset Browser for an imported asset type or source extension. | Apply one or more declarations to a container type. The container has no runtime responsibility; it only keeps related icon declarations together so the type catalog can discover them. | — | - -### `Inno.Editor.Panel.FileBrowser.AssetImportSettingsEdits` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.FileBrowser.AssetImportSettingsEdits.AssetImportSettingsEdits(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Types.TypeCatalog types, Inno.Editor.Interactions.EditorInteractions interactions)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs#L39) | Uses the authoring owners responsible for source identity, converter generations and shared undo.
`assets`:Authoritative asset pipeline.
`serialization`:Current owner converter registry.
`types`:Current type generation owner.
`interactions`:Shared Editor history owner. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetImportSettingsEdits.Apply(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable settings, string expectedFingerprint)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs#L66) | Saves one settings gesture, recording neutral before/after properties even when reimport reports an error.
返回:Whether the saved configuration reimported successfully; false does not mean the settings were discarded.
`path`:Writable source asset path.
`settings`:Detached current-generation settings value.
`expectedFingerprint`:Sidecar fingerprint captured when editing began. | — | — | -| [`public sealed class Inno.Editor.Panel.FileBrowser.AssetImportSettingsEdits : Inno.Editor.Core.EditorModule`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs#L16) | Applies importer configuration through the common sidecar pipeline and stable-identity Editor history. | — | — | - -### `Inno.Editor.Panel.FileBrowser.AssetOperationValidation` - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.FileBrowser.AssetOperationValidation.AssetOperationValidation(bool isValid, string message)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L17) | Creates the validation result returned before an asset transaction begins.
`isValid`:Whether the requested asset operation may proceed.
`message`:The user-facing rejection diagnostic, or an empty string for a valid result. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetOperationValidation.Equals(Inno.Editor.Panel.FileBrowser.AssetOperationValidation other)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Panel.FileBrowser.AssetOperationValidation.Equals(object obj)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Panel.FileBrowser.AssetOperationValidation.GetHashCode()`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static Inno.Editor.Panel.FileBrowser.AssetOperationValidation Inno.Editor.Panel.FileBrowser.AssetOperationValidation.Invalid(string message)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L49) | Creates a failed asset-operation validation result.
返回:An invalid validation result containing the supplied diagnostic.
`message`:The user-facing diagnostic explaining why the operation was rejected. | — | — | -| [`public static bool Inno.Editor.Panel.FileBrowser.AssetOperationValidation.operator ==(Inno.Editor.Panel.FileBrowser.AssetOperationValidation left, Inno.Editor.Panel.FileBrowser.AssetOperationValidation right)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Panel.FileBrowser.AssetOperationValidation.operator !=(Inno.Editor.Panel.FileBrowser.AssetOperationValidation left, Inno.Editor.Panel.FileBrowser.AssetOperationValidation right)`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Panel.FileBrowser.AssetOperationValidation.ToString()`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Panel.FileBrowser.AssetOperationValidation.isValid { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L28) | Gets whether the operation may proceed. | — | — | -| [`public string Inno.Editor.Panel.FileBrowser.AssetOperationValidation.message { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L33) | Gets the validation diagnostic. | — | — | -| [`public static Inno.Editor.Panel.FileBrowser.AssetOperationValidation Inno.Editor.Panel.FileBrowser.AssetOperationValidation.valid { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L38) | Gets a successful validation result. | — | — | -| [`public readonly record struct Inno.Editor.Panel.FileBrowser.AssetOperationValidation : System.IEquatable`](../../src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs#L6) | Describes whether an editor asset operation may proceed. | — | — | +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Scripting.Compiler](../scripting/Inno.Scripting.Compiler.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Assets](Inno.Editor.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scripting](Inno.Editor.Scripting.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Inspection](Inno.Editor.Inspection.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Plugins.Authoring](../plugins/Inno.Plugins.Authoring.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Plugins.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scripting` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Inspection` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.GameView.md b/docs/editor/Inno.Editor.Panel.GameView.md index 7a052a49e..2c473c26d 100644 --- a/docs/editor/Inno.Editor.Panel.GameView.md +++ b/docs/editor/Inno.Editor.Panel.GameView.md @@ -24,23 +24,24 @@ Editor 的通用 `RenderRuntime` 不注入任何隐式 Scene content。Scene Vie ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Rendering](Inno.Editor.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.PlayMode](Inno.Editor.PlayMode.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.References](../references/Inno.References.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Runtime](../rendering/Inno.Rendering.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.References` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Hierarchy.md b/docs/editor/Inno.Editor.Panel.Hierarchy.md index 6d49f86ea..82f0d4e53 100644 --- a/docs/editor/Inno.Editor.Panel.Hierarchy.md +++ b/docs/editor/Inno.Editor.Panel.Hierarchy.md @@ -82,48 +82,40 @@ EditorScripts 使用 `InnoEditor.Hierarchy` 获取公开 drop target;Attribute ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget.HierarchyObjectDropTarget(Inno.Scene.GameObject gameObject)`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L22) | Creates a hierarchy drop target representing one live game object row.
`gameObject`:The game object represented by the target row. | — | `System.ArgumentNullException`:Thrown when gameObject is null. | -| [`public Inno.Scene.GameObject Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget.gameObject { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L30) | Gets the target game object. | — | — | -| [`public sealed class Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L11) | Identifies a game object hierarchy drop target. | — | — | - -### `Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget.HierarchySceneDropTarget(Inno.Scene.GameScene scene)`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchySceneDropTarget.cs#L22) | Creates a hierarchy drop target representing one loaded scene row or root.
`scene`:The loaded scene represented by the target. | — | `System.ArgumentNullException`:Thrown when scene is null. | -| [`public Inno.Scene.GameScene Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget.scene { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchySceneDropTarget.cs#L30) | Gets the target scene. | — | — | +| [`public sealed class Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L11) | Identifies a game object hierarchy drop target. | — | — | +| [`public Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget.HierarchyObjectDropTarget(Inno.Scene.GameObject gameObject)`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L22) | Creates a hierarchy drop target representing one live game object row.
`gameObject`: The game object represented by the target row. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Scene.GameObject Inno.Editor.Panel.Hierarchy.HierarchyObjectDropTarget.gameObject { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchyObjectDropTarget.cs#L30) | Gets the target game object. | — | — | | [`public sealed class Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchySceneDropTarget.cs#L11) | Identifies a scene row or scene root hierarchy drop target. | — | — | +| [`public Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget.HierarchySceneDropTarget(Inno.Scene.GameScene scene)`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchySceneDropTarget.cs#L22) | Creates a hierarchy drop target representing one loaded scene row or root.
`scene`: The loaded scene represented by the target. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public Inno.Scene.GameScene Inno.Editor.Panel.Hierarchy.HierarchySceneDropTarget.scene { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/HierarchySceneDropTarget.cs#L30) | Gets the target scene. | — | — | ## 项目依赖 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Panel.FileBrowser](Inno.Editor.Panel.FileBrowser.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene.Assets](../scene/Inno.Scene.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene](../scene/Inno.Scene.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Scene` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Inspector.md b/docs/editor/Inno.Editor.Panel.Inspector.md index d0c047853..a9f834f3d 100644 --- a/docs/editor/Inno.Editor.Panel.Inspector.md +++ b/docs/editor/Inno.Editor.Panel.Inspector.md @@ -18,7 +18,7 @@ Play Mode 中 Project scene 的运行副本保持可编辑;Inspector 的提示 Target Header 右上角提供 lock/unlock 控件,其交互面积、图标居中与 hover 表现和 Panel Tab Bar 的关闭 X 使用同一套 compact icon widget。锁定只固定 Inspector 当前展示目标,不修改全局 Selection;Hierarchy 和 File Browser 可以继续选择其他对象,以便把它们拖到被锁定目标的属性上。Scene identity 只以 persistent ID 保留并从当前 Edit/Play Session 重新解析,因此 assembly reload 后会指向 replacement 或 Missing placeholder;没有稳定 identity 的 collectible target 只保留弱引用。锁定目标被销毁、移除或无法重解析时自动解锁,不会固定退休 Plugin/Script ALC。 -Asset target Drawer 由 FileBrowser 项目自身提供,并通过 `IInspectionIconProvider` 复用 `AssetEditorModule` 的 type/extension icon registry;因此 File Browser Tree/List/Grid 与 Inspector Header 始终一致,EditorScripts 热重载图标声明后两处会同时更新。第二行 source path 使用与 File Browser 底部 breadcrumb 相同的半透明 palette color。Plugin Source Mount 根使用 `IPlugin` 类型,不伪装成普通 Directory,也不创建 `.iplugin` companion asset。 +Asset target Drawer 由 FileBrowser 项目自身提供,并通过 `IInspectionIconProvider` 复用 `AssetEditingModule` 的 type/extension icon registry;因此 File Browser Tree/List/Grid 与 Inspector Header 始终一致,EditorScripts 热重载图标声明后两处会同时更新。第二行 source path 使用与 File Browser 底部 breadcrumb 相同的半透明 palette color。Plugin Source Mount 根使用 `IPlugin` 类型,不伪装成普通 Directory,也不创建 `.iplugin` companion asset。 GameObject Header 的第二行包含 Active、项目 Tag picker 和 Layer picker。`SceneProjectSettingsModule` 按 `ProjectSettingsStore.revision` 刷新隔离的 `GameTagCatalog` 与 `GameLayerCatalog` 快照;Inspector 不从 `editor.ini` 或 `Settings.Editor.inno` 建立第二份 catalog。对象修改通过 `SceneEdits` 进入 Scene History。 @@ -87,57 +87,52 @@ EditorScripts 使用 `InnoEditor.Inspection`,可声明 InspectionDrawer、Prop ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Panel.Inspector.AssetReferenceDropTarget` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.AssetReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L28) | Creates a drop target that validates an asset type and assigns its persistent identity to a property.
`expectedType`:The runtime asset type accepted by the property.
`assign`:The callback that writes an accepted persistent identity to the property. | — | `System.ArgumentNullException`:Thrown when either argument is null.
`System.InvalidOperationException`:Thrown when the type does not belong to the active type catalog. | -| [`public void Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.Assign(System.Guid persistentId)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L48) | Assigns a persistent asset identity to the represented property.
`persistentId`:The stable persistent identity used for lookup. | — | — | -| [`public System.Type Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L40) | Gets the required asset type. | — | — | -| [`public sealed class Inno.Editor.Panel.Inspector.AssetReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L8) | Provides an assignable asset-reference property drop target. | — | — | - -### `Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.EngineObjectReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L31) | Creates a drop target that validates and assigns an engine object to a serialized property.
`expectedType`:The engine object type accepted by the property.
`assign`:The callback that writes an accepted object to the property. | — | `System.ArgumentNullException`:Thrown when either argument is null.
`System.InvalidOperationException`:Thrown when the type does not belong to the active type catalog. | -| [`public void Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.Assign(Inno.Scene.EngineObject value)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L51) | Assigns an accepted engine object to the represented property.
`value`:The concrete value read or transformed by this operation. | — | — | -| [`public System.Type Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L43) | Gets the required engine object type. | — | — | +| [`public sealed class Inno.Editor.Panel.Inspector.AssetReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L8) | Provides an assignable asset-reference property drop target. | — | — | +| [`public Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.AssetReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L28) | Creates a drop target that validates an asset type and assigns its persistent identity to a property.
`expectedType`: The runtime asset type accepted by the property.
`assign`: The callback that writes an accepted persistent identity to the property. | — | `System.ArgumentNullException`: Thrown when either argument is .
`System.InvalidOperationException`: Thrown when the type does not belong to the active type catalog. | +| [`public System.Type Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L40) | Gets the required asset type. | — | — | +| [`public void Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.Assign(System.Guid persistentId)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L48) | Assigns a persistent asset identity to the represented property.
`persistentId`: The stable persistent identity used for lookup. | — | — | | [`public sealed class Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L11) | Provides an assignable engine-object-reference property drop target. | — | — | +| [`public Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.EngineObjectReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L31) | Creates a drop target that validates and assigns an engine object to a serialized property.
`expectedType`: The engine object type accepted by the property.
`assign`: The callback that writes an accepted object to the property. | — | `System.ArgumentNullException`: Thrown when either argument is .
`System.InvalidOperationException`: Thrown when the type does not belong to the active type catalog. | +| [`public System.Type Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L43) | Gets the required engine object type. | — | — | +| [`public void Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.Assign(Inno.Scene.EngineObject value)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L51) | Assigns an accepted engine object to the represented property.
`value`: The concrete value read or transformed by this operation. | — | — | ## 项目依赖 -- [Inno.Editor.Inspection](Inno.Editor.Inspection.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.PlayMode](Inno.Editor.PlayMode.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene.Assets](../scene/Inno.Scene.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene](../scene/Inno.Scene.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Shaders.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Inspection` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Scene` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Logging.md b/docs/editor/Inno.Editor.Panel.Logging.md index bfad0a844..408ec767b 100644 --- a/docs/editor/Inno.Editor.Panel.Logging.md +++ b/docs/editor/Inno.Editor.Panel.Logging.md @@ -38,21 +38,21 @@ Console card header 显示等级和来源,例如 `[Info] [Log]` 或 `[Error] [ ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Diagnostics](Inno.Editor.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.PlayMode](Inno.Editor.PlayMode.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.SceneView.md b/docs/editor/Inno.Editor.Panel.SceneView.md index 2fc406069..d9162695a 100644 --- a/docs/editor/Inno.Editor.Panel.SceneView.md +++ b/docs/editor/Inno.Editor.Panel.SceneView.md @@ -37,25 +37,25 @@ Contributor 在 `EditorViewportManipulationSpace.plane` 声明平面时,Scene ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGuizmo](../backends/ImGui/Inno.Native.ImGuizmo.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Rendering](Inno.Editor.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Mathematics](../core/Inno.Core.Mathematics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene](../scene/Inno.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.References](../references/Inno.References.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Core.Mathematics` | 实现依赖;不传递 compile surface | +| `Inno.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.References` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Settings.md b/docs/editor/Inno.Editor.Panel.Settings.md index d435701dc..834b11b02 100644 --- a/docs/editor/Inno.Editor.Panel.Settings.md +++ b/docs/editor/Inno.Editor.Panel.Settings.md @@ -110,20 +110,20 @@ Settings frontend 不内建 Boolean、Layer、Tag、PBR 或其他业务字段类 ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Build](../build/Inno.Build.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Managed](../build/Inno.Build.Managed.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Build` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Build.Managed` | 实现依赖;不传递 compile surface | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.ShaderEditor.md b/docs/editor/Inno.Editor.Panel.ShaderEditor.md index a59f3db1b..9a1bac244 100644 --- a/docs/editor/Inno.Editor.Panel.ShaderEditor.md +++ b/docs/editor/Inno.Editor.Panel.ShaderEditor.md @@ -89,32 +89,36 @@ Stage Input 的 `Source = Builtin` 表示该值由 GPU 阶段或引擎/Adapter ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Editor.Inspection](Inno.Editor.Inspection.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Shaders](Inno.Editor.Shaders.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Rendering](Inno.Editor.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Panel.FileBrowser](Inno.Editor.Panel.FileBrowser.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Input](../core/Inno.Core.Input.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Shaders](../rendering/Inno.Rendering.Shaders.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets](../rendering/Inno.Rendering.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Graph](Inno.Editor.Graph.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Graphs](../core/Inno.Core.Graphs.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets.Authoring](../rendering/Inno.Rendering.Assets.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.Graph.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Shaders.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Inspection` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Core.Input` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Graph` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Core.Graphs` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Stats.md b/docs/editor/Inno.Editor.Panel.Stats.md index 347e01753..5114712b1 100644 --- a/docs/editor/Inno.Editor.Panel.Stats.md +++ b/docs/editor/Inno.Editor.Panel.Stats.md @@ -38,15 +38,15 @@ static void PublishPending( ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.PlayMode.md b/docs/editor/Inno.Editor.PlayMode.md index 1139189c6..067eb34dd 100644 --- a/docs/editor/Inno.Editor.PlayMode.md +++ b/docs/editor/Inno.Editor.PlayMode.md @@ -58,7 +58,7 @@ flowchart LR | `isPlaying` | 仅在 `Playing` 时为 `true`。 | | `lastFailure` | 最近一次编译门禁、切换或模拟失败;新 entry 请求会清空。 | | `activeSessionId` | 当前 Play 日志 Session;不在 Play 请求内时为 `none`。 | -| `stateChanged` | 状态实际变化后触发;单个订阅者异常被隔离。 | +| `ListenStateChanges(callback)` | 通过 Core Events 订阅状态变化;返回值由订阅者释放。通知全部观察者后聚合异常。 | | `EnterPlayMode()` | 从 `Editing` 或 `Failed` 接受 fresh 请求;返回值表示是否接受。 | | `ExitPlayMode()` | 取消 Compiling、停止 Preparing/Playing,或把 `Failed` 清回 `Editing`;其他状态返回 `false`。 | @@ -142,57 +142,15 @@ Command/Ctrl + `P` 与 icon 使用同一个 Action 和状态查询,因此不 - 编译门禁:[Inno.Editor.Scripting](Inno.Editor.Scripting.md) - 下一页:[Inno.Editor.Hosting](Inno.Editor.Hosting.md) -## 当前源码公开 API 清单 - -签名来自当前 SDK 求值与语义符号;按类型列出 public/protected 契约。工作流与生命周期见本页前文。 - -### `Inno.Editor.PlayMode.EditorPlayModeController` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Action? Inno.Editor.PlayMode.EditorPlayModeController.stateChanged`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L144) | Occurs after the controller commits a state transition. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.PlayMode.EditorPlayModeController.EditorPlayModeController(Inno.Runtime.EngineHost engineHost, Inno.Runtime.RuntimeSessionOptions runtimeOptions, Inno.Editor.Scene.IEditorScenePlayMode scenes, Inno.Editor.Scripting.IEditorScriptCompilation scripting, Inno.Editor.Interactions.IEditorHistoryIsolation history, Inno.Core.Logging.LogRouter logs, Inno.Editor.Audio.IEditorAudioHost? audio = null)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L84) | Creates a Play Mode controller around the runtime host, scene snapshot, scripting, and history boundaries.
`engineHost`:The application host that owns every isolated Play Mode runtime session.
`runtimeOptions`:The validated Play Mode storage and timing policy used for every accepted request.
`scenes`:The scene workspace that creates isolated runtime scene sessions.
`scripting`:The script compilation service that publishes generation readiness.
`history`:The editor history boundary isolated during simulation.
`logs`:The application log router that receives Play Mode lifecycle failures.
`audio`:Optional Editor audio host that creates an isolated backend generation for Play Mode. | 约束由所属类型与本页生命周期定义。 | `System.ArgumentNullException`:Thrown when any dependency is null. | -| [`public void Inno.Editor.PlayMode.EditorPlayModeController.AdvanceTransition()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L206) | Advances at most one Play Mode transition at an editor-controlled frame-safe point. | 约束由所属类型与本页生命周期定义。 | `System.ObjectDisposedException`:Thrown after the controller has been disposed. | -| [`public void Inno.Editor.PlayMode.EditorPlayModeController.Dispose()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L242) | Releases the isolated runtime session and history scope owned by this controller. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.EnterPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L155) | Requests a fresh script generation before preparing an isolated runtime session.
返回:true when the controller accepted a new request; otherwise, false. | 约束由所属类型与本页生命周期定义。 | `System.ObjectDisposedException`:Thrown after the controller has been disposed. | -| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.ExitPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L179) | Requests cancellation, runtime-session disposal, or dismissal of a failed transition.
返回:true when the current state accepted the request; otherwise, false. | 约束由所属类型与本页生命周期定义。 | `System.ObjectDisposedException`:Thrown after the controller has been disposed. | -| [`public void Inno.Editor.PlayMode.EditorPlayModeController.Tick(float deltaTime)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L230) | Advances the isolated runtime session by one complete frame.
`deltaTime`:The elapsed editor frame time in seconds. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Core.Logging.LogSessionId Inno.Editor.PlayMode.EditorPlayModeController.activeSessionId { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L139) | Gets the isolated runtime log session associated with the current request. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.isPlaying { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L117) | Gets whether isolated runtime scenes are actively simulating. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public string? Inno.Editor.PlayMode.EditorPlayModeController.lastFailure { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L134) | Gets the most recent transition or simulation failure. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public Inno.Editor.PlayMode.EditorPlayModeState Inno.Editor.PlayMode.EditorPlayModeController.state { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L112) | Gets the current Play Mode transition state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.PlayMode.EditorPlayModeController : Inno.Editor.PlayMode.IEditorPlayMode, Inno.Editor.Core.IEditorReloadGuard, System.IDisposable`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L28) | Implements the deterministic state machine for one editor Play Mode workflow. | The editor host advances transitions at frame-safe points. Editable scenes remain owned by the scene workspace while the controller owns only the isolated runtime session. | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.PlayMode.EditorPlayModeLoop` +## 状态观察生命周期 - - - - - +IEditorPlayMode.ListenStateChanges 返回 Core Events 的独立订阅 lease;Console 在 Start 注册并在 Dispose 释放。Controller 的私有 Core EventHub 在成功退休后释放,pending retirement 保留 owner。观察者异常仍在所有观察者得到通知后聚合报告,不吞掉错误。没有第二个 UI 监听器注册表或事件总线。 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.PlayMode.EditorPlayModeLoop.EditorPlayModeLoop()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L8) | C# 生成的声明;状态与值语义遵循所属类型契约。 | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public System.IDisposable Inno.Editor.PlayMode.EditorPlayModeLoop.EnterPresentationScope()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L29) | Binds editor presentation and interaction work to the isolated Play session while simulation is active, or returns an inert scope while the editor is not presenting runtime copies.
返回:A scope that must be disposed after the current editor presentation phase completes. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public void Inno.Editor.PlayMode.EditorPlayModeLoop.Quiesce()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L37) | Drains the active Play session before the product tears down editor modules, history or presentation. | 约束由所属类型与本页生命周期定义。 | `System.InvalidOperationException`:The session cannot retire within its deadline; the host is faulted and its dependencies must remain alive. | -| [`public void Inno.Editor.PlayMode.EditorPlayModeLoop.Tick(float deltaTime)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L20) | Advances the active simulation by one complete frame when Play Mode is running.
`deltaTime`:The elapsed frame time in seconds. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public sealed class Inno.Editor.PlayMode.EditorPlayModeLoop`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L8) | Bridges the editor host frame clock to the active Play Mode session. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | +## 当前源码公开 API 清单 -### `Inno.Editor.PlayMode.EditorPlayModeState` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -201,55 +159,86 @@ Command/Ctrl + `P` 与 icon 使用同一个 Action 和状态查询,因此不 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L16) | Play Mode is waiting for the requested script generation to become active. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Editing = 0`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L11) | Editable scene documents are loaded and game simulation is stopped. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Failed = 5`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L36) | The most recent transition failed and its diagnostic remains available for inspection. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Playing = 3`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L26) | Isolated runtime scenes are receiving the game update lifecycle. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Preparing = 2`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L21) | The active script generation is ready and isolated runtime state is being prepared. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState.Stopping = 4`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L31) | The isolated runtime session is stopping and releasing its owned state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public enum Inno.Editor.PlayMode.EditorPlayModeState : int`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L6) | Identifies the current relationship between editor documents and game simulation. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | - -### `Inno.Editor.PlayMode.IEditorPlayMode` - - + + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`System.Action? Inno.Editor.PlayMode.IEditorPlayMode.stateChanged`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L35) | Occurs after P:Inno.Editor.PlayMode.IEditorPlayMode.state changes. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`bool Inno.Editor.PlayMode.IEditorPlayMode.EnterPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L43) | Requests entry after the active script generation becomes ready.
返回:true when a new entry request was accepted. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`bool Inno.Editor.PlayMode.IEditorPlayMode.ExitPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L51) | Requests disposal of the active runtime session or dismisses a failed transition.
返回:true when entry was cancelled or a new exit request was accepted. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Core.Logging.LogSessionId Inno.Editor.PlayMode.IEditorPlayMode.activeSessionId { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L30) | Gets the active isolated runtime log session, or P:Inno.Core.Logging.LogSessionId.none outside a Play Mode request. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`bool Inno.Editor.PlayMode.IEditorPlayMode.isPlaying { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L20) | Gets whether isolated runtime scenes are actively simulating. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`string? Inno.Editor.PlayMode.IEditorPlayMode.lastFailure { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L25) | Gets the most recent transition or simulation failure, or null when no failure is active. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`Inno.Editor.PlayMode.EditorPlayModeState Inno.Editor.PlayMode.IEditorPlayMode.state { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L15) | Gets the current Play Mode transition state. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | -| [`public interface Inno.Editor.PlayMode.IEditorPlayMode`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L10) | Controls the editor's isolated game-simulation session. | 约束由所属类型与本页生命周期定义。 | 具体失败按方法与类型契约处理。 | +| [`public enum Inno.Editor.PlayMode.EditorPlayModeState : int`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L6) | Identifies the current relationship between editor documents and game simulation. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Editing = 0`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L11) | Editable scene documents are loaded and game simulation is stopped. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L16) | Play Mode is waiting for the requested script generation to become active. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Preparing = 2`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L21) | The active script generation is ready and isolated runtime state is being prepared. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Playing = 3`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L26) | Isolated runtime scenes are receiving the game update lifecycle. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Stopping = 4`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L31) | The isolated runtime session is stopping and releasing its owned state. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState.Failed = 5`](../../src/composition/editor/features/Inno.Editor.PlayMode/EditorPlayModeState.cs#L36) | The most recent transition failed and its diagnostic remains available for inspection. | — | — | +| [`public interface Inno.Editor.PlayMode.IEditorPlayMode`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L10) | Controls the editor's isolated game-simulation session. | — | — | +| [`Inno.Editor.PlayMode.EditorPlayModeState Inno.Editor.PlayMode.IEditorPlayMode.state { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L15) | Gets the current Play Mode transition state. | — | — | +| [`bool Inno.Editor.PlayMode.IEditorPlayMode.isPlaying { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L20) | Gets whether isolated runtime scenes are actively simulating. | — | — | +| [`string? Inno.Editor.PlayMode.IEditorPlayMode.lastFailure { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L25) | Gets the most recent transition or simulation failure, or when no failure is active. | — | — | +| [`Inno.Core.Logging.LogSessionId Inno.Editor.PlayMode.IEditorPlayMode.activeSessionId { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L30) | Gets the active isolated runtime log session, or outside a Play Mode request. | — | — | +| [`System.IDisposable Inno.Editor.PlayMode.IEditorPlayMode.ListenStateChanges(System.Action listener)`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L44) | Subscribes to committed state transitions through the Core event lifetime.
`listener`: The observer invoked synchronously after a transition. | An independently disposable subscription; dispose it before retiring the observer. | `System.ArgumentNullException`: The listener is null. | +| [`bool Inno.Editor.PlayMode.IEditorPlayMode.EnterPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L52) | Requests entry after the active script generation becomes ready. | when a new entry request was accepted. | — | +| [`bool Inno.Editor.PlayMode.IEditorPlayMode.ExitPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs#L60) | Requests disposal of the active runtime session or dismisses a failed transition. | when entry was cancelled or a new exit request was accepted. | — | +| [`public sealed class Inno.Editor.PlayMode.EditorPlayModeController : Inno.Editor.PlayMode.IEditorPlayMode, Inno.Editor.Core.IEditorReloadGuard, System.IDisposable`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L29) | Implements the deterministic state machine for one editor Play Mode workflow. | — | — | +| [`public Inno.Editor.PlayMode.EditorPlayModeController.EditorPlayModeController(Inno.Runtime.EngineHost engineHost, Inno.Runtime.RuntimeSessionOptions runtimeOptions, Inno.Editor.Scene.IEditorScenePlayMode scenes, Inno.Editor.Scripting.IEditorScriptCompilation scripting, Inno.Editor.Interactions.IEditorHistoryIsolation history, Inno.Core.Logging.LogRouter logs, Inno.Editor.Audio.IEditorAudioHost? audio = null)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L87) | Creates a Play Mode controller around the runtime host, scene snapshot, scripting, and history boundaries.
`engineHost`: The application host that owns every isolated Play Mode runtime session.
`runtimeOptions`: The validated Play Mode storage and timing policy used for every accepted request.
`scenes`: The scene workspace that creates isolated runtime scene sessions.
`scripting`: The script compilation service that publishes generation readiness.
`history`: The editor history boundary isolated during simulation.
`logs`: The application log router that receives Play Mode lifecycle failures.
`audio`: Optional Editor audio host that creates an isolated backend generation for Play Mode. | — | `System.ArgumentNullException`: Thrown when any dependency is . | +| [`public Inno.Editor.PlayMode.EditorPlayModeState Inno.Editor.PlayMode.EditorPlayModeController.state { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L116) | Gets the current Play Mode transition state. | — | — | +| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.isPlaying { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L121) | Gets whether isolated runtime scenes are actively simulating. | — | — | +| [`public string? Inno.Editor.PlayMode.EditorPlayModeController.lastFailure { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L138) | Gets the most recent transition or simulation failure. | — | — | +| [`public Inno.Core.Logging.LogSessionId Inno.Editor.PlayMode.EditorPlayModeController.activeSessionId { get; }`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L143) | Gets the isolated runtime log session associated with the current request. | — | — | +| [`public System.IDisposable Inno.Editor.PlayMode.EditorPlayModeController.ListenStateChanges(System.Action listener)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L146) | Subscribes to committed state transitions through the Core event lifetime.
`listener`: The observer invoked synchronously after a transition. | An independently disposable subscription; dispose it before retiring the observer. | `System.ArgumentNullException`: The listener is null. | +| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.EnterPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L172) | Requests a fresh script generation before preparing an isolated runtime session. | when the controller accepted a new request; otherwise, . | `System.ObjectDisposedException`: Thrown after the controller has been disposed. | +| [`public bool Inno.Editor.PlayMode.EditorPlayModeController.ExitPlayMode()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L196) | Requests cancellation, runtime-session disposal, or dismissal of a failed transition. | when the current state accepted the request; otherwise, . | `System.ObjectDisposedException`: Thrown after the controller has been disposed. | +| [`public void Inno.Editor.PlayMode.EditorPlayModeController.AdvanceTransition()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L223) | Advances at most one Play Mode transition at an editor-controlled frame-safe point. | — | `System.ObjectDisposedException`: Thrown after the controller has been disposed. | +| [`public void Inno.Editor.PlayMode.EditorPlayModeController.Tick(float deltaTime)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L247) | Advances the isolated runtime session by one complete frame.
`deltaTime`: The elapsed editor frame time in seconds. | — | — | +| [`public void Inno.Editor.PlayMode.EditorPlayModeController.Dispose()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs#L259) | Releases the isolated runtime session and history scope owned by this controller. | — | — | +| [`public sealed class Inno.Editor.PlayMode.EditorPlayModeLoop`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L8) | Bridges the editor host frame clock to the active Play Mode session. | — | — | +| [`public void Inno.Editor.PlayMode.EditorPlayModeLoop.Tick(float deltaTime)`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L20) | Advances the active simulation by one complete frame when Play Mode is running.
`deltaTime`: The elapsed frame time in seconds. | — | — | +| [`public System.IDisposable Inno.Editor.PlayMode.EditorPlayModeLoop.EnterPresentationScope()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L29) | Binds editor presentation and interaction work to the isolated Play session while simulation is active, or returns an inert scope while the editor is not presenting runtime copies. | A scope that must be disposed after the current editor presentation phase completes. | — | +| [`public void Inno.Editor.PlayMode.EditorPlayModeLoop.Quiesce()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L37) | Drains the active Play session before the product tears down editor modules, history or presentation. | — | `System.InvalidOperationException`: The session cannot retire within its deadline; the host is faulted and its dependencies must remain alive. | +| [`public Inno.Editor.PlayMode.EditorPlayModeLoop.EditorPlayModeLoop()`](../../src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeLoop.cs#L8) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.UI.Runtime](../ui/Inno.UI.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Compiler](../scripting/Inno.Scripting.Compiler.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Input](../core/Inno.Core.Input.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene](../scene/Inno.Scene.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Audio](Inno.Editor.Audio.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Scene](Inno.Editor.Scene.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Scripting](Inno.Editor.Scripting.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Events` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.UI.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 实现依赖;不传递 compile surface | +| `Inno.Core.Input` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Audio` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Runtime` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.Editor.Scene` | 公开签名依赖 | +| `Inno.Editor.Scripting` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Rendering.md b/docs/editor/Inno.Editor.Rendering.md index 436351a2d..34c8d2f32 100644 --- a/docs/editor/Inno.Editor.Rendering.md +++ b/docs/editor/Inno.Editor.Rendering.md @@ -23,8 +23,8 @@ 公开 API(同时导出到 `InnoEditor.Rendering`): - `Open(path)` / `Read(id)`:打开源并解码独立 Pipeline 草稿。 -- `Replace(id, candidate, finishGesture)` / `Commit(id)`:一手势一次共享 History,不保存、不改变 Scene/Game。 -- `ReplaceSettings(id, settings, finishGesture)`:自动捕获设置的完整依赖,写入草稿 pipelineState。 +- `Replace(id, candidate, mergeKey)`:每次修改立即进入共享 History;连续手势通过同一事务和 merge key 合并,不保存、不改变 Scene/Game。 +- `ReplaceSettings(id, settings, mergeKey)`:自动捕获设置的完整依赖,写入草稿 pipelineState。 Save/Revert 使用共享文档服务,保存与导入/激活状态分开呈现;源冲突拒绝覆盖。 当前 Inspector 支持既有设置类型的字段、Feature 开关和顺序。Pipeline 类型及新增 Feature 由配置创建 API/插件模板提供。 @@ -106,46 +106,82 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Rendering.EditorGizmoContext` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorGizmoContext.EditorGizmoContext(Inno.References.ContentReadScope content, Inno.Core.Identity.RuntimeIdentity? selected, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L29) | Creates a frame-scoped gizmo request.
`content`:The scenes presented by the Scene viewport.
`selected`:The selected live object, when available.
`pixelWidth`:The Scene viewport width.
`pixelHeight`:The Scene viewport height. | — | — | -| [`public Inno.References.ContentReadScope Inno.Editor.Rendering.EditorGizmoContext.content { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L44) | Gets the scene roots visible in this Editor viewport. | — | — | -| [`public int Inno.Editor.Rendering.EditorGizmoContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L56) | Gets the viewport height in physical pixels. | — | — | -| [`public int Inno.Editor.Rendering.EditorGizmoContext.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L52) | Gets the viewport width in physical pixels. | — | — | -| [`public Inno.Core.Identity.RuntimeIdentity? Inno.Editor.Rendering.EditorGizmoContext.selected { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L48) | Gets the transient selected identity. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorGizmoContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L12) | Provides scene content and selection for transient Editor gizmos. | — | — | - -### `Inno.Editor.Rendering.EditorGizmoFrame` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorGizmoFrame.EditorGizmoFrame()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L176) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Editor.Rendering.EditorGizmoFrame.Icon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L202) | Appends a selectable scene icon to the current gizmo frame.
`owner`:The object that owns the resulting lifetime and state.
`position`:The position consumed by icon; ownership remains with the caller unless explicitly stated otherwise.
`iconId`:The stable icon identity resolved by the Scene View. | — | — | -| [`public void Inno.Editor.Rendering.EditorGizmoFrame.Line(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L220) | Appends a world space line to the current gizmo frame.
`start`:The start consumed by line; ownership remains with the caller unless explicitly stated otherwise.
`end`:The end consumed by line; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorGizmoFrame.icons { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L184) | Gets the ordered selectable icons. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorGizmoFrame.lines { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L188) | Gets the ordered non-interactive lines. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorGizmoFrame : Inno.Editor.Rendering.IEditorGizmoSink`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L176) | Holds the transient gizmo primitives for one Scene viewport frame. | — | — | - -### `Inno.Editor.Rendering.EditorGizmoIcon` - + + + + + + + + + @@ -158,24 +194,6 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorGizmoIcon.EditorGizmoIcon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | One selectable Editor icon.
`owner`:The object that owns the resulting lifetime and state.
`position`:The vector3 value used to initialize this instance.
`iconId`:The stable visual identity resolved by the Scene View presentation. | — | — | -| [`public void Inno.Editor.Rendering.EditorGizmoIcon.Deconstruct(out Inno.Core.Identity.Identity owner, out Inno.Core.Mathematics.Vector3 position, out string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Rendering.EditorGizmoIcon.Equals(Inno.Editor.Rendering.EditorGizmoIcon other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorGizmoIcon.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorGizmoIcon.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorGizmoIcon.operator ==(Inno.Editor.Rendering.EditorGizmoIcon left, Inno.Editor.Rendering.EditorGizmoIcon right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorGizmoIcon.operator !=(Inno.Editor.Rendering.EditorGizmoIcon left, Inno.Editor.Rendering.EditorGizmoIcon right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorGizmoIcon.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public string Inno.Editor.Rendering.EditorGizmoIcon.iconId { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L156) | The stable visual identity resolved by the Scene View presentation. | — | — | -| [`public Inno.Core.Identity.Identity Inno.Editor.Rendering.EditorGizmoIcon.owner { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L154) | The object that owns the resulting lifetime and state. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoIcon.position { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L155) | The vector3 value used to initialize this instance. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorGizmoIcon : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | One selectable Editor icon.
`owner`:The object that owns the resulting lifetime and state.
`position`:The vector3 value used to initialize this instance.
`iconId`:The stable visual identity resolved by the Scene View presentation. | — | — | - -### `Inno.Editor.Rendering.EditorGizmoLine` - @@ -187,47 +205,12 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorGizmoLine.EditorGizmoLine(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | One non-interactive Editor line.
`start`:The vector3 value used to initialize this instance.
`end`:The vector3 value used to initialize this instance. | — | — | -| [`public void Inno.Editor.Rendering.EditorGizmoLine.Deconstruct(out Inno.Core.Mathematics.Vector3 start, out Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Rendering.EditorGizmoLine.Equals(Inno.Editor.Rendering.EditorGizmoLine other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorGizmoLine.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorGizmoLine.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorGizmoLine.operator ==(Inno.Editor.Rendering.EditorGizmoLine left, Inno.Editor.Rendering.EditorGizmoLine right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorGizmoLine.operator !=(Inno.Editor.Rendering.EditorGizmoLine left, Inno.Editor.Rendering.EditorGizmoLine right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorGizmoLine.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoLine.end { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L170) | The vector3 value used to initialize this instance. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoLine.start { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L169) | The vector3 value used to initialize this instance. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorGizmoLine : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | One non-interactive Editor line.
`start`:The vector3 value used to initialize this instance.
`end`:The vector3 value used to initialize this instance. | — | — | - -### `Inno.Editor.Rendering.EditorGizmoProvider` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Rendering.EditorGizmoProvider.EditorGizmoProvider()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L100) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract void Inno.Editor.Rendering.EditorGizmoProvider.Collect(Inno.Editor.Rendering.EditorGizmoContext context, Inno.Editor.Rendering.IEditorGizmoSink sink)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L111) | Submits transient icons and lines for the presented scenes.
`context`:The current Scene viewport request.
`sink`:The collector that owns submitted primitives for this frame. | — | — | -| [`public abstract class Inno.Editor.Rendering.EditorGizmoProvider`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L100) | Contributes Editor-only icons and bounds without modifying a rendering model. | — | — | - -### `Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute.EditorGizmoProviderExtensionAttribute(string id)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L129) | Creates a provider declaration with a stable identifier.
`id`:The globally unique provider identifier. | — | — | -| [`public string Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L138) | Gets the stable provider identifier. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L121) | Marks one reloadable Editor gizmo provider. | — | — | - -### `Inno.Editor.Rendering.EditorPreviewHandle` - + + + + + + @@ -241,25 +224,15 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorPreviewHandle.EditorPreviewHandle(ulong value, uint deviceGeneration, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L28) | Creates one generation-scoped preview handle.
`value`:Non-zero preview identity.
`deviceGeneration`:Non-zero rendering-device generation.
`pixelWidth`:Positive source width.
`pixelHeight`:Positive source height. | — | — | -| [`public bool Inno.Editor.Rendering.EditorPreviewHandle.Equals(Inno.Editor.Rendering.EditorPreviewHandle other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorPreviewHandle.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorPreviewHandle.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorPreviewHandle.operator ==(Inno.Editor.Rendering.EditorPreviewHandle left, Inno.Editor.Rendering.EditorPreviewHandle right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorPreviewHandle.operator !=(Inno.Editor.Rendering.EditorPreviewHandle left, Inno.Editor.Rendering.EditorPreviewHandle right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorPreviewHandle.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public uint Inno.Editor.Rendering.EditorPreviewHandle.deviceGeneration { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L52) | Gets the rendering-device generation that owns this handle. | — | — | -| [`public bool Inno.Editor.Rendering.EditorPreviewHandle.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L67) | Gets whether this handle identifies a usable generation-scoped preview. | — | — | -| [`public int Inno.Editor.Rendering.EditorPreviewHandle.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L62) | Gets source height in pixels. | — | — | -| [`public int Inno.Editor.Rendering.EditorPreviewHandle.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L57) | Gets source width in pixels. | — | — | -| [`public ulong Inno.Editor.Rendering.EditorPreviewHandle.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L47) | Gets the opaque preview identity. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorPreviewHandle : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Identifies one preview texture owned by a specific rendering-device generation. | — | — | - -### `Inno.Editor.Rendering.EditorRenderingModule` - + + + + + + + + + @@ -281,199 +254,22 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorRenderingModule.EditorRenderingModule(Inno.Editor.Rendering.IEditorRenderingHost host, Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L56) | Creates the composition host around stable rendering and interaction services.
`host`:Host-owned target and opaque texture bridge.
`interactions`:Shared Editor interaction and selection service.
`types`:The type catalog that owns viewport contributor generations. | — | — | -| [`public Inno.Editor.Rendering.EditorGizmoFrame Inno.Editor.Rendering.EditorRenderingModule.CollectGizmos(string viewportId, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L84) | Collects Editor-only icons and bounds for the current Scene viewport.
返回:Transient gizmo primitives for the submitted frame.
`viewportId`:The Scene viewport identity.
`pixelWidth`:The viewport width.
`pixelHeight`:The viewport height. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.Draw(Inno.Editor.Rendering.EditorViewportOutput output, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L558) | Draws a ready output in the current panel.
`output`:Opaque output returned by Inno.Editor.Rendering.EditorRenderingModule.TrySubmit(Inno.Editor.Rendering.EditorViewportKindId,System.String,System.Int32,System.Int32,Inno.Editor.Rendering.EditorViewportOutput@).
`logicalSize`:Destination size in logical UI pixels. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.DrawControllerToolbar(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L319) | Draws toolbar controls owned by the selected model controller.
`kind`:Open viewport purpose.
`viewportId`:Stable panel viewport identity.
`pixelWidth`:Current target width.
`pixelHeight`:Current target height. | — | — | -| [`public string? Inno.Editor.Rendering.EditorRenderingModule.GetCompositionError(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L158) | Gets the most recent isolated contribution or composition failure for one stable viewport.
返回:The failure message, or null when no current failure exists.
`viewportId`:Stable panel viewport identity. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationState Inno.Editor.Rendering.EditorRenderingModule.GetNavigationState(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L174) | Gets the host-owned neutral navigation state for one stable Editor viewport.
返回:The reusable navigation state owned by the Editor host.
`viewportId`:Stable panel viewport identity. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.HandlePointer(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, float x, float y, int button)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L518) | Forwards a normalized click to the selected model controller.
`kind`:Open viewport purpose.
`viewportId`:Stable panel viewport identity.
`pixelWidth`:Current target width.
`pixelHeight`:Current target height.
`x`:Normalized horizontal position.
`y`:Normalized vertical position.
`button`:Platform-independent pointer button index. | — | — | -| [`public bool Inno.Editor.Rendering.EditorRenderingModule.HasContributors(Inno.Editor.Rendering.EditorViewportKindId kind)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L110) | Gets whether the current extension generation contributes to one viewport purpose.
返回:true when at least one contributor is registered.
`kind`:Open viewport purpose. | — | — | -| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnDispose()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L672) | Releases resources retained by this feature after it has stopped. | — | — | -| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L587) | Initializes this feature when its owning runtime becomes active.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L654) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L599) | Advances this feature using the current runtime state.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.Release(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L569) | Stops retaining one viewport target.
`viewportId`:Stable viewport identity. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetContentScope(string viewportId, Inno.References.ContentReadScope content)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L195) | Sets the explicit ordered host content visible to one Editor viewport.
`viewportId`:Stable panel viewport identity.
`content`:Current frame-safe content scope. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetOutputInput(string viewportId, Inno.Rendering.Runtime.RenderOutputInput input)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L121) | Supplies viewport-local input captured by the output panel for this frame.
`viewportId`:Stable output identity.
`input`:Physical-pixel input snapshot. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetOutputRoute(string viewportId, Inno.Rendering.Runtime.RenderOutputRoute? route)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L138) | Sets the exact model contributor order for an output with several applicable models.
`viewportId`:Stable output identity.
`route`:Explicit contributor route, or null for single-model selection. | — | — | -| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetPresentation(string viewportId, Inno.Editor.Rendering.EditorViewportPresentation presentation)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L275) | Sets presentation preferences supplied to every contributor for one Editor viewport.
`viewportId`:Stable panel viewport identity.
`presentation`:Current host-owned presentation preferences. | — | — | -| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TryConfigureNavigation(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorViewportNavigationProfile profile)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L227) | Queries the selected controller's navigation contract before viewport input is processed.
返回:True when a contributor returned a usable navigation profile and became the controller.
`kind`:Open viewport purpose.
`viewportId`:Stable panel viewport identity.
`pixelWidth`:Positive target width.
`pixelHeight`:Positive target height.
`profile`:Receives the selected controller profile. | — | — | -| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TryGetManipulationSpace(string viewportId, out Inno.Editor.Rendering.EditorViewportManipulationSpace space)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L296) | Tries to get the manipulation space from the selected controller's latest contribution.
返回:true when the selected controller supplied a manipulation space.
`viewportId`:Stable panel viewport identity.
`space`:Receives the latest exact view/projection contract. | — | — | -| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TrySubmit(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorViewportOutput output)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L368) | Builds, composes, submits, and returns one host-owned offscreen viewport.
返回:true when at least one model contribution was accepted.
`kind`:Open viewport purpose.
`viewportId`:Stable panel viewport identity.
`pixelWidth`:Positive target width.
`pixelHeight`:Positive target height.
`output`:Receives the opaque viewport output. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorRenderingModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L22) | Composes reloadable rendering-model contributors while retaining only opaque presentation outputs. | — | — | - -### `Inno.Editor.Rendering.EditorRenderTargetArtifactProvider` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.EditorRenderTargetArtifactProvider(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Types.TypeCatalog types, Inno.Rendering.Assets.Authoring.ShaderCompiler shaderCompiler, Inno.Rendering.Assets.Authoring.ITextureTargetCompiler textureCompiler, Inno.Core.Diagnostics.IDiagnosticReporter diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L63) | Creates an Editor artifact provider backed by explicit shader and texture toolchains.
`shaderCompiler`:The compiler that turns backend-neutral shader IR into device target programs.
`assets`:The authoring asset pipeline that owns source mounts.
`serialization`:The serialization registry that owns active Shader contracts.
`textureCompiler`:The compiler that turns artist texture sources into portable KTX artifacts.
`diagnostics`:The sink that receives compilation and last-good fallback diagnostics.
`types`:The shared authoring type generation owner for graph and source extensions. | — | `System.ArgumentNullException`:Thrown when any required service is null. | -| [`public void Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.Dispose()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L240) | Cancels pending toolchain work and releases every cached authoring artifact. | — | — | -| [`public Inno.Rendering.Runtime.RenderTargetArtifactStatus Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.GetShaderArtifact(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant, Inno.Rendering.GraphicsCapabilities capabilities, out Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L104) | Returns a matching compiled shader when available and schedules a new candidate when source state changed.
返回:Inno.Rendering.Runtime.RenderTargetArtifactStatus.Ready when a current or last-good artifact is usable; otherwise, the exact pending or failed state of the first candidate.
`shader`:The imported backend-neutral shader asset.
`variant`:The exact material keyword selection.
`capabilities`:The active device capability snapshot used to select the target compiler profile.
`artifact`:Receives the current candidate or the last-good artifact while a replacement is compiling. | — | `System.ObjectDisposedException`:Thrown when this provider has been disposed.
`System.ArgumentNullException`:Thrown when shader or capabilities is null. | -| [`public Inno.Rendering.Runtime.RenderTargetArtifactStatus Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.GetTextureArtifact(Inno.Rendering.Assets.RenderTextureArtifactReference texture, out System.ReadOnlyMemory artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L212) | Returns a matching compiled texture when available and schedules a replacement when source state changed.
返回:Inno.Rendering.Runtime.RenderTargetArtifactStatus.Ready when a current or last-good KTX artifact is usable; otherwise, the exact pending or failed state of the first candidate.
`texture`:The imported texture asset whose authoring source is compiled.
`artifact`:Receives the current candidate or the last-good KTX artifact while a replacement is compiling. | — | `System.ObjectDisposedException`:Thrown when this provider has been disposed.
`System.ArgumentNullException`:Thrown when texture is null. | -| [`public Inno.Rendering.Assets.ShaderDefinition Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.ReadShaderDefinition(Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L154) | Reads and validates the shader definition value from its authoritative source.
返回:The validated shader definition that represents the completed operation.
`artifact`:The resolved immutable artifact payload returned to the caller. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.RequestShaderCompilation(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant, Inno.Rendering.GraphicsCapabilities capabilities)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L177) | Schedules compilation if required and reports saving-independent state for the exact shader, variant and device target.
返回:A detached status and diagnostic snapshot; last-good is explicit and never implies current-source success.
`shader`:Current imported shader.
`variant`:Selected keyword variant.
`capabilities`:Current device capabilities. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorRenderTargetArtifactProvider : Inno.Rendering.Runtime.IRenderTargetArtifactProvider, System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L23) | Produces target-specific render artifacts from imported authoring assets without exposing source access to the backend-neutral render runtime. | — | — | - -### `Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.EditorShaderArtifactValidationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Contains active-device validation state for one immutable Editor shader candidate.
`state`:Pending, successful, or failed validation state.
`diagnostics`:Detached device validation diagnostics. | — | — | -| [`public void Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Determines whether the specified object is equal to the current object.
返回:true if the specified object is equal to the current object; otherwise, false.
`obj`:The object to compare with the current object. | — | — | -| [`public override int Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Serves as the default hash function.
返回:A hash code for the current object. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? left, Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? left, Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Returns a string that represents the current object.
返回:A string that represents the current object. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L19) | Detached device validation diagnostics. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L18) | Pending, successful, or failed validation state. | — | — | -| [`public sealed record Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Contains active-device validation state for one immutable Editor shader candidate.
`state`:Pending, successful, or failed validation state.
`diagnostics`:Detached device validation diagnostics. | — | — | - -### `Inno.Editor.Rendering.EditorShaderCompilation` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorShaderCompilation.EditorShaderCompilation(Inno.Editor.Rendering.EditorRenderTargetArtifactProvider provider, Inno.Editor.Rendering.IEditorShaderArtifactValidator validator)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L103) | Pairs the authoring artifact owner with active-device validation at frame safety points.
`provider`:Host-owned shader compilation service.
`validator`:Host-owned active-device artifact validator. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilation.EditorShaderCompilation(Inno.Editor.Rendering.EditorRenderTargetArtifactProvider provider, Inno.Rendering.GraphicsCapabilities capabilities)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L86) | Pairs the authoring artifact owner with the active device's immutable capability snapshot.
`provider`:Host-owned shader compilation service.
`capabilities`:Current device capability snapshot. | — | — | -| [`public Inno.Rendering.Assets.ShaderDefinition Inno.Editor.Rendering.EditorShaderCompilation.ReadDefinition(Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L159) | Decodes the exact contract captured with a candidate using the current owner's references.
返回:A detached current-generation definition.
`artifact`:Complete candidate, not the latest uncompiled asset definition. | — | — | -| [`public void Inno.Editor.Rendering.EditorShaderCompilation.ReleaseDraft(System.Guid documentId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L216) | Cancels a document's preview work and retires its cached candidate without touching canonical artifacts.
`documentId`:Closing preview owner identity. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.Request(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L123) | Requests current compilation and reads its explicit last-good status.
返回:A detached status snapshot.
`shader`:Current generation shader asset, not retained by the service.
`variant`:Exact static keyword selection. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.RequestArtifact(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L141) | Reads the canonical Shader candidate for an isolated Material preview without modifying Material values.
返回:Compilation status and immutable last-good artifact when available.
`shader`:Current owner Shader reference.
`variant`:Exact Material keyword selection. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.RequestDraft(System.Guid documentId, Inno.Core.Graphs.GraphDocument graph, ulong revision, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L179) | Compiles a detached document through the same graph toolchain in an isolated preview cache.
返回:Preview-only state and artifact; calling this method cannot publish to Scene/Game.
`documentId`:Preview owner identity, independent of canonical artifact lookup.
`graph`:Current draft, never modified or retained.
`revision`:Monotonic draft revision, including Undo/Redo.
`variant`:Exact static keyword selection. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorShaderCompilation`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L72) | Schedules Editor shader compilation for the active device without exposing native handles to authoring panels. | — | — | - -### `Inno.Editor.Rendering.EditorShaderCompilationSnapshot` - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot.EditorShaderCompilationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, bool usingLastGood, System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Contains detached compiler state without retaining assets, tasks or extension instances.
`state`:Latest requested semantic candidate state.
`usingLastGood`:Whether rendering still uses a previous successful candidate.
`diagnostics`:Immutable latest completed compiler diagnostics. | — | — | -| [`public void Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out bool usingLastGood, out System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Determines whether the specified object is equal to the current object.
返回:true if the specified object is equal to the current object; otherwise, false.
`obj`:The object to compare with the current object. | — | — | -| [`public override int Inno.Editor.Rendering.EditorShaderCompilationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Serves as the default hash function.
返回:A hash code for the current object. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorShaderCompilationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Returns a string that represents the current object.
返回:A string that represents the current object. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderCompilationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L44) | Immutable latest completed compiler diagnostics. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderCompilationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L42) | Latest requested semantic candidate state. | — | — | -| [`public bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.usingLastGood { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L43) | Whether rendering still uses a previous successful candidate. | — | — | -| [`public sealed record Inno.Editor.Rendering.EditorShaderCompilationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Contains detached compiler state without retaining assets, tasks or extension instances.
`state`:Latest requested semantic candidate state.
`usingLastGood`:Whether rendering still uses a previous successful candidate.
`diagnostics`:Immutable latest completed compiler diagnostics. | — | — | - -### `Inno.Editor.Rendering.EditorShaderCompilationState` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Rendering.EditorShaderCompilationState.Compiling = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L18) | The latest semantic candidate is being compiled. | — | — | -| [`Inno.Editor.Rendering.EditorShaderCompilationState.Failed = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L26) | The latest semantic candidate failed. | — | — | -| [`Inno.Editor.Rendering.EditorShaderCompilationState.Succeeded = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L22) | The latest semantic candidate compiled successfully. | — | — | -| [`public enum Inno.Editor.Rendering.EditorShaderCompilationState : int`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L13) | Identifies native compilation state independently of document persistence. | — | — | - -### `Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot` - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.EditorShaderDraftCompilationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, bool usingLastGood, System.Collections.Generic.IReadOnlyList diagnostics, Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Contains a preview-only immutable candidate, never registered as a canonical asset artifact.
`state`:Latest draft compilation state.
`usingLastGood`:Whether the preview artifact belongs to an earlier successful draft.
`diagnostics`:Latest detached preview diagnostics, separate from project diagnostics.
`artifact`:Preview-only complete shader candidate, or null before the first success. | — | — | -| [`public void Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out bool usingLastGood, out System.Collections.Generic.IReadOnlyList diagnostics, out Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Determines whether the specified object is equal to the current object.
返回:true if the specified object is equal to the current object; otherwise, false.
`obj`:The object to compare with the current object. | — | — | -| [`public override int Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Serves as the default hash function.
返回:A hash code for the current object. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Returns a string that represents the current object.
返回:A string that represents the current object. | — | — | -| [`public Inno.Rendering.Assets.RenderShaderArtifact? Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.artifact { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L66) | Preview-only complete shader candidate, or null before the first success. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L65) | Latest detached preview diagnostics, separate from project diagnostics. | — | — | -| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L63) | Latest draft compilation state. | — | — | -| [`public bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.usingLastGood { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L64) | Whether the preview artifact belongs to an earlier successful draft. | — | — | -| [`public sealed record Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Contains a preview-only immutable candidate, never registered as a canonical asset artifact.
`state`:Latest draft compilation state.
`usingLastGood`:Whether the preview artifact belongs to an earlier successful draft.
`diagnostics`:Latest detached preview diagnostics, separate from project diagnostics.
`artifact`:Preview-only complete shader candidate, or null before the first success. | — | — | - -### `Inno.Editor.Rendering.EditorViewportComposition` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportComposition.EditorViewportComposition(string viewportId, int pixelWidth, int pixelHeight, Inno.Rendering.RenderTextureFormat targetFormat, System.Collections.Generic.IEnumerable layers)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L207) | Creates an immutable viewport composition.
`viewportId`:Stable panel viewport identity.
`pixelWidth`:Positive target width.
`pixelHeight`:Positive target height.
`targetFormat`:Shared presentation target format required by every layer.
`layers`:Ordered non-empty rendering-model layer collection. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorViewportComposition.layers { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L261) | Gets the ordered rendering-model layers. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportComposition.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L251) | Gets the target height. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportComposition.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L246) | Gets the target width. | — | — | -| [`public Inno.Rendering.RenderTextureFormat Inno.Editor.Rendering.EditorViewportComposition.targetFormat { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L256) | Gets the shared presentation target format. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportComposition.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L241) | Gets the stable viewport identity. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportComposition`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L185) | Describes a complete ordered set of rendering-model layers targeting one Editor viewport. | — | — | - -### `Inno.Editor.Rendering.EditorViewportContext` - + + + + + + + + + + + + + + + + @@ -487,167 +283,21 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.References.ContentReadScope Inno.Editor.Rendering.EditorViewportContext.content { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L184) | Gets the explicit ordered host content visible to this viewport. | — | — | -| [`public Inno.Editor.Core.EditorContext Inno.Editor.Rendering.EditorViewportContext.editor { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L149) | Gets the current Editor context. | — | — | -| [`public ulong Inno.Editor.Rendering.EditorViewportContext.frameIndex { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L199) | Gets the frame index shared by all Editor views in this output frame. | — | — | -| [`public Inno.Rendering.Runtime.RenderOutputInput Inno.Editor.Rendering.EditorViewportContext.input { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L203) | Gets input located in this viewport's physical pixels. | — | — | -| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Rendering.EditorViewportContext.interactions { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L154) | Gets the shared Editor interaction and selection service. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportKindId Inno.Editor.Rendering.EditorViewportContext.kind { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L159) | Gets the open viewport purpose. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationState Inno.Editor.Rendering.EditorViewportContext.navigation { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L179) | Gets host-owned neutral navigation state that the selected controller can map to its camera model. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L174) | Gets target height in physical pixels. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportContext.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L169) | Gets target width in physical pixels. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportPresentation Inno.Editor.Rendering.EditorViewportContext.presentation { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L189) | Gets host-selected presentation preferences for this viewport. | — | — | -| [`public Inno.Rendering.Runtime.IViewContentCollector Inno.Editor.Rendering.EditorViewportContext.viewContent { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L194) | Gets the active generation's model-independent world-content collector. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportContext.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L164) | Gets the stable panel viewport identity. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L108) | Supplies frame-only Editor interaction and output dimensions to a contributor. | — | — | - -### `Inno.Editor.Rendering.EditorViewportContribution` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportContribution.EditorViewportContribution(Inno.Rendering.Runtime.RenderFrameData data, Inno.Rendering.Assets.RenderPipelineAsset? pipeline = null, Inno.Rendering.RenderTextureFormat targetFormat = Inno.Rendering.RenderTextureFormat.RGBA8Srgb, Inno.Editor.Rendering.EditorViewportManipulationSpace? manipulationSpace = null)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L346) | Creates a viewport model contribution.
`data`:Pipeline-defined frame-only data.
`pipeline`:Contributor-selected pipeline, or null for the project default.
`targetFormat`:Presentation target format expected by the pipeline.
`manipulationSpace`:Optional exact view/projection contract for host-owned transform manipulation tools. | — | — | -| [`public Inno.Rendering.Runtime.RenderFrameData Inno.Editor.Rendering.EditorViewportContribution.data { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L361) | Gets pipeline-defined frame-only data. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportManipulationSpace? Inno.Editor.Rendering.EditorViewportContribution.manipulationSpace { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L376) | Gets the optional exact view/projection contract used by host-owned transform manipulation tools. | — | — | -| [`public Inno.Rendering.Assets.RenderPipelineAsset? Inno.Editor.Rendering.EditorViewportContribution.pipeline { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L366) | Gets the selected pipeline, or null for the project default. | — | — | -| [`public Inno.Rendering.RenderTextureFormat Inno.Editor.Rendering.EditorViewportContribution.targetFormat { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L371) | Gets the presentation target format expected by the pipeline. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportContribution`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L329) | Returns one rendering-model contribution without exposing a GPU backend. | — | — | - -### `Inno.Editor.Rendering.EditorViewportContributor` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Rendering.EditorViewportContributor.EditorViewportContributor()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L425) | Creates a parameterless reloadable viewport contributor. | — | — | -| [`public abstract Inno.Editor.Rendering.EditorViewportContribution Inno.Editor.Rendering.EditorViewportContributor.Build(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L461) | Builds one model-neutral render contribution for the current frame.
返回:Contributor-selected pipeline and frame data.
`context`:Frame-only Editor and viewport context. | — | — | -| [`public abstract bool Inno.Editor.Rendering.EditorViewportContributor.CanContribute(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L438) | Determines whether this rendering model participates in the supplied viewport content.
返回:true when this contributor can build a valid model contribution.
`context`:Frame-only Editor, navigation, content, and viewport context. | — | — | -| [`public virtual Inno.Editor.Rendering.EditorViewportNavigationProfile Inno.Editor.Rendering.EditorViewportContributor.ConfigureNavigation(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L449) | Configures neutral host navigation before input is processed and a request is built.
返回:The controller's current navigation capabilities and optional selection focus bound.
`context`:Frame-only Editor, navigation, content, and viewport context. | — | — | -| [`public virtual void Inno.Editor.Rendering.EditorViewportContributor.DrawToolbar(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L469) | Draws optional controller-specific toolbar controls when this contributor owns viewport interaction.
`context`:Frame-only Editor and viewport context. | — | — | -| [`public virtual void Inno.Editor.Rendering.EditorViewportContributor.HandlePointer(Inno.Editor.Rendering.EditorViewportPointerContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L479) | Handles one pointer click when this contributor owns viewport interaction.
`context`:Normalized frame-only pointer context. | — | — | -| [`public abstract class Inno.Editor.Rendering.EditorViewportContributor`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L420) | Contributes one rendering model while the host owns viewport composition, targets, and presentation. | — | — | - -### `Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.EditorViewportContributorExtensionAttribute(string id, string kind, int order = 0, int controllerPriority = 0)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L70) | Creates a viewport contributor declaration.
`id`:Globally stable contributor identity.
`kind`:Open viewport purpose handled by the contributor.
`order`:Ascending model-composition order within the viewport.
`controllerPriority`:Priority used to select the contributor that owns navigation, tools, and pointer interaction. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.controllerPriority { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L102) | Gets the priority used to select the viewport interaction controller. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L87) | Gets the globally stable contributor identity. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportKindId Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.kind { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L92) | Gets the open viewport purpose handled by the contributor. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.order { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L97) | Gets ascending model-composition order. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L53) | Marks a reloadable rendering-model contributor for one Editor viewport purpose. | — | — | - -### `Inno.Editor.Rendering.EditorViewportFocusBounds` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportFocusBounds.EditorViewportFocusBounds(Inno.Core.Mathematics.Vector3 center, float radius)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L142) | Creates a finite focus bound.
`center`:World-space focus center.
`radius`:Non-negative world-space radius. | — | `System.ArgumentException`:Thrown when the center contains a non-finite value.
`System.ArgumentOutOfRangeException`:Thrown when the radius is negative or non-finite. | -| [`public bool Inno.Editor.Rendering.EditorViewportFocusBounds.Equals(Inno.Editor.Rendering.EditorViewportFocusBounds other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportFocusBounds.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportFocusBounds.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportFocusBounds.operator ==(Inno.Editor.Rendering.EditorViewportFocusBounds left, Inno.Editor.Rendering.EditorViewportFocusBounds right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportFocusBounds.operator !=(Inno.Editor.Rendering.EditorViewportFocusBounds left, Inno.Editor.Rendering.EditorViewportFocusBounds right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportFocusBounds.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportFocusBounds.center { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L157) | Gets the world-space focus center. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportFocusBounds.radius { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L162) | Gets the non-negative world-space radius. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportFocusBounds : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Describes a world-space sphere that can be framed by host navigation. | — | — | - -### `Inno.Editor.Rendering.EditorViewportKindId` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportKindId.EditorViewportKindId(string value)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L24) | Creates a stable viewport purpose identifier.
`value`:Globally stable viewport purpose. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportKindId.Equals(Inno.Editor.Rendering.EditorViewportKindId other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportKindId.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportKindId.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportKindId.operator ==(Inno.Editor.Rendering.EditorViewportKindId left, Inno.Editor.Rendering.EditorViewportKindId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportKindId.operator !=(Inno.Editor.Rendering.EditorViewportKindId left, Inno.Editor.Rendering.EditorViewportKindId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportKindId.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L46) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportKindId.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L38) | Gets whether this identifier is usable. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportKindId.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L33) | Gets the stable viewport purpose. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportKindId : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Identifies one open Editor viewport purpose without prescribing rendering semantics. | — | — | - -### `Inno.Editor.Rendering.EditorViewportLayer` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportLayer.EditorViewportLayer(string contributorId, Inno.Rendering.Assets.RenderPipelineAsset? pipeline, Inno.Rendering.Runtime.RenderFrameData data, int order)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L147) | Creates one immutable model-neutral viewport layer.
`contributorId`:Stable identity of the rendering-model contributor that produced the layer.
`pipeline`:Contributor-selected pipeline, or null for the project default.
`data`:Contributor-defined frame-only data.
`order`:Ascending model-composition order. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportLayer.contributorId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L164) | Gets the stable identity of the rendering-model contributor. | — | — | -| [`public Inno.Rendering.Runtime.RenderFrameData Inno.Editor.Rendering.EditorViewportLayer.data { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L174) | Gets contributor-defined frame-only data. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportLayer.order { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L179) | Gets ascending model-composition order. | — | — | -| [`public Inno.Rendering.Assets.RenderPipelineAsset? Inno.Editor.Rendering.EditorViewportLayer.pipeline { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L169) | Gets the contributor-selected pipeline, or null for the project default. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportLayer`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L130) | Describes one model-neutral layer in an Editor viewport composition. | — | — | - -### `Inno.Editor.Rendering.EditorViewportManipulationPlane` - + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.Spatial = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L249) | Keeps the full three-dimensional translation, rotation, and scale controls. | — | — | -| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.XY = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L254) | Uses the X/Y translation and scale axes with the Z rotation handle. | — | — | -| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.XZ = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L259) | Uses the X/Z translation and scale axes with the Y rotation handle. | — | — | -| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.YZ = 3`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L264) | Uses the Y/Z translation and scale axes with the X rotation handle. | — | — | -| [`public enum Inno.Editor.Rendering.EditorViewportManipulationPlane : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L244) | Selects whether transform handles use all axes or a contributor-declared planar orientation. | — | — | - -### `Inno.Editor.Rendering.EditorViewportManipulationSpace` - @@ -660,24 +310,31 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportManipulationSpace.EditorViewportManipulationSpace(Inno.Core.Mathematics.Matrix viewMatrix, Inno.Core.Mathematics.Matrix projectionMatrix, bool isOrthographic, Inno.Editor.Rendering.EditorViewportManipulationPlane plane = Inno.Editor.Rendering.EditorViewportManipulationPlane.Spatial)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L291) | Creates a manipulation space matching one rendered viewport frame.
`viewMatrix`:World-to-view matrix used by the submitted frame.
`projectionMatrix`:View-to-clip matrix used by the submitted frame.
`isOrthographic`:Whether the projection is orthographic; this does not imply planar manipulation.
`plane`:The contributor-selected transform handle orientation, independent of projection. | — | `System.ArgumentOutOfRangeException`:The plane is not one of the defined neutral manipulation orientations. | -| [`public bool Inno.Editor.Rendering.EditorViewportManipulationSpace.Equals(Inno.Editor.Rendering.EditorViewportManipulationSpace other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportManipulationSpace.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportManipulationSpace.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportManipulationSpace.operator ==(Inno.Editor.Rendering.EditorViewportManipulationSpace left, Inno.Editor.Rendering.EditorViewportManipulationSpace right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportManipulationSpace.operator !=(Inno.Editor.Rendering.EditorViewportManipulationSpace left, Inno.Editor.Rendering.EditorViewportManipulationSpace right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportManipulationSpace.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportManipulationSpace.isOrthographic { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L318) | Gets whether the submitted frame used an orthographic projection. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportManipulationPlane Inno.Editor.Rendering.EditorViewportManipulationSpace.plane { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L323) | Gets the contributor-declared neutral manipulation plane, or Spatial for all axes. | — | — | -| [`public Inno.Core.Mathematics.Matrix Inno.Editor.Rendering.EditorViewportManipulationSpace.projectionMatrix { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L313) | Gets the view-to-clip matrix used by the submitted frame. | — | — | -| [`public Inno.Core.Mathematics.Matrix Inno.Editor.Rendering.EditorViewportManipulationSpace.viewMatrix { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L308) | Gets the world-to-view matrix used by the submitted frame. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportManipulationSpace : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Describes the exact backend-neutral view and projection used to draw a viewport so host tools can manipulate selected scene transforms without knowing the controller's camera model. | — | — | - -### `Inno.Editor.Rendering.EditorViewportNavigationCapabilities` - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -686,34 +343,26 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Fly = 8`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L78) | Allows free-look movement. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.FrameSelection = 16`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L83) | Allows framing a provider-supplied or host-derived selection bound. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.None = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L53) | Disables host-owned navigation. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Orbit = 4`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L73) | Allows pivot-oriented orbit and dolly. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Pan = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L58) | Allows translating the view parallel to its image plane. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Planar = Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Pan | Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Zoom`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L68) | Allows the complete plane-oriented pan and zoom interaction model. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Zoom = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L63) | Allows changing orthographic size or perspective focus distance. | — | — | -| [`public enum Inno.Editor.Rendering.EditorViewportNavigationCapabilities : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L48) | Declares navigation operations supported by one viewport interaction controller. | — | — | - -### `Inno.Editor.Rendering.EditorViewportNavigationMode` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Fly = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L41) | Moves and looks freely from the current view position. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Orbit = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L36) | Rotates around a focus pivot and dollies along the view direction. | — | — | -| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Planar = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L31) | Pans and zooms over a provider-defined plane. | — | — | -| [`public enum Inno.Editor.Rendering.EditorViewportNavigationMode : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L26) | Identifies the active interaction model without prescribing a rendering camera type. | — | — | - -### `Inno.Editor.Rendering.EditorViewportNavigationProfile` - + + + + + + + + + + + + + + + + + + + + @@ -730,54 +379,6 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportNavigationProfile.EditorViewportNavigationProfile(Inno.Editor.Rendering.EditorViewportNavigationProfileId id, Inno.Editor.Rendering.EditorViewportNavigationCapabilities capabilities, Inno.Editor.Rendering.EditorViewportNavigationMode defaultMode)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L184) | Creates a provider-defined navigation profile.
`id`:Stable profile identity.
`capabilities`:Operations accepted by the provider.
`defaultMode`:Preferred mode when current state is unsupported. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationCapabilities Inno.Editor.Rendering.EditorViewportNavigationProfile.capabilities { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L219) | Gets the operations accepted by this provider. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationMode Inno.Editor.Rendering.EditorViewportNavigationProfile.defaultMode { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L224) | Gets the mode selected when current state is not supported. | — | — | -| [`public static Inno.Editor.Rendering.EditorViewportNavigationProfile Inno.Editor.Rendering.EditorViewportNavigationProfile.disabled { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L279) | Gets a disabled fallback profile used when no provider navigation contract exists. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.fastMovementMultiplier { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L269) | Gets or sets the multiplier used while fast fly movement is requested. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportFocusBounds? Inno.Editor.Rendering.EditorViewportNavigationProfile.focusBounds { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L234) | Gets or sets the optional current selection bound. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.framePadding { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L274) | Gets or sets additional framing space around selection bounds. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationProfileId Inno.Editor.Rendering.EditorViewportNavigationProfile.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L214) | Gets the stable profile identity. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.maximumFocusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L264) | Gets or sets maximum orbit or framing distance. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.maximumOrthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L254) | Gets or sets maximum orthographic half-height. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.minimumFocusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L259) | Gets or sets minimum orbit or framing distance. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.minimumOrthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L249) | Gets or sets minimum orthographic half-height. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.rotationSensitivity { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L239) | Gets or sets pointer rotation sensitivity in radians per pixel. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationProfile.worldUp { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L229) | Gets or sets the provider-defined world-up direction. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.zoomSensitivity { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L244) | Gets or sets exponential wheel zoom sensitivity. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportNavigationProfile`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L170) | Describes how the host may navigate one provider-owned viewport without exposing the provider's camera model. | — | — | - -### `Inno.Editor.Rendering.EditorViewportNavigationProfileId` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportNavigationProfileId.EditorViewportNavigationProfileId(string value)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L97) | Creates a stable profile identifier.
`value`:Globally stable profile identity. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.Equals(Inno.Editor.Rendering.EditorViewportNavigationProfileId other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportNavigationProfileId.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.operator ==(Inno.Editor.Rendering.EditorViewportNavigationProfileId left, Inno.Editor.Rendering.EditorViewportNavigationProfileId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.operator !=(Inno.Editor.Rendering.EditorViewportNavigationProfileId left, Inno.Editor.Rendering.EditorViewportNavigationProfileId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportNavigationProfileId.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L119) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L111) | Gets whether this identifier contains a usable value. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportNavigationProfileId.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L106) | Gets the stable profile identity. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportNavigationProfileId : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Identifies a provider-defined navigation profile across reload generations. | — | — | - -### `Inno.Editor.Rendering.EditorViewportNavigationState` - @@ -794,28 +395,6 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportNavigationState.EditorViewportNavigationState()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L303) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Editor.Rendering.EditorViewportNavigationState.ConfigureOrthographic(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, float orthographicSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L501) | Atomically initializes an orthographic navigation view.
`position`:Provider-defined world-space position.
`rotation`:Provider-defined world-space rotation.
`orthographicSize`:Positive orthographic half-height. | — | — | -| [`public void Inno.Editor.Rendering.EditorViewportNavigationState.ConfigurePerspective(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, float fieldOfView, float nearClip, float farClip)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L538) | Atomically initializes a perspective navigation view.
`position`:Provider-defined world-space position.
`rotation`:Provider-defined world-space rotation.
`fieldOfView`:Perspective vertical field of view in degrees.
`nearClip`:Positive near clipping distance.
`farClip`:Far clipping distance greater than nearClip. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.farClip { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L448) | Gets or sets the far clipping distance greater than the near distance. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.fieldOfView { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L418) | Gets or sets the perspective vertical field of view in degrees. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.focusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L463) | Gets or sets the positive orbit and framing distance. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportNavigationState.isInitialized { get; private set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L324) | Gets whether a provider or restored panel state initialized this state. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportNavigationMode Inno.Editor.Rendering.EditorViewportNavigationState.mode { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L344) | Gets or sets the active host navigation mode. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.movementSpeed { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L478) | Gets or sets the positive base movement speed in world units per second. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.nearClip { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L433) | Gets or sets the positive near clipping distance. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.orthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L403) | Gets or sets the positive orthographic half-height in provider world units. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationState.pivot { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L389) | Gets or sets the world-space orbit and framing pivot. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationState.position { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L359) | Gets or sets the provider-defined world-space view position. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportProjection Inno.Editor.Rendering.EditorViewportNavigationState.projection { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L329) | Gets or sets the current projection family. | — | — | -| [`public Inno.Core.Mathematics.Quaternion Inno.Editor.Rendering.EditorViewportNavigationState.rotation { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L373) | Gets or sets the normalized provider-defined world-space view rotation. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportNavigationState`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L303) | Stores rendering-model-neutral navigation state for one Editor viewport across provider reloads. | — | — | - -### `Inno.Editor.Rendering.EditorViewportOutput` - @@ -829,115 +408,6 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportOutput.EditorViewportOutput(string viewportId, Inno.Adapter.Presentation.PresentationTextureHandle texture, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L32) | Creates an editor viewport output snapshot.
`viewportId`:Stable viewport identity.
`texture`:Opaque presentation token, or an invalid token while warming up.
`pixelWidth`:Current target width.
`pixelHeight`:Current target height. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportOutput.Equals(Inno.Editor.Rendering.EditorViewportOutput other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportOutput.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportOutput.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportOutput.operator ==(Inno.Editor.Rendering.EditorViewportOutput left, Inno.Editor.Rendering.EditorViewportOutput right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportOutput.operator !=(Inno.Editor.Rendering.EditorViewportOutput left, Inno.Editor.Rendering.EditorViewportOutput right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportOutput.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportOutput.isReady { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L70) | Gets whether a completed target can be drawn. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportOutput.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L65) | Gets the current target height. | — | — | -| [`public int Inno.Editor.Rendering.EditorViewportOutput.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L60) | Gets the current target width. | — | — | -| [`public Inno.Adapter.Presentation.PresentationTextureHandle Inno.Editor.Rendering.EditorViewportOutput.texture { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L55) | Gets the opaque presentation token. | — | — | -| [`public string Inno.Editor.Rendering.EditorViewportOutput.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L50) | Gets the stable viewport identity. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportOutput : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Identifies a renderer-owned editor viewport image through an opaque presentation token. | — | — | - -### `Inno.Editor.Rendering.EditorViewportPointerContext` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public int Inno.Editor.Rendering.EditorViewportPointerContext.button { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L414) | Gets the platform-independent pointer button index. | — | — | -| [`public Inno.Editor.Rendering.EditorViewportContext Inno.Editor.Rendering.EditorViewportPointerContext.viewport { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L399) | Gets the owning frame-only viewport context. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportPointerContext.x { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L404) | Gets normalized horizontal pointer position. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportPointerContext.y { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L409) | Gets normalized vertical pointer position. | — | — | -| [`public sealed class Inno.Editor.Rendering.EditorViewportPointerContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L382) | Supplies normalized pointer interaction over one rendered viewport. | — | — | - -### `Inno.Editor.Rendering.EditorViewportPresentation` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Rendering.EditorViewportPresentation.EditorViewportPresentation(Inno.Core.Mathematics.Color backgroundColor, float pixelDensity = 1)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L220) | Creates presentation preferences for one Editor viewport.
`backgroundColor`:Linear clear color preferred by the host panel.
`pixelDensity`:Physical render pixels per logical presentation unit. | — | — | -| [`public bool Inno.Editor.Rendering.EditorViewportPresentation.Equals(Inno.Editor.Rendering.EditorViewportPresentation other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Rendering.EditorViewportPresentation.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Rendering.EditorViewportPresentation.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportPresentation.operator ==(Inno.Editor.Rendering.EditorViewportPresentation left, Inno.Editor.Rendering.EditorViewportPresentation right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Rendering.EditorViewportPresentation.operator !=(Inno.Editor.Rendering.EditorViewportPresentation left, Inno.Editor.Rendering.EditorViewportPresentation right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Rendering.EditorViewportPresentation.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public Inno.Core.Mathematics.Color Inno.Editor.Rendering.EditorViewportPresentation.backgroundColor { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L233) | Gets the linear clear color preferred by the host panel. | — | — | -| [`public float Inno.Editor.Rendering.EditorViewportPresentation.pixelDensity { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L238) | Gets physical render pixels per logical presentation unit. | — | — | -| [`public readonly record struct Inno.Editor.Rendering.EditorViewportPresentation : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Describes host-selected presentation preferences without prescribing rendering behavior. | — | — | - -### `Inno.Editor.Rendering.EditorViewportProjection` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Rendering.EditorViewportProjection.Orthographic = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L15) | Uses a parallel projection controlled by an orthographic half-height. | — | — | -| [`Inno.Editor.Rendering.EditorViewportProjection.Perspective = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L20) | Uses a perspective projection controlled by a vertical field of view. | — | — | -| [`public enum Inno.Editor.Rendering.EditorViewportProjection : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L10) | Identifies the projection family represented by neutral Editor viewport navigation state. | — | — | - -### `Inno.Editor.Rendering.IEditorGizmoSink` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Rendering.IEditorGizmoSink.Icon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L76) | Adds a visible, selectable icon at an object's world position.
`owner`:The scene object selected when the icon is clicked.
`position`:The icon position in world space.
`iconId`:A stable semantic icon ID. Unknown IDs use the generic scene icon. | — | — | -| [`void Inno.Editor.Rendering.IEditorGizmoSink.Line(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L91) | Adds a non-interactive world-space line, normally for selected bounds.
`start`:The first world-space endpoint.
`end`:The second world-space endpoint. | — | — | -| [`public interface Inno.Editor.Rendering.IEditorGizmoSink`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L62) | Receives transient world-space gizmo geometry from independent Editor extensions. | — | — | - -### `Inno.Editor.Rendering.IEditorPreviewService` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Rendering.IEditorPreviewService.Draw(Inno.Editor.Rendering.EditorPreviewHandle handle, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L156) | Draws one current-generation preview into the active presentation surface.
`handle`:Preview handle returned by this service.
`logicalSize`:Positive destination size in logical pixels. | — | — | -| [`bool Inno.Editor.Rendering.IEditorPreviewService.Release(Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L170) | Releases one cached preview registration.
返回:true when the handle was active and released.
`handle`:Preview handle returned by this service. | — | — | -| [`void Inno.Editor.Rendering.IEditorPreviewService.ReleaseAll()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L175) | Releases every cached preview registration. | — | — | -| [`void Inno.Editor.Rendering.IEditorPreviewService.ReleaseRendered(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L103) | Releases an offscreen preview, including one which has not produced its first handle.
`viewportId`:The same consumer-qualified identity submitted in the composition. | — | — | -| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryGetTexture(Inno.Rendering.Assets.TextureAsset texture, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L117) | Tries to resolve a standalone texture preview without blocking target compilation.
返回:true when the preview is ready.
`texture`:Texture asset to preview.
`handle`:Receives a generation-scoped handle when ready. | — | — | -| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryGetTextureArtifact(Inno.Rendering.Assets.RenderTextureArtifactReference texture, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L140) | Tries to resolve a named texture artifact preview without blocking target compilation.
返回:true when the preview is ready.
`texture`:Stable named texture artifact reference.
`pixelWidth`:Positive source width.
`pixelHeight`:Positive source height.
`handle`:Receives a generation-scoped handle when ready. | — | — | -| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryRender(Inno.Editor.Rendering.EditorViewportComposition composition, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L92) | Submits an isolated rendering composition through the shared offscreen viewport bridge.
返回:True when the completed previous output can be drawn; false during initial allocation or resize.
`composition`:Invocation-local layers; the host retains no contributor instances after the frame.
`handle`:Receives a current-generation preview once its target is resident. | — | — | -| [`uint Inno.Editor.Rendering.IEditorPreviewService.deviceGeneration { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L78) | Gets the active rendering-device generation. | — | — | -| [`public interface Inno.Editor.Rendering.IEditorPreviewService`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L73) | Provides one shared preview texture bridge for browsers, inspectors, and document canvases. | — | — | - -### `Inno.Editor.Rendering.IEditorRenderingHost` - @@ -945,78 +415,388 @@ Host 创建或 resize `RenderTexture`,首个目标写入成功录制后再将 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Rendering.IEditorRenderingHost.Draw(Inno.Editor.Rendering.EditorViewportOutput output, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L108) | Draws a ready viewport output inside the current ImGui window.
`output`:Output returned by Inno.Editor.Rendering.IEditorRenderingHost.Submit(Inno.Editor.Rendering.EditorViewportComposition).
`logicalSize`:Destination size in logical UI pixels. | — | — | -| [`void Inno.Editor.Rendering.IEditorRenderingHost.Release(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L119) | Releases one viewport and queues its GPU target for frame-safe destruction.
`viewportId`:Stable viewport identity. | — | — | -| [`void Inno.Editor.Rendering.IEditorRenderingHost.ReleaseAll()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L124) | Releases every viewport owned by this editor host service. | — | — | -| [`Inno.Editor.Rendering.EditorViewportOutput Inno.Editor.Rendering.IEditorRenderingHost.Submit(Inno.Editor.Rendering.EditorViewportComposition composition)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L97) | Submits or updates one composed offscreen editor viewport.
返回:The current presentation output, which can be warming up for one frame.
`composition`:Complete ordered model composition for the current frame. | — | — | -| [`ulong Inno.Editor.Rendering.IEditorRenderingHost.currentFrameIndex { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L86) | Gets the current output frame index shared by Editor views. | — | — | -| [`Inno.Rendering.Runtime.IViewContentCollector Inno.Editor.Rendering.IEditorRenderingHost.viewContent { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L81) | Gets the active generation's model-independent world-content collector. | — | — | -| [`public interface Inno.Editor.Rendering.IEditorRenderingHost`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L76) | Bridges editor extensions to a host-owned render request sink and opaque texture presenter. | — | — | - -### `Inno.Editor.Rendering.IEditorShaderArtifactValidator` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Editor.Rendering.IEditorShaderArtifactValidator.Release(System.Guid documentId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L59) | Forgets pending and completed validation state owned by one document.
`documentId`:Closing or invalidated document identity. | — | — | -| [`Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot Inno.Editor.Rendering.IEditorShaderArtifactValidator.Request(System.Guid documentId, ulong revision, Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L47) | Requests or reads validation for an exact document revision and immutable artifact.
返回:Current detached validation state.
`documentId`:Stable Editor document identity.
`revision`:Exact document revision that produced the artifact.
`artifact`:Complete immutable candidate compiled for the active device. | — | — | -| [`Inno.Rendering.GraphicsCapabilities Inno.Editor.Rendering.IEditorShaderArtifactValidator.capabilities { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L30) | Gets the capability snapshot used to compile candidates for this validator. | — | — | -| [`public interface Inno.Editor.Rendering.IEditorShaderArtifactValidator`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L25) | Queues immutable shader candidates for validation at the active device's next frame safety point. | — | — | - -### `Inno.Editor.Rendering.PipelineDocuments` - + + + + + + + + + + + + + + + + - - - + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public void Inno.Editor.Rendering.PipelineDocuments.Commit(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L84) | Completes one gesture without saving the source.
`assetId`:Open Pipeline identity. | — | — | -| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L133) | Initializes this feature when its owning runtime becomes active.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L147) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L140) | Advances this feature using the current runtime state.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public System.Guid Inno.Editor.Rendering.PipelineDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L46) | Opens a native Pipeline source in the shared document service, including failed imports.
返回:The persistent source identity used by draft operations.
`path`:Mounted source path. | — | — | -| [`public Inno.Rendering.Assets.RenderPipelineAsset Inno.Editor.Rendering.PipelineDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L57) | Reads a detached current-generation Pipeline value; its referenced canonical assets must not be mutated.
返回:The unsaved native Pipeline value.
`assetId`:Open Pipeline identity. | — | — | -| [`public void Inno.Editor.Rendering.PipelineDocuments.Replace(System.Guid assetId, Inno.Rendering.Assets.RenderPipelineAsset candidate, bool finishGesture = true)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L71) | Changes a draft through shared History without publishing it to Scene/Game.
`assetId`:Open Pipeline identity.
`candidate`:Detached edited value.
`finishGesture`:Whether this sample completes the gesture. | — | — | -| [`public void Inno.Editor.Rendering.PipelineDocuments.ReplaceSettings(System.Guid assetId, TSettings settings, bool finishGesture = true) where TSettings : class, Inno.Core.Serialization.ISerializable`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L101) | Captures typed Pipeline settings with this source owner's complete reference and dependency context.
`assetId`:Open Pipeline identity.
`settings`:Detached edited settings.
`finishGesture`:Whether this sample completes the gesture. | — | — | -| [`public sealed class Inno.Editor.Rendering.PipelineDocuments : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L19) | Edits Pipeline sources and typed extension settings without changing canonical rendering assets before Save. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorRenderTargetArtifactProvider : Inno.Rendering.Runtime.IRenderTargetArtifactProvider, System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L23) | Produces target-specific render artifacts from imported authoring assets without exposing source access to the backend-neutral render runtime. | — | — | +| [`public Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.EditorRenderTargetArtifactProvider(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Types.TypeCatalog types, Inno.Rendering.Assets.Authoring.ShaderCompiler shaderCompiler, Inno.Rendering.Assets.Authoring.ITextureTargetCompiler textureCompiler, Inno.Core.Diagnostics.IDiagnosticReporter diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L63) | Creates an Editor artifact provider backed by explicit shader and texture toolchains.
`shaderCompiler`: The compiler that turns backend-neutral shader IR into device target programs.
`assets`: The authoring asset pipeline that owns source mounts.
`serialization`: The serialization registry that owns active Shader contracts.
`textureCompiler`: The compiler that turns artist texture sources into portable KTX artifacts.
`diagnostics`: The sink that receives compilation and last-good fallback diagnostics.
`types`: The shared authoring type generation owner for graph and source extensions. | — | `System.ArgumentNullException`: Thrown when any required service is null. | +| [`public Inno.Rendering.Runtime.RenderTargetArtifactStatus Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.GetShaderArtifact(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant, Inno.Rendering.GraphicsCapabilities capabilities, out Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L104) | Returns a matching compiled shader when available and schedules a new candidate when source state changed.
`shader`: The imported backend-neutral shader asset.
`variant`: The exact material keyword selection.
`capabilities`: The active device capability snapshot used to select the target compiler profile.
`artifact`: Receives the current candidate or the last-good artifact while a replacement is compiling. | when a current or last-good artifact is usable; otherwise, the exact pending or failed state of the first candidate. | `System.ObjectDisposedException`: Thrown when this provider has been disposed.
`System.ArgumentNullException`: Thrown when or is . | +| [`public Inno.Rendering.Assets.ShaderDefinition Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.ReadShaderDefinition(Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L154) | Reads and validates the shader definition value from its authoritative source.
`artifact`: The resolved immutable artifact payload returned to the caller. | The validated shader definition that represents the completed operation. | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.RequestShaderCompilation(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant, Inno.Rendering.GraphicsCapabilities capabilities)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L177) | Schedules compilation if required and reports saving-independent state for the exact shader, variant and device target.
`shader`: Current imported shader.
`variant`: Selected keyword variant.
`capabilities`: Current device capabilities. | A detached status and diagnostic snapshot; last-good is explicit and never implies current-source success. | — | +| [`public Inno.Rendering.Runtime.RenderTargetArtifactStatus Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.GetTextureArtifact(Inno.Rendering.Assets.RenderTextureArtifactReference texture, out System.ReadOnlyMemory artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L212) | Returns a matching compiled texture when available and schedules a replacement when source state changed.
`texture`: The imported texture asset whose authoring source is compiled.
`artifact`: Receives the current candidate or the last-good KTX artifact while a replacement is compiling. | when a current or last-good KTX artifact is usable; otherwise, the exact pending or failed state of the first candidate. | `System.ObjectDisposedException`: Thrown when this provider has been disposed.
`System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.Rendering.EditorRenderTargetArtifactProvider.Dispose()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorRenderTargetArtifactProvider.cs#L240) | Cancels pending toolchain work and releases every cached authoring artifact. | — | — | +| [`public sealed record Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Contains active-device validation state for one immutable Editor shader candidate.
`state`: Pending, successful, or failed validation state.
`diagnostics`: Detached device validation diagnostics. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.EditorShaderArtifactValidationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Contains active-device validation state for one immutable Editor shader candidate.
`state`: Pending, successful, or failed validation state.
`diagnostics`: Detached device validation diagnostics. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L18) | Pending, successful, or failed validation state. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L19) | Detached device validation diagnostics. | — | — | +| [`public override string Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? left, Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? left, Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public interface Inno.Editor.Rendering.IEditorShaderArtifactValidator`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L25) | Queues immutable shader candidates for validation at the active device's next frame safety point. | — | — | +| [`Inno.Rendering.GraphicsCapabilities Inno.Editor.Rendering.IEditorShaderArtifactValidator.capabilities { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L30) | Gets the capability snapshot used to compile candidates for this validator. | — | — | +| [`Inno.Editor.Rendering.EditorShaderArtifactValidationSnapshot Inno.Editor.Rendering.IEditorShaderArtifactValidator.Request(System.Guid documentId, ulong revision, Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L47) | Requests or reads validation for an exact document revision and immutable artifact.
`documentId`: Stable Editor document identity.
`revision`: Exact document revision that produced the artifact.
`artifact`: Complete immutable candidate compiled for the active device. | Current detached validation state. | — | +| [`void Inno.Editor.Rendering.IEditorShaderArtifactValidator.Release(System.Guid documentId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderArtifactValidation.cs#L59) | Forgets pending and completed validation state owned by one document.
`documentId`: Closing or invalidated document identity. | — | — | +| [`public enum Inno.Editor.Rendering.EditorShaderCompilationState : int`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L13) | Identifies native compilation state independently of document persistence. | — | — | +| [`Inno.Editor.Rendering.EditorShaderCompilationState.Compiling = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L18) | The latest semantic candidate is being compiled. | — | — | +| [`Inno.Editor.Rendering.EditorShaderCompilationState.Succeeded = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L22) | The latest semantic candidate compiled successfully. | — | — | +| [`Inno.Editor.Rendering.EditorShaderCompilationState.Failed = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L26) | The latest semantic candidate failed. | — | — | +| [`public sealed record Inno.Editor.Rendering.EditorShaderCompilationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Contains detached compiler state without retaining assets, tasks or extension instances.
`state`: Latest requested semantic candidate state.
`usingLastGood`: Whether rendering still uses a previous successful candidate.
`diagnostics`: Immutable latest completed compiler diagnostics. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot.EditorShaderCompilationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, bool usingLastGood, System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Contains detached compiler state without retaining assets, tasks or extension instances.
`state`: Latest requested semantic candidate state.
`usingLastGood`: Whether rendering still uses a previous successful candidate.
`diagnostics`: Immutable latest completed compiler diagnostics. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderCompilationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L42) | Latest requested semantic candidate state. | — | — | +| [`public bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.usingLastGood { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L43) | Whether rendering still uses a previous successful candidate. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderCompilationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L44) | Immutable latest completed compiler diagnostics. | — | — | +| [`public override string Inno.Editor.Rendering.EditorShaderCompilationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorShaderCompilationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderCompilationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Rendering.EditorShaderCompilationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out bool usingLastGood, out System.Collections.Generic.IReadOnlyList diagnostics)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L41) | Inherited or compiler-provided contract. | — | — | +| [`public sealed record Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Contains a preview-only immutable candidate, never registered as a canonical asset artifact.
`state`: Latest draft compilation state.
`usingLastGood`: Whether the preview artifact belongs to an earlier successful draft.
`diagnostics`: Latest detached preview diagnostics, separate from project diagnostics.
`artifact`: Preview-only complete shader candidate, or null before the first success. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.EditorShaderDraftCompilationSnapshot(Inno.Editor.Rendering.EditorShaderCompilationState state, bool usingLastGood, System.Collections.Generic.IReadOnlyList diagnostics, Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Contains a preview-only immutable candidate, never registered as a canonical asset artifact.
`state`: Latest draft compilation state.
`usingLastGood`: Whether the preview artifact belongs to an earlier successful draft.
`diagnostics`: Latest detached preview diagnostics, separate from project diagnostics.
`artifact`: Preview-only complete shader candidate, or null before the first success. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationState Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.state { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L63) | Latest draft compilation state. | — | — | +| [`public bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.usingLastGood { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L64) | Whether the preview artifact belongs to an earlier successful draft. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.diagnostics { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L65) | Latest detached preview diagnostics, separate from project diagnostics. | — | — | +| [`public Inno.Rendering.Assets.RenderShaderArtifact? Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.artifact { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L66) | Preview-only complete shader candidate, or null before the first success. | — | — | +| [`public override string Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.operator !=(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.operator ==(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? left, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? right)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Equals(object? obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Equals(Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot? other)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot.Deconstruct(out Inno.Editor.Rendering.EditorShaderCompilationState state, out bool usingLastGood, out System.Collections.Generic.IReadOnlyList diagnostics, out Inno.Rendering.Assets.RenderShaderArtifact? artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L62) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorShaderCompilation`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L72) | Schedules Editor shader compilation for the active device without exposing native handles to authoring panels. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilation.EditorShaderCompilation(Inno.Editor.Rendering.EditorRenderTargetArtifactProvider provider, Inno.Rendering.GraphicsCapabilities capabilities)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L86) | Pairs the authoring artifact owner with the active device's immutable capability snapshot.
`provider`: Host-owned shader compilation service.
`capabilities`: Current device capability snapshot. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilation.EditorShaderCompilation(Inno.Editor.Rendering.EditorRenderTargetArtifactProvider provider, Inno.Editor.Rendering.IEditorShaderArtifactValidator validator)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L103) | Pairs the authoring artifact owner with active-device validation at frame safety points.
`provider`: Host-owned shader compilation service.
`validator`: Host-owned active-device artifact validator. | — | — | +| [`public Inno.Editor.Rendering.EditorShaderCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.Request(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L123) | Requests current compilation and reads its explicit last-good status.
`shader`: Current generation shader asset, not retained by the service.
`variant`: Exact static keyword selection. | A detached status snapshot. | — | +| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.RequestArtifact(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L141) | Reads the canonical Shader candidate for an isolated Material preview without modifying Material values.
`shader`: Current owner Shader reference.
`variant`: Exact Material keyword selection. | Compilation status and immutable last-good artifact when available. | — | +| [`public Inno.Rendering.Assets.ShaderDefinition Inno.Editor.Rendering.EditorShaderCompilation.ReadDefinition(Inno.Rendering.Assets.RenderShaderArtifact artifact)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L159) | Decodes the exact contract captured with a candidate using the current owner's references.
`artifact`: Complete candidate, not the latest uncompiled asset definition. | A detached current-generation definition. | — | +| [`public Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot Inno.Editor.Rendering.EditorShaderCompilation.RequestDraft(System.Guid documentId, Inno.Core.Graphs.GraphDocument graph, ulong revision, Inno.Rendering.Assets.RenderShaderVariant variant)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L179) | Compiles a detached document through the same graph toolchain in an isolated preview cache.
`documentId`: Preview owner identity, independent of canonical artifact lookup.
`graph`: Current draft, never modified or retained.
`revision`: Monotonic draft revision, including Undo/Redo.
`variant`: Exact static keyword selection. | Preview-only state and artifact; calling this method cannot publish to Scene/Game. | — | +| [`public void Inno.Editor.Rendering.EditorShaderCompilation.ReleaseDraft(System.Guid documentId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Compilation/EditorShaderCompilation.cs#L216) | Cancels a document's preview work and retires its cached candidate without touching canonical artifacts.
`documentId`: Closing preview owner identity. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorGizmoContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L12) | Provides scene content and selection for transient Editor gizmos. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoContext.EditorGizmoContext(Inno.References.ContentReadScope content, Inno.Core.Identity.RuntimeIdentity? selected, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L29) | Creates a frame-scoped gizmo request.
`content`: The scenes presented by the Scene viewport.
`selected`: The selected live object, when available.
`pixelWidth`: The Scene viewport width.
`pixelHeight`: The Scene viewport height. | — | — | +| [`public Inno.References.ContentReadScope Inno.Editor.Rendering.EditorGizmoContext.content { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L44) | Gets the scene roots visible in this Editor viewport. | — | — | +| [`public Inno.Core.Identity.RuntimeIdentity? Inno.Editor.Rendering.EditorGizmoContext.selected { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L48) | Gets the transient selected identity. | — | — | +| [`public int Inno.Editor.Rendering.EditorGizmoContext.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L52) | Gets the viewport width in physical pixels. | — | — | +| [`public int Inno.Editor.Rendering.EditorGizmoContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L56) | Gets the viewport height in physical pixels. | — | — | +| [`public interface Inno.Editor.Rendering.IEditorGizmoSink`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L62) | Receives transient world-space gizmo geometry from independent Editor extensions. | — | — | +| [`void Inno.Editor.Rendering.IEditorGizmoSink.Icon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L76) | Adds a visible, selectable icon at an object's world position.
`owner`: The scene object selected when the icon is clicked.
`position`: The icon position in world space.
`iconId`: A stable semantic icon ID. Unknown IDs use the generic scene icon. | — | — | +| [`void Inno.Editor.Rendering.IEditorGizmoSink.Line(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L91) | Adds a non-interactive world-space line, normally for selected bounds.
`start`: The first world-space endpoint.
`end`: The second world-space endpoint. | — | — | +| [`public abstract class Inno.Editor.Rendering.EditorGizmoProvider`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L100) | Contributes Editor-only icons and bounds without modifying a rendering model. | — | — | +| [`public abstract void Inno.Editor.Rendering.EditorGizmoProvider.Collect(Inno.Editor.Rendering.EditorGizmoContext context, Inno.Editor.Rendering.IEditorGizmoSink sink)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L111) | Submits transient icons and lines for the presented scenes.
`context`: The current Scene viewport request.
`sink`: The collector that owns submitted primitives for this frame. | — | — | +| [`protected Inno.Editor.Rendering.EditorGizmoProvider.EditorGizmoProvider()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L100) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L121) | Marks one reloadable Editor gizmo provider. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute.EditorGizmoProviderExtensionAttribute(string id)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L129) | Creates a provider declaration with a stable identifier.
`id`: The globally unique provider identifier. | — | — | +| [`public string Inno.Editor.Rendering.EditorGizmoProviderExtensionAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L138) | Gets the stable provider identifier. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorGizmoIcon : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | One selectable Editor icon.
`owner`: The object that owns the resulting lifetime and state.
`position`: The vector3 value used to initialize this instance.
`iconId`: The stable visual identity resolved by the Scene View presentation. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoIcon.EditorGizmoIcon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | One selectable Editor icon.
`owner`: The object that owns the resulting lifetime and state.
`position`: The vector3 value used to initialize this instance.
`iconId`: The stable visual identity resolved by the Scene View presentation. | — | — | +| [`public Inno.Core.Identity.Identity Inno.Editor.Rendering.EditorGizmoIcon.owner { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L154) | The object that owns the resulting lifetime and state. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoIcon.position { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L155) | The vector3 value used to initialize this instance. | — | — | +| [`public string Inno.Editor.Rendering.EditorGizmoIcon.iconId { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L156) | The stable visual identity resolved by the Scene View presentation. | — | — | +| [`public override string Inno.Editor.Rendering.EditorGizmoIcon.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorGizmoIcon.operator !=(Inno.Editor.Rendering.EditorGizmoIcon left, Inno.Editor.Rendering.EditorGizmoIcon right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorGizmoIcon.operator ==(Inno.Editor.Rendering.EditorGizmoIcon left, Inno.Editor.Rendering.EditorGizmoIcon right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorGizmoIcon.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorGizmoIcon.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorGizmoIcon.Equals(Inno.Editor.Rendering.EditorGizmoIcon other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Rendering.EditorGizmoIcon.Deconstruct(out Inno.Core.Identity.Identity owner, out Inno.Core.Mathematics.Vector3 position, out string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L153) | Inherited or compiler-provided contract. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorGizmoLine : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | One non-interactive Editor line.
`start`: The vector3 value used to initialize this instance.
`end`: The vector3 value used to initialize this instance. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoLine.EditorGizmoLine(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | One non-interactive Editor line.
`start`: The vector3 value used to initialize this instance.
`end`: The vector3 value used to initialize this instance. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoLine.start { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L169) | The vector3 value used to initialize this instance. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorGizmoLine.end { get; init; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L170) | The vector3 value used to initialize this instance. | — | — | +| [`public override string Inno.Editor.Rendering.EditorGizmoLine.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorGizmoLine.operator !=(Inno.Editor.Rendering.EditorGizmoLine left, Inno.Editor.Rendering.EditorGizmoLine right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorGizmoLine.operator ==(Inno.Editor.Rendering.EditorGizmoLine left, Inno.Editor.Rendering.EditorGizmoLine right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorGizmoLine.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorGizmoLine.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorGizmoLine.Equals(Inno.Editor.Rendering.EditorGizmoLine other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Editor.Rendering.EditorGizmoLine.Deconstruct(out Inno.Core.Mathematics.Vector3 start, out Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L168) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorGizmoFrame : Inno.Editor.Rendering.IEditorGizmoSink`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L176) | Holds the transient gizmo primitives for one Scene viewport frame. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorGizmoFrame.icons { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L184) | Gets the ordered selectable icons. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorGizmoFrame.lines { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L188) | Gets the ordered non-interactive lines. | — | — | +| [`public void Inno.Editor.Rendering.EditorGizmoFrame.Icon(Inno.Core.Identity.Identity owner, Inno.Core.Mathematics.Vector3 position, string iconId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L202) | Appends a selectable scene icon to the current gizmo frame.
`owner`: The object that owns the resulting lifetime and state.
`position`: The position consumed by icon; ownership remains with the caller unless explicitly stated otherwise.
`iconId`: The stable icon identity resolved by the Scene View. | — | — | +| [`public void Inno.Editor.Rendering.EditorGizmoFrame.Line(Inno.Core.Mathematics.Vector3 start, Inno.Core.Mathematics.Vector3 end)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L220) | Appends a world space line to the current gizmo frame.
`start`: The start consumed by line; ownership remains with the caller unless explicitly stated otherwise.
`end`: The end consumed by line; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoFrame.EditorGizmoFrame()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorGizmoProvider.cs#L176) | Inherited or compiler-provided contract. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorPreviewHandle : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Identifies one preview texture owned by a specific rendering-device generation. | — | — | +| [`public Inno.Editor.Rendering.EditorPreviewHandle.EditorPreviewHandle(ulong value, uint deviceGeneration, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L28) | Creates one generation-scoped preview handle.
`value`: Non-zero preview identity.
`deviceGeneration`: Non-zero rendering-device generation.
`pixelWidth`: Positive source width.
`pixelHeight`: Positive source height. | — | — | +| [`public ulong Inno.Editor.Rendering.EditorPreviewHandle.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L47) | Gets the opaque preview identity. | — | — | +| [`public uint Inno.Editor.Rendering.EditorPreviewHandle.deviceGeneration { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L52) | Gets the rendering-device generation that owns this handle. | — | — | +| [`public int Inno.Editor.Rendering.EditorPreviewHandle.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L57) | Gets source width in pixels. | — | — | +| [`public int Inno.Editor.Rendering.EditorPreviewHandle.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L62) | Gets source height in pixels. | — | — | +| [`public bool Inno.Editor.Rendering.EditorPreviewHandle.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L67) | Gets whether this handle identifies a usable generation-scoped preview. | — | — | +| [`public override string Inno.Editor.Rendering.EditorPreviewHandle.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorPreviewHandle.operator !=(Inno.Editor.Rendering.EditorPreviewHandle left, Inno.Editor.Rendering.EditorPreviewHandle right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorPreviewHandle.operator ==(Inno.Editor.Rendering.EditorPreviewHandle left, Inno.Editor.Rendering.EditorPreviewHandle right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorPreviewHandle.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorPreviewHandle.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorPreviewHandle.Equals(Inno.Editor.Rendering.EditorPreviewHandle other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L11) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public interface Inno.Editor.Rendering.IEditorPreviewService`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L73) | Provides one shared preview texture bridge for browsers, inspectors, and document canvases. | — | — | +| [`uint Inno.Editor.Rendering.IEditorPreviewService.deviceGeneration { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L78) | Gets the active rendering-device generation. | — | — | +| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryRender(Inno.Editor.Rendering.EditorViewportComposition composition, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L92) | Submits an isolated rendering composition through the shared offscreen viewport bridge.
`composition`: Invocation-local layers; the host retains no contributor instances after the frame.
`handle`: Receives a current-generation preview once its target is resident. | True when the completed previous output can be drawn; false during initial allocation or resize. | — | +| [`void Inno.Editor.Rendering.IEditorPreviewService.ReleaseRendered(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L103) | Releases an offscreen preview, including one which has not produced its first handle.
`viewportId`: The same consumer-qualified identity submitted in the composition. | — | — | +| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryGetTexture(Inno.Rendering.Assets.TextureAsset texture, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L117) | Tries to resolve a standalone texture preview without blocking target compilation.
`texture`: Texture asset to preview.
`handle`: Receives a generation-scoped handle when ready. | when the preview is ready. | — | +| [`bool Inno.Editor.Rendering.IEditorPreviewService.TryGetTextureArtifact(Inno.Rendering.Assets.RenderTextureArtifactReference texture, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L140) | Tries to resolve a named texture artifact preview without blocking target compilation.
`texture`: Stable named texture artifact reference.
`pixelWidth`: Positive source width.
`pixelHeight`: Positive source height.
`handle`: Receives a generation-scoped handle when ready. | when the preview is ready. | — | +| [`void Inno.Editor.Rendering.IEditorPreviewService.Draw(Inno.Editor.Rendering.EditorPreviewHandle handle, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L156) | Draws one current-generation preview into the active presentation surface.
`handle`: Preview handle returned by this service.
`logicalSize`: Positive destination size in logical pixels. | — | — | +| [`bool Inno.Editor.Rendering.IEditorPreviewService.Release(Inno.Editor.Rendering.EditorPreviewHandle handle)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L170) | Releases one cached preview registration.
`handle`: Preview handle returned by this service. | when the handle was active and released. | — | +| [`void Inno.Editor.Rendering.IEditorPreviewService.ReleaseAll()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorPreviewService.cs#L175) | Releases every cached preview registration. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorRenderingModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L22) | Composes reloadable rendering-model contributors while retaining only opaque presentation outputs. | — | — | +| [`public Inno.Editor.Rendering.EditorRenderingModule.EditorRenderingModule(Inno.Editor.Rendering.IEditorRenderingHost host, Inno.Editor.Interactions.EditorInteractions interactions, Inno.Extensibility.Types.TypeCatalog types)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L56) | Creates the composition host around stable rendering and interaction services.
`host`: Host-owned target and opaque texture bridge.
`interactions`: Shared Editor interaction and selection service.
`types`: The type catalog that owns viewport contributor generations. | — | — | +| [`public Inno.Editor.Rendering.EditorGizmoFrame Inno.Editor.Rendering.EditorRenderingModule.CollectGizmos(string viewportId, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L84) | Collects Editor-only icons and bounds for the current Scene viewport.
`viewportId`: The Scene viewport identity.
`pixelWidth`: The viewport width.
`pixelHeight`: The viewport height. | Transient gizmo primitives for the submitted frame. | — | +| [`public bool Inno.Editor.Rendering.EditorRenderingModule.HasContributors(Inno.Editor.Rendering.EditorViewportKindId kind)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L110) | Gets whether the current extension generation contributes to one viewport purpose.
`kind`: Open viewport purpose. | when at least one contributor is registered. | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetOutputInput(string viewportId, Inno.Rendering.Runtime.RenderOutputInput input)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L121) | Supplies viewport-local input captured by the output panel for this frame.
`viewportId`: Stable output identity.
`input`: Physical-pixel input snapshot. | — | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetOutputRoute(string viewportId, Inno.Rendering.Runtime.RenderOutputRoute? route)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L138) | Sets the exact model contributor order for an output with several applicable models.
`viewportId`: Stable output identity.
`route`: Explicit contributor route, or null for single-model selection. | — | — | +| [`public string? Inno.Editor.Rendering.EditorRenderingModule.GetCompositionError(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L158) | Gets the most recent isolated contribution or composition failure for one stable viewport.
`viewportId`: Stable panel viewport identity. | The failure message, or null when no current failure exists. | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationState Inno.Editor.Rendering.EditorRenderingModule.GetNavigationState(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L174) | Gets the host-owned neutral navigation state for one stable Editor viewport.
`viewportId`: Stable panel viewport identity. | The reusable navigation state owned by the Editor host. | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetContentScope(string viewportId, Inno.References.ContentReadScope content)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L195) | Sets the explicit ordered host content visible to one Editor viewport.
`viewportId`: Stable panel viewport identity.
`content`: Current frame-safe content scope. | — | — | +| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TryConfigureNavigation(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorViewportNavigationProfile profile)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L227) | Queries the selected controller's navigation contract before viewport input is processed.
`kind`: Open viewport purpose.
`viewportId`: Stable panel viewport identity.
`pixelWidth`: Positive target width.
`pixelHeight`: Positive target height.
`profile`: Receives the selected controller profile. | True when a contributor returned a usable navigation profile and became the controller. | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.SetPresentation(string viewportId, Inno.Editor.Rendering.EditorViewportPresentation presentation)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L275) | Sets presentation preferences supplied to every contributor for one Editor viewport.
`viewportId`: Stable panel viewport identity.
`presentation`: Current host-owned presentation preferences. | — | — | +| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TryGetManipulationSpace(string viewportId, out Inno.Editor.Rendering.EditorViewportManipulationSpace space)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L296) | Tries to get the manipulation space from the selected controller's latest contribution.
`viewportId`: Stable panel viewport identity.
`space`: Receives the latest exact view/projection contract. | when the selected controller supplied a manipulation space. | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.DrawControllerToolbar(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L319) | Draws toolbar controls owned by the selected model controller.
`kind`: Open viewport purpose.
`viewportId`: Stable panel viewport identity.
`pixelWidth`: Current target width.
`pixelHeight`: Current target height. | — | — | +| [`public bool Inno.Editor.Rendering.EditorRenderingModule.TrySubmit(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, out Inno.Editor.Rendering.EditorViewportOutput output)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L368) | Builds, composes, submits, and returns one host-owned offscreen viewport.
`kind`: Open viewport purpose.
`viewportId`: Stable panel viewport identity.
`pixelWidth`: Positive target width.
`pixelHeight`: Positive target height.
`output`: Receives the opaque viewport output. | when at least one model contribution was accepted. | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.HandlePointer(Inno.Editor.Rendering.EditorViewportKindId kind, string viewportId, int pixelWidth, int pixelHeight, float x, float y, int button)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L518) | Forwards a normalized click to the selected model controller.
`kind`: Open viewport purpose.
`viewportId`: Stable panel viewport identity.
`pixelWidth`: Current target width.
`pixelHeight`: Current target height.
`x`: Normalized horizontal position.
`y`: Normalized vertical position.
`button`: Platform-independent pointer button index. | — | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.Draw(Inno.Editor.Rendering.EditorViewportOutput output, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L558) | Draws a ready output in the current panel.
`output`: Opaque output returned by .
`logicalSize`: Destination size in logical UI pixels. | — | — | +| [`public void Inno.Editor.Rendering.EditorRenderingModule.Release(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L569) | Stops retaining one viewport target.
`viewportId`: Stable viewport identity. | — | — | +| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L587) | Initializes this feature when its owning runtime becomes active.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L599) | Advances this feature using the current runtime state.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L654) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Rendering.EditorRenderingModule.OnDispose()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorRenderingModule.cs#L672) | Releases resources retained by this feature after it has stopped. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportKindId : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Identifies one open Editor viewport purpose without prescribing rendering semantics. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportKindId.EditorViewportKindId(string value)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L24) | Creates a stable viewport purpose identifier.
`value`: Globally stable viewport purpose. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportKindId.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L33) | Gets the stable viewport purpose. | — | — | +| [`public bool Inno.Editor.Rendering.EditorViewportKindId.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L38) | Gets whether this identifier is usable. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportKindId.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L46) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportKindId.operator !=(Inno.Editor.Rendering.EditorViewportKindId left, Inno.Editor.Rendering.EditorViewportKindId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportKindId.operator ==(Inno.Editor.Rendering.EditorViewportKindId left, Inno.Editor.Rendering.EditorViewportKindId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportKindId.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportKindId.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportKindId.Equals(Inno.Editor.Rendering.EditorViewportKindId other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L16) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L53) | Marks a reloadable rendering-model contributor for one Editor viewport purpose. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.EditorViewportContributorExtensionAttribute(string id, string kind, int order = 0, int controllerPriority = 0)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L70) | Creates a viewport contributor declaration.
`id`: Globally stable contributor identity.
`kind`: Open viewport purpose handled by the contributor.
`order`: Ascending model-composition order within the viewport.
`controllerPriority`: Priority used to select the contributor that owns navigation, tools, and pointer interaction. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L87) | Gets the globally stable contributor identity. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportKindId Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.kind { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L92) | Gets the open viewport purpose handled by the contributor. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.order { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L97) | Gets ascending model-composition order. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportContributorExtensionAttribute.controllerPriority { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L102) | Gets the priority used to select the viewport interaction controller. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L108) | Supplies frame-only Editor interaction and output dimensions to a contributor. | — | — | +| [`public Inno.Editor.Core.EditorContext Inno.Editor.Rendering.EditorViewportContext.editor { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L149) | Gets the current Editor context. | — | — | +| [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.Rendering.EditorViewportContext.interactions { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L154) | Gets the shared Editor interaction and selection service. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportKindId Inno.Editor.Rendering.EditorViewportContext.kind { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L159) | Gets the open viewport purpose. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportContext.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L164) | Gets the stable panel viewport identity. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportContext.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L169) | Gets target width in physical pixels. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L174) | Gets target height in physical pixels. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationState Inno.Editor.Rendering.EditorViewportContext.navigation { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L179) | Gets host-owned neutral navigation state that the selected controller can map to its camera model. | — | — | +| [`public Inno.References.ContentReadScope Inno.Editor.Rendering.EditorViewportContext.content { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L184) | Gets the explicit ordered host content visible to this viewport. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportPresentation Inno.Editor.Rendering.EditorViewportContext.presentation { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L189) | Gets host-selected presentation preferences for this viewport. | — | — | +| [`public Inno.Rendering.Runtime.IViewContentCollector Inno.Editor.Rendering.EditorViewportContext.viewContent { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L194) | Gets the active generation's model-independent world-content collector. | — | — | +| [`public ulong Inno.Editor.Rendering.EditorViewportContext.frameIndex { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L199) | Gets the frame index shared by all Editor views in this output frame. | — | — | +| [`public Inno.Rendering.Runtime.RenderOutputInput Inno.Editor.Rendering.EditorViewportContext.input { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L203) | Gets input located in this viewport's physical pixels. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportPresentation : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Describes host-selected presentation preferences without prescribing rendering behavior. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportPresentation.EditorViewportPresentation(Inno.Core.Mathematics.Color backgroundColor, float pixelDensity = 1)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L220) | Creates presentation preferences for one Editor viewport.
`backgroundColor`: Linear clear color preferred by the host panel.
`pixelDensity`: Physical render pixels per logical presentation unit. | — | — | +| [`public Inno.Core.Mathematics.Color Inno.Editor.Rendering.EditorViewportPresentation.backgroundColor { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L233) | Gets the linear clear color preferred by the host panel. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportPresentation.pixelDensity { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L238) | Gets physical render pixels per logical presentation unit. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportPresentation.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportPresentation.operator !=(Inno.Editor.Rendering.EditorViewportPresentation left, Inno.Editor.Rendering.EditorViewportPresentation right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportPresentation.operator ==(Inno.Editor.Rendering.EditorViewportPresentation left, Inno.Editor.Rendering.EditorViewportPresentation right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportPresentation.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportPresentation.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportPresentation.Equals(Inno.Editor.Rendering.EditorViewportPresentation other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L209) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public enum Inno.Editor.Rendering.EditorViewportManipulationPlane : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L244) | Selects whether transform handles use all axes or a contributor-declared planar orientation. | — | — | +| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.Spatial = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L249) | Keeps the full three-dimensional translation, rotation, and scale controls. | — | — | +| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.XY = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L254) | Uses the X/Y translation and scale axes with the Z rotation handle. | — | — | +| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.XZ = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L259) | Uses the X/Z translation and scale axes with the Y rotation handle. | — | — | +| [`Inno.Editor.Rendering.EditorViewportManipulationPlane.YZ = 3`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L264) | Uses the Y/Z translation and scale axes with the X rotation handle. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportManipulationSpace : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Describes the exact backend-neutral view and projection used to draw a viewport so host tools can manipulate selected scene transforms without knowing the controller's camera model. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportManipulationSpace.EditorViewportManipulationSpace(Inno.Core.Mathematics.Matrix viewMatrix, Inno.Core.Mathematics.Matrix projectionMatrix, bool isOrthographic, Inno.Editor.Rendering.EditorViewportManipulationPlane plane = Inno.Editor.Rendering.EditorViewportManipulationPlane.Spatial)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L291) | Creates a manipulation space matching one rendered viewport frame.
`viewMatrix`: World-to-view matrix used by the submitted frame.
`projectionMatrix`: View-to-clip matrix used by the submitted frame.
`isOrthographic`: Whether the projection is orthographic; this does not imply planar manipulation.
`plane`: The contributor-selected transform handle orientation, independent of projection. | — | `System.ArgumentOutOfRangeException`: The plane is not one of the defined neutral manipulation orientations. | +| [`public Inno.Core.Mathematics.Matrix Inno.Editor.Rendering.EditorViewportManipulationSpace.viewMatrix { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L308) | Gets the world-to-view matrix used by the submitted frame. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Editor.Rendering.EditorViewportManipulationSpace.projectionMatrix { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L313) | Gets the view-to-clip matrix used by the submitted frame. | — | — | +| [`public bool Inno.Editor.Rendering.EditorViewportManipulationSpace.isOrthographic { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L318) | Gets whether the submitted frame used an orthographic projection. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportManipulationPlane Inno.Editor.Rendering.EditorViewportManipulationSpace.plane { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L323) | Gets the contributor-declared neutral manipulation plane, or Spatial for all axes. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportManipulationSpace.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportManipulationSpace.operator !=(Inno.Editor.Rendering.EditorViewportManipulationSpace left, Inno.Editor.Rendering.EditorViewportManipulationSpace right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportManipulationSpace.operator ==(Inno.Editor.Rendering.EditorViewportManipulationSpace left, Inno.Editor.Rendering.EditorViewportManipulationSpace right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportManipulationSpace.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportManipulationSpace.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportManipulationSpace.Equals(Inno.Editor.Rendering.EditorViewportManipulationSpace other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L271) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportContribution`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L329) | Returns one rendering-model contribution without exposing a GPU backend. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportContribution.EditorViewportContribution(Inno.Rendering.Runtime.RenderFrameData data, Inno.Rendering.Assets.RenderPipelineAsset? pipeline = null, Inno.Rendering.RenderTextureFormat targetFormat = Inno.Rendering.RenderTextureFormat.RGBA8Srgb, Inno.Editor.Rendering.EditorViewportManipulationSpace? manipulationSpace = null)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L346) | Creates a viewport model contribution.
`data`: Pipeline-defined frame-only data.
`pipeline`: Contributor-selected pipeline, or null for the project default.
`targetFormat`: Presentation target format expected by the pipeline.
`manipulationSpace`: Optional exact view/projection contract for host-owned transform manipulation tools. | — | — | +| [`public Inno.Rendering.Runtime.RenderFrameData Inno.Editor.Rendering.EditorViewportContribution.data { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L361) | Gets pipeline-defined frame-only data. | — | — | +| [`public Inno.Rendering.Assets.RenderPipelineAsset? Inno.Editor.Rendering.EditorViewportContribution.pipeline { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L366) | Gets the selected pipeline, or null for the project default. | — | — | +| [`public Inno.Rendering.RenderTextureFormat Inno.Editor.Rendering.EditorViewportContribution.targetFormat { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L371) | Gets the presentation target format expected by the pipeline. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportManipulationSpace? Inno.Editor.Rendering.EditorViewportContribution.manipulationSpace { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L376) | Gets the optional exact view/projection contract used by host-owned transform manipulation tools. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportPointerContext`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L382) | Supplies normalized pointer interaction over one rendered viewport. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportContext Inno.Editor.Rendering.EditorViewportPointerContext.viewport { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L399) | Gets the owning frame-only viewport context. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportPointerContext.x { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L404) | Gets normalized horizontal pointer position. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportPointerContext.y { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L409) | Gets normalized vertical pointer position. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportPointerContext.button { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L414) | Gets the platform-independent pointer button index. | — | — | +| [`public abstract class Inno.Editor.Rendering.EditorViewportContributor`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L420) | Contributes one rendering model while the host owns viewport composition, targets, and presentation. | — | — | +| [`protected Inno.Editor.Rendering.EditorViewportContributor.EditorViewportContributor()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L425) | Creates a parameterless reloadable viewport contributor. | — | — | +| [`public abstract bool Inno.Editor.Rendering.EditorViewportContributor.CanContribute(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L438) | Determines whether this rendering model participates in the supplied viewport content.
`context`: Frame-only Editor, navigation, content, and viewport context. | when this contributor can build a valid model contribution. | — | +| [`public virtual Inno.Editor.Rendering.EditorViewportNavigationProfile Inno.Editor.Rendering.EditorViewportContributor.ConfigureNavigation(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L449) | Configures neutral host navigation before input is processed and a request is built.
`context`: Frame-only Editor, navigation, content, and viewport context. | The controller's current navigation capabilities and optional selection focus bound. | — | +| [`public abstract Inno.Editor.Rendering.EditorViewportContribution Inno.Editor.Rendering.EditorViewportContributor.Build(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L461) | Builds one model-neutral render contribution for the current frame.
`context`: Frame-only Editor and viewport context. | Contributor-selected pipeline and frame data. | — | +| [`public virtual void Inno.Editor.Rendering.EditorViewportContributor.DrawToolbar(Inno.Editor.Rendering.EditorViewportContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L469) | Draws optional controller-specific toolbar controls when this contributor owns viewport interaction.
`context`: Frame-only Editor and viewport context. | — | — | +| [`public virtual void Inno.Editor.Rendering.EditorViewportContributor.HandlePointer(Inno.Editor.Rendering.EditorViewportPointerContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportContributor.cs#L479) | Handles one pointer click when this contributor owns viewport interaction.
`context`: Normalized frame-only pointer context. | — | — | +| [`public enum Inno.Editor.Rendering.EditorViewportProjection : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L10) | Identifies the projection family represented by neutral Editor viewport navigation state. | — | — | +| [`Inno.Editor.Rendering.EditorViewportProjection.Orthographic = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L15) | Uses a parallel projection controlled by an orthographic half-height. | — | — | +| [`Inno.Editor.Rendering.EditorViewportProjection.Perspective = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L20) | Uses a perspective projection controlled by a vertical field of view. | — | — | +| [`public enum Inno.Editor.Rendering.EditorViewportNavigationMode : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L26) | Identifies the active interaction model without prescribing a rendering camera type. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Planar = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L31) | Pans and zooms over a provider-defined plane. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Orbit = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L36) | Rotates around a focus pivot and dollies along the view direction. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationMode.Fly = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L41) | Moves and looks freely from the current view position. | — | — | +| [`public enum Inno.Editor.Rendering.EditorViewportNavigationCapabilities : int`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L48) | Declares navigation operations supported by one viewport interaction controller. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.None = 0`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L53) | Disables host-owned navigation. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Pan = 1`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L58) | Allows translating the view parallel to its image plane. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Zoom = 2`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L63) | Allows changing orthographic size or perspective focus distance. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Planar = Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Pan \| Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Zoom`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L68) | Allows the complete plane-oriented pan and zoom interaction model. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Orbit = 4`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L73) | Allows pivot-oriented orbit and dolly. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.Fly = 8`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L78) | Allows free-look movement. | — | — | +| [`Inno.Editor.Rendering.EditorViewportNavigationCapabilities.FrameSelection = 16`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L83) | Allows framing a provider-supplied or host-derived selection bound. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportNavigationProfileId : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Identifies a provider-defined navigation profile across reload generations. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationProfileId.EditorViewportNavigationProfileId(string value)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L97) | Creates a stable profile identifier.
`value`: Globally stable profile identity. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportNavigationProfileId.value { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L106) | Gets the stable profile identity. | — | — | +| [`public bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.isValid { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L111) | Gets whether this identifier contains a usable value. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportNavigationProfileId.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L119) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.operator !=(Inno.Editor.Rendering.EditorViewportNavigationProfileId left, Inno.Editor.Rendering.EditorViewportNavigationProfileId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.operator ==(Inno.Editor.Rendering.EditorViewportNavigationProfileId left, Inno.Editor.Rendering.EditorViewportNavigationProfileId right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportNavigationProfileId.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportNavigationProfileId.Equals(Inno.Editor.Rendering.EditorViewportNavigationProfileId other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L89) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportFocusBounds : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Describes a world-space sphere that can be framed by host navigation. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportFocusBounds.EditorViewportFocusBounds(Inno.Core.Mathematics.Vector3 center, float radius)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L142) | Creates a finite focus bound.
`center`: World-space focus center.
`radius`: Non-negative world-space radius. | — | `System.ArgumentException`: Thrown when the center contains a non-finite value.
`System.ArgumentOutOfRangeException`: Thrown when the radius is negative or non-finite. | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportFocusBounds.center { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L157) | Gets the world-space focus center. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportFocusBounds.radius { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L162) | Gets the non-negative world-space radius. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportFocusBounds.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportFocusBounds.operator !=(Inno.Editor.Rendering.EditorViewportFocusBounds left, Inno.Editor.Rendering.EditorViewportFocusBounds right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportFocusBounds.operator ==(Inno.Editor.Rendering.EditorViewportFocusBounds left, Inno.Editor.Rendering.EditorViewportFocusBounds right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportFocusBounds.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportFocusBounds.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportFocusBounds.Equals(Inno.Editor.Rendering.EditorViewportFocusBounds other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L125) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportNavigationProfile`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L170) | Describes how the host may navigate one provider-owned viewport without exposing the provider's camera model. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationProfile.EditorViewportNavigationProfile(Inno.Editor.Rendering.EditorViewportNavigationProfileId id, Inno.Editor.Rendering.EditorViewportNavigationCapabilities capabilities, Inno.Editor.Rendering.EditorViewportNavigationMode defaultMode)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L184) | Creates a provider-defined navigation profile.
`id`: Stable profile identity.
`capabilities`: Operations accepted by the provider.
`defaultMode`: Preferred mode when current state is unsupported. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationProfileId Inno.Editor.Rendering.EditorViewportNavigationProfile.id { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L214) | Gets the stable profile identity. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationCapabilities Inno.Editor.Rendering.EditorViewportNavigationProfile.capabilities { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L219) | Gets the operations accepted by this provider. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationMode Inno.Editor.Rendering.EditorViewportNavigationProfile.defaultMode { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L224) | Gets the mode selected when current state is not supported. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationProfile.worldUp { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L229) | Gets or sets the provider-defined world-up direction. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportFocusBounds? Inno.Editor.Rendering.EditorViewportNavigationProfile.focusBounds { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L234) | Gets or sets the optional current selection bound. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.rotationSensitivity { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L239) | Gets or sets pointer rotation sensitivity in radians per pixel. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.zoomSensitivity { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L244) | Gets or sets exponential wheel zoom sensitivity. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.minimumOrthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L249) | Gets or sets minimum orthographic half-height. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.maximumOrthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L254) | Gets or sets maximum orthographic half-height. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.minimumFocusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L259) | Gets or sets minimum orbit or framing distance. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.maximumFocusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L264) | Gets or sets maximum orbit or framing distance. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.fastMovementMultiplier { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L269) | Gets or sets the multiplier used while fast fly movement is requested. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationProfile.framePadding { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L274) | Gets or sets additional framing space around selection bounds. | — | — | +| [`public static Inno.Editor.Rendering.EditorViewportNavigationProfile Inno.Editor.Rendering.EditorViewportNavigationProfile.disabled { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L279) | Gets a disabled fallback profile used when no provider navigation contract exists. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportNavigationState`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L303) | Stores rendering-model-neutral navigation state for one Editor viewport across provider reloads. | — | — | +| [`public bool Inno.Editor.Rendering.EditorViewportNavigationState.isInitialized { get; private set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L324) | Gets whether a provider or restored panel state initialized this state. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportProjection Inno.Editor.Rendering.EditorViewportNavigationState.projection { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L329) | Gets or sets the current projection family. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationMode Inno.Editor.Rendering.EditorViewportNavigationState.mode { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L344) | Gets or sets the active host navigation mode. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationState.position { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L359) | Gets or sets the provider-defined world-space view position. | — | — | +| [`public Inno.Core.Mathematics.Quaternion Inno.Editor.Rendering.EditorViewportNavigationState.rotation { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L373) | Gets or sets the normalized provider-defined world-space view rotation. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Editor.Rendering.EditorViewportNavigationState.pivot { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L389) | Gets or sets the world-space orbit and framing pivot. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.orthographicSize { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L403) | Gets or sets the positive orthographic half-height in provider world units. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.fieldOfView { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L418) | Gets or sets the perspective vertical field of view in degrees. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.nearClip { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L433) | Gets or sets the positive near clipping distance. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.farClip { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L448) | Gets or sets the far clipping distance greater than the near distance. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.focusDistance { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L463) | Gets or sets the positive orbit and framing distance. | — | — | +| [`public float Inno.Editor.Rendering.EditorViewportNavigationState.movementSpeed { get; set; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L478) | Gets or sets the positive base movement speed in world units per second. | — | — | +| [`public void Inno.Editor.Rendering.EditorViewportNavigationState.ConfigureOrthographic(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, float orthographicSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L501) | Atomically initializes an orthographic navigation view.
`position`: Provider-defined world-space position.
`rotation`: Provider-defined world-space rotation.
`orthographicSize`: Positive orthographic half-height. | — | — | +| [`public void Inno.Editor.Rendering.EditorViewportNavigationState.ConfigurePerspective(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, float fieldOfView, float nearClip, float farClip)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L538) | Atomically initializes a perspective navigation view.
`position`: Provider-defined world-space position.
`rotation`: Provider-defined world-space rotation.
`fieldOfView`: Perspective vertical field of view in degrees.
`nearClip`: Positive near clipping distance.
`farClip`: Far clipping distance greater than . | — | — | +| [`public Inno.Editor.Rendering.EditorViewportNavigationState.EditorViewportNavigationState()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportNavigation.cs#L303) | Inherited or compiler-provided contract. | — | — | +| [`public readonly record struct Inno.Editor.Rendering.EditorViewportOutput : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Identifies a renderer-owned editor viewport image through an opaque presentation token. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportOutput.EditorViewportOutput(string viewportId, Inno.Adapter.Presentation.PresentationTextureHandle texture, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L32) | Creates an editor viewport output snapshot.
`viewportId`: Stable viewport identity.
`texture`: Opaque presentation token, or an invalid token while warming up.
`pixelWidth`: Current target width.
`pixelHeight`: Current target height. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportOutput.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L50) | Gets the stable viewport identity. | — | — | +| [`public Inno.Adapter.Presentation.PresentationTextureHandle Inno.Editor.Rendering.EditorViewportOutput.texture { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L55) | Gets the opaque presentation token. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportOutput.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L60) | Gets the current target width. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportOutput.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L65) | Gets the current target height. | — | — | +| [`public bool Inno.Editor.Rendering.EditorViewportOutput.isReady { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L70) | Gets whether a completed target can be drawn. | — | — | +| [`public override string Inno.Editor.Rendering.EditorViewportOutput.ToString()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportOutput.operator !=(Inno.Editor.Rendering.EditorViewportOutput left, Inno.Editor.Rendering.EditorViewportOutput right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Rendering.EditorViewportOutput.operator ==(Inno.Editor.Rendering.EditorViewportOutput left, Inno.Editor.Rendering.EditorViewportOutput right)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Rendering.EditorViewportOutput.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Rendering.EditorViewportOutput.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Rendering.EditorViewportOutput.Equals(Inno.Editor.Rendering.EditorViewportOutput other)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L15) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public interface Inno.Editor.Rendering.IEditorRenderingHost`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L76) | Bridges editor extensions to a host-owned render request sink and opaque texture presenter. | — | — | +| [`Inno.Rendering.Runtime.IViewContentCollector Inno.Editor.Rendering.IEditorRenderingHost.viewContent { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L81) | Gets the active generation's model-independent world-content collector. | — | — | +| [`ulong Inno.Editor.Rendering.IEditorRenderingHost.currentFrameIndex { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L86) | Gets the current output frame index shared by Editor views. | — | — | +| [`Inno.Editor.Rendering.EditorViewportOutput Inno.Editor.Rendering.IEditorRenderingHost.Submit(Inno.Editor.Rendering.EditorViewportComposition composition)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L97) | Submits or updates one composed offscreen editor viewport.
`composition`: Complete ordered model composition for the current frame. | The current presentation output, which can be warming up for one frame. | — | +| [`void Inno.Editor.Rendering.IEditorRenderingHost.Draw(Inno.Editor.Rendering.EditorViewportOutput output, System.Numerics.Vector2 logicalSize)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L108) | Draws a ready viewport output inside the current ImGui window.
`output`: Output returned by .
`logicalSize`: Destination size in logical UI pixels. | — | — | +| [`void Inno.Editor.Rendering.IEditorRenderingHost.Release(string viewportId)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L119) | Releases one viewport and queues its GPU target for frame-safe destruction.
`viewportId`: Stable viewport identity. | — | — | +| [`void Inno.Editor.Rendering.IEditorRenderingHost.ReleaseAll()`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L124) | Releases every viewport owned by this editor host service. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportLayer`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L130) | Describes one model-neutral layer in an Editor viewport composition. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportLayer.EditorViewportLayer(string contributorId, Inno.Rendering.Assets.RenderPipelineAsset? pipeline, Inno.Rendering.Runtime.RenderFrameData data, int order)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L147) | Creates one immutable model-neutral viewport layer.
`contributorId`: Stable identity of the rendering-model contributor that produced the layer.
`pipeline`: Contributor-selected pipeline, or null for the project default.
`data`: Contributor-defined frame-only data.
`order`: Ascending model-composition order. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportLayer.contributorId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L164) | Gets the stable identity of the rendering-model contributor. | — | — | +| [`public Inno.Rendering.Assets.RenderPipelineAsset? Inno.Editor.Rendering.EditorViewportLayer.pipeline { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L169) | Gets the contributor-selected pipeline, or null for the project default. | — | — | +| [`public Inno.Rendering.Runtime.RenderFrameData Inno.Editor.Rendering.EditorViewportLayer.data { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L174) | Gets contributor-defined frame-only data. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportLayer.order { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L179) | Gets ascending model-composition order. | — | — | +| [`public sealed class Inno.Editor.Rendering.EditorViewportComposition`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L185) | Describes a complete ordered set of rendering-model layers targeting one Editor viewport. | — | — | +| [`public Inno.Editor.Rendering.EditorViewportComposition.EditorViewportComposition(string viewportId, int pixelWidth, int pixelHeight, Inno.Rendering.RenderTextureFormat targetFormat, System.Collections.Generic.IEnumerable layers)`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L207) | Creates an immutable viewport composition.
`viewportId`: Stable panel viewport identity.
`pixelWidth`: Positive target width.
`pixelHeight`: Positive target height.
`targetFormat`: Shared presentation target format required by every layer.
`layers`: Ordered non-empty rendering-model layer collection. | — | — | +| [`public string Inno.Editor.Rendering.EditorViewportComposition.viewportId { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L241) | Gets the stable viewport identity. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportComposition.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L246) | Gets the target width. | — | — | +| [`public int Inno.Editor.Rendering.EditorViewportComposition.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L251) | Gets the target height. | — | — | +| [`public Inno.Rendering.RenderTextureFormat Inno.Editor.Rendering.EditorViewportComposition.targetFormat { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L256) | Gets the shared presentation target format. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Rendering.EditorViewportComposition.layers { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/EditorViewportOutput.cs#L261) | Gets the ordered rendering-model layers. | — | — | +| [`public sealed class Inno.Editor.Rendering.PipelineDocuments : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L18) | Edits Pipeline sources and typed extension settings without changing canonical rendering assets before Save. | — | — | +| [`public Inno.Assets.Pipeline.AssetPipeline Inno.Editor.Rendering.PipelineDocuments.assets { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L26) | Gets the authoritative asset pipeline used by source inspectors. | — | — | +| [`public Inno.Core.Serialization.SerializationRegistry Inno.Editor.Rendering.PipelineDocuments.serialization { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L32) | Gets the current owner serialization registry. | — | — | +| [`public Inno.Editor.Assets.AssetDraftDocuments Inno.Editor.Rendering.PipelineDocuments.drafts { get; }`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L38) | Gets the source draft owner shared by Pipeline inspectors. | — | — | +| [`public System.Guid Inno.Editor.Rendering.PipelineDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L61) | Opens a native Pipeline source in the shared document service, including failed imports.
`path`: Mounted source path. | The persistent source identity used by draft operations. | — | +| [`public Inno.Rendering.Assets.RenderPipelineAsset Inno.Editor.Rendering.PipelineDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L71) | Reads a detached current-generation Pipeline value; its referenced canonical assets must not be mutated.
`assetId`: Open Pipeline identity. | The unsaved native Pipeline value. | — | +| [`public void Inno.Editor.Rendering.PipelineDocuments.Replace(System.Guid assetId, Inno.Rendering.Assets.RenderPipelineAsset candidate, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L84) | Changes a draft through shared History without publishing it to Scene/Game.
`assetId`: Open Pipeline identity.
`candidate`: Detached edited value.
`mergeKey`: The current gesture key, or null for a discrete edit. | — | — | +| [`public void Inno.Editor.Rendering.PipelineDocuments.ReplaceSettings(System.Guid assetId, TSettings settings, string? mergeKey = null) where TSettings : class, Inno.Core.Serialization.ISerializable`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L104) | Captures typed Pipeline settings with this source owner's complete reference and dependency context.
`assetId`: Open Pipeline identity.
`settings`: Detached edited settings.
`mergeKey`: The current gesture key, or null for a discrete edit.
`TSettings`: Current plugin settings contract. | — | — | +| [`public Inno.Core.Serialization.ISerializable? Inno.Editor.Rendering.PipelineDocuments.RestoreSettings(Inno.Rendering.Assets.SerializedRenderExtensionState state)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L126) | Restores detached settings using the current type and serialization generation.
`state`: Neutral settings retaining their stable type identity and properties. | The detached settings, or null when the extension is temporarily unavailable. | — | +| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L145) | Initializes this feature when its owning runtime becomes active.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L152) | Advances this feature using the current runtime state.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Rendering.PipelineDocuments.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs#L159) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | ## 项目依赖 -- [Inno.Editor.Assets](Inno.Editor.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Inspection](Inno.Editor.Inspection.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Runtime](../rendering/Inno.Rendering.Runtime.md):公开引用边界由实际签名核对。 -- [Inno.Core.Mathematics](../core/Inno.Core.Mathematics.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Presentation](../platform/Inno.Adapter.Presentation.md):公开引用边界由实际签名核对。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.References](../references/Inno.References.md):公开引用边界由实际签名核对。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Core.Graphs](../core/Inno.Core.Graphs.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Rendering.Assets](../rendering/Inno.Rendering.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Rendering.Assets.Authoring](../rendering/Inno.Rendering.Assets.Authoring.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Presentation` | 公开签名依赖 | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.References` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Core.Graphs` | 公开签名依赖 | +| `Inno.Core.Identity` | 公开签名依赖 | +| `Inno.Core.Mathematics` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Rendering.Assets.Authoring` | 公开签名依赖 | +| `Inno.Rendering.Assets` | 公开签名依赖 | +| `Inno.Rendering.Runtime` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.Editor.Assets` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Scene.md b/docs/editor/Inno.Editor.Scene.md index 3667e6825..5bf3e80bb 100644 --- a/docs/editor/Inno.Editor.Scene.md +++ b/docs/editor/Inno.Editor.Scene.md @@ -144,56 +144,17 @@ EditorScripts 显式 `using InnoEditor.Scene;` 后只看到 `IEditorSceneWorkspa ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Scene.EditorSceneWorkspaceFactory` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Editor.Scene.EditorSceneWorkspaceHost Inno.Editor.Scene.EditorSceneWorkspaceFactory.Create(Inno.Runtime.RuntimeSession runtimeSession, Inno.Assets.Pipeline.AssetPipeline assets, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Logging.LogRouter logs, Inno.Editor.Interactions.IEditorSelectionCoordinator? selection = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceFactory.cs#L44) | Creates an unattached workspace over explicitly owned Edit-session services.
返回:A host that owns both the document and Play Mode interfaces of the workspace.
`runtimeSession`:The Edit session that owns the workspace scene world.
`assets`:The authoring asset pipeline used for scene and prefab documents.
`types`:The host type catalog used for scene extension generations.
`serialization`:The host serialization registry used for scene snapshots.
`logs`:The application log router used for workspace lifecycle diagnostics.
`selection`:The optional selection coordinator used by the standalone document workspace. | — | `System.ArgumentNullException`:Thrown when a required owner is null. | -| [`public static class Inno.Editor.Scene.EditorSceneWorkspaceFactory`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceFactory.cs#L15) | Creates explicitly owned editor scene workspaces for embedded editor hosts and command-line tooling. | — | — | - -### `Inno.Editor.Scene.EditorSceneWorkspaceHost` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Scene.EditorSceneWorkspaceHost.Dispose()`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L58) | Releases the workspace and any isolated scene session that it still owns. | — | — | -| [`public Inno.Editor.Scene.IEditorGameScenePresentation Inno.Editor.Scene.EditorSceneWorkspaceHost.gamePresentation { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L48) | Gets the Edit-or-Play rendering presentation boundary owned by this host. | — | — | -| [`public Inno.Editor.Scene.IEditorScenePlayMode Inno.Editor.Scene.EditorSceneWorkspaceHost.playMode { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L53) | Gets the isolated Play Mode scene-session boundary owned by this host. | — | — | -| [`public Inno.Editor.Scene.IEditorSceneWorkspace Inno.Editor.Scene.EditorSceneWorkspaceHost.workspace { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L43) | Gets the Edit-or-Play scene presentation and persistence boundary owned by this host. | — | — | -| [`public sealed class Inno.Editor.Scene.EditorSceneWorkspaceHost : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L17) | Owns an editor scene workspace created outside the attribute-discovered editor application. | — | — | - -### `Inno.Editor.Scene.IEditorGameScenePresentation` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.References.ContentReadScope Inno.Editor.Scene.IEditorGameScenePresentation.Capture()`](../../src/composition/editor/features/Inno.Editor.Scene/Presentation/IEditorGameScenePresentation.cs#L19) | Captures one coherent game-scene presentation for the current Editor frame.
返回:A caller-owned identity scope for the Edit scenes while not playing and the isolated Play scenes after Play Mode commits. Dispose it after consumption; it retains no scene instances and stale identities never resolve to a replacement session or generation. | — | — | -| [`public interface Inno.Editor.Scene.IEditorGameScenePresentation`](../../src/composition/editor/features/Inno.Editor.Scene/Presentation/IEditorGameScenePresentation.cs#L9) | Supplies the scene set that represents the game to Editor viewport consumers without exposing runtime-session ownership. | — | — | - -### `Inno.Editor.Scene.IEditorScenePlayMode` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`System.IDisposable Inno.Editor.Scene.IEditorScenePlayMode.BeginPlayMode(Inno.Runtime.RuntimeSession runtimeSession)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorScenePlayMode.cs#L30) | Captures the editable scene set and materializes independent runtime copies in the supplied session.
返回:A lease that presents the runtime copies to editor features, prevents their persistence, and restores the Edit presentation when disposed.
`runtimeSession`:The isolated Play Mode session that receives the immutable start snapshot and owns editor-side operations performed against the runtime copies. | — | `System.ArgumentNullException`:Thrown when runtimeSession is null.
`System.InvalidOperationException`:Thrown when another Play Mode lease is active, the target world is not empty, or the snapshot cannot be materialized atomically. | -| [`public interface Inno.Editor.Scene.IEditorScenePlayMode`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorScenePlayMode.cs#L10) | Creates isolated runtime scene sessions from the current editable scene set. | — | — | - -### `Inno.Editor.Scene.IEditorSceneWorkspace` - @@ -206,24 +167,6 @@ EditorScripts 显式 `using InnoEditor.Scene;` 后只看到 `IEditorSceneWorkspa - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.CanEdit(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L37) | Gets whether a loaded scene may be changed in the current Edit or isolated Play world.
返回:true for a writable Project scene or transient scene in the presented world. Changes made during Play remain in the runtime copy and are discarded on stop.
`scene`:The scene to inspect. | — | — | -| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.IsDirty(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L63) | Gets whether an Edit scene contains unsaved serialized changes.
返回:true when an Edit scene differs from its saved baseline; runtime copies always return false because they cannot be persisted.
`scene`:The scene to inspect. | — | — | -| [`Inno.Scene.GameScene Inno.Editor.Scene.IEditorSceneWorkspace.Open(string relativePath)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L77) | Opens a scene asset additively as the active editor scene.
返回:The existing loaded instance or newly loaded scene.
`relativePath`:The source-relative scene asset path. | — | `System.InvalidOperationException`:Thrown while Play Mode runtime copies are active or when the asset cannot be opened. | -| [`string Inno.Editor.Scene.IEditorSceneWorkspace.Save(Inno.Scene.GameScene scene, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L94) | Saves a scene to its existing path or into a fallback directory.
返回:The saved source-relative path.
`scene`:The scene to save.
`currentDirectory`:The fallback asset directory for an unsaved scene. | — | `System.InvalidOperationException`:Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | -| [`string Inno.Editor.Scene.IEditorSceneWorkspace.SavePrefab(Inno.Scene.GameObject gameObject, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L134) | Captures a game object subtree as a prefab in the requested directory.
返回:The saved source-relative path.
`gameObject`:The prefab root.
`currentDirectory`:The target asset directory. | — | `System.InvalidOperationException`:Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | -| [`string Inno.Editor.Scene.IEditorSceneWorkspace.SaveToDirectory(Inno.Scene.GameScene scene, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L114) | Saves a scene into the requested asset directory.
返回:The saved source-relative path.
`scene`:The scene to save.
`currentDirectory`:The target asset directory. | — | `System.InvalidOperationException`:Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | -| [`void Inno.Editor.Scene.IEditorSceneWorkspace.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L51) | Makes one presented scene active without changing scene order.
`scene`:The loaded scene to activate. | — | `System.ArgumentNullException`:Thrown when scene is null.
`System.InvalidOperationException`:Thrown when the scene is not loaded by this workspace. | -| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.TryGetSourcePath(Inno.Scene.GameScene scene, out string relativePath)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L151) | Tries to get the current source-relative asset path of a saved scene.
返回:true when the scene is backed by a scene asset.
`scene`:The scene whose path is requested.
`relativePath`:The saved path when available. | — | — | -| [`Inno.Scene.GameScene? Inno.Editor.Scene.IEditorSceneWorkspace.activeScene { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L20) | Gets the active scene from the currently presented Edit or Play world. | — | — | -| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.canPersist { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L25) | Gets whether the currently presented scenes are authoring documents that may be persisted. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Editor.Scene.IEditorSceneWorkspace.scenes { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L15) | Gets the Edit scenes outside Play Mode or the isolated runtime copies while Play Mode is active. | — | — | -| [`public interface Inno.Editor.Scene.IEditorSceneWorkspace`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L10) | Exposes the active Edit or Play scene presentation and the persistence operations available to it. | — | — | - -### `Inno.Editor.Scene.SceneEdits` - @@ -234,8 +177,8 @@ EditorScripts 显式 `using InnoEditor.Scene;` 后只看到 `IEditorSceneWorkspa - - + + @@ -248,61 +191,90 @@ EditorScripts 显式 `using InnoEditor.Scene;` 后只看到 `IEditorSceneWorkspa - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.GameComponent Inno.Editor.Scene.SceneEdits.AddComponent(Inno.Scene.GameObject owner, System.Type componentType, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L33) | Adds one component and records its identity, stable type, index, and persistent properties.
返回:The newly attached component.
`owner`:The live GameObject receiving the component.
`componentType`:The concrete component type to create.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public Inno.Scene.GameSystem Inno.Editor.Scene.SceneEdits.AddSystem(Inno.Scene.GameScene scene, System.Type systemType, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L283) | Adds one scene system and records its identity, stable type, index, and persistent properties.
返回:The newly registered system.
`scene`:The loaded scene receiving the system.
`systemType`:The concrete system type to create.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.CanEdit(Inno.Scene.EngineObject target)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L63) | Gets whether a scene object belongs to an editable presented scene.
返回:true when its owning scene is writable.
`target`:The scene object to inspect. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.CanEdit(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L52) | Gets whether the scene is editable in the current Edit or isolated Play world.
返回:true when scene commands may change this scene. Play changes use a temporary history branch and are discarded when the Play session ends.
`scene`:The loaded scene to inspect. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.ChangeHierarchy(Inno.Scene.GameObject gameObject, System.Action mutation, string historyName = "Move GameObject", System.Collections.Generic.IReadOnlyCollection? relatedObjects = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Hierarchy.cs#L80) | Applies a hierarchy mutation and records only the affected parent and sibling-index tuples.
返回:true when at least one placement changed.
`gameObject`:The primary GameObject being moved.
`mutation`:The hierarchy mutation to execute through the world-owned operation context.
`historyName`:The user-facing history entry name.
`relatedObjects`:Additional objects whose placements the mutation may change, such as promoted children. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.ChangeProperty(Inno.Scene.EngineObject target, string propertyName, System.Action mutation, string historyName, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L210) | Applies a mutation to one serializable scene property and records only its before and after values.
返回:true when the property value changed and a history entry was recorded.
`target`:The live scene object containing the root serialized property.
`propertyName`:The exact root serialized member key.
`mutation`:The mutation that assigns the new value.
`historyName`:The user-facing history entry name.
`mergeKey`:An optional stable key for coalescing adjacent continuous edits. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.CloseScene(Inno.Scene.GameScene scene, string historyName = "Close Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L130) | Closes one loaded scene without deleting its source asset and records a reversible document change.
返回:true when the scene was closed and recorded.
`scene`:The loaded scene to close.
`historyName`:The user-facing history entry name. | — | — | -| [`public Inno.Scene.GameObject Inno.Editor.Scene.SceneEdits.CreateGameObject(Inno.Scene.GameScene scene, Inno.Scene.Components.Transform? parent = null, string historyName = "Create GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L33) | Creates a GameObject, optionally parents it, and records only the new subtree state.
返回:The newly created GameObject.
`scene`:The loaded scene that will own the object.
`parent`:The optional parent transform.
`historyName`:The user-facing history entry name. | — | — | -| [`public Inno.Scene.GameScene Inno.Editor.Scene.SceneEdits.CreateScene(string historyName = "Create Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L87) | Creates an additive scene and records a reversible document change.
返回:The newly created active scene.
`historyName`:The user-facing history entry name. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.DeleteGameObject(Inno.Scene.GameObject gameObject, string historyName = "Delete GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L150) | Deletes a GameObject subtree and records only that subtree plus incoming serialized references.
返回:true when the subtree was deleted and recorded.
`gameObject`:The live subtree root to delete.
`historyName`:The user-facing history entry name. | — | — | -| [`public Inno.Scene.GameObject Inno.Editor.Scene.SceneEdits.InstantiatePrefab(Inno.Scene.PrefabAsset prefab, Inno.Scene.GameScene scene, Inno.Scene.Components.Transform? parent = null, string historyName = "Instantiate Prefab")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L91) | Instantiates a prefab into a loaded scene and records the created subtree as one reversible edit.
返回:The instantiated prefab root.
`prefab`:The imported prefab asset to instantiate.
`scene`:The loaded scene that will own the instance.
`parent`:The optional parent transform for the instantiated root.
`historyName`:The user-facing history entry name. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.RemoveComponent(Inno.Scene.GameComponent component, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L90) | Removes the component from its scene owner and records a reversible serialized history change.
返回:true when the component was removed and recorded.
`component`:The attached non-Transform component to remove.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public bool Inno.Editor.Scene.SceneEdits.RemoveSystem(Inno.Scene.GameScene scene, Inno.Scene.GameSystem system, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L341) | Removes the system from its scene and records a reversible serialized history change.
返回:true when the system was removed and recorded.
`scene`:The scene currently owning the system.
`system`:The registered system to remove.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.RenameGameObject(Inno.Scene.GameObject gameObject, string name, string historyName = "Rename GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L61) | Renames a live GameObject and records the two display strings.
`gameObject`:The live GameObject to rename.
`name`:The new display name.
`historyName`:The user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.RenameScene(Inno.Scene.GameScene scene, string name, string historyName = "Rename Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L30) | Renames a loaded scene and records the two display strings.
`scene`:The loaded scene to rename.
`name`:The new display name.
`historyName`:The user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.ResetComponent(Inno.Scene.GameComponent component, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L165) | Resets one component and records its compact property state before and after Reset.
`component`:The attached component to reset.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.ResetSystem(Inno.Scene.GameScene scene, Inno.Scene.GameSystem system, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L419) | Resets one scene system and records its compact property state before and after Reset.
`scene`:The scene currently owning the system.
`system`:The registered system to reset.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.SetComponentIndex(Inno.Scene.GameComponent component, int componentIndex, string historyName = "Move Component")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L225) | Moves an attached component and records only its two attachment indices.
`component`:The attached component to move, including the mandatory Transform.
`componentIndex`:The requested attachment index.
`historyName`:The user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectActive(Inno.Scene.GameObject gameObject, bool active, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L92) | Changes the explicit active state of a GameObject and records the two Boolean values.
`gameObject`:The live GameObject whose active state should change.
`active`:The requested explicit active state.
`historyName`:An optional user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectLayer(Inno.Scene.GameObject gameObject, Inno.Scene.Layers.GameLayer layer, string historyName = "Set GameObject Layer")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L169) | Changes the layer of a live GameObject and records the two stable numeric layer slots.
`gameObject`:The live GameObject whose layer should change.
`layer`:The requested project layer slot.
`historyName`:The user-facing history entry name. | — | `System.ArgumentNullException`:Thrown when gameObject is null.
`System.ArgumentException`:Thrown when historyName is empty. | -| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectTag(Inno.Scene.GameObject gameObject, string tag, string historyName = "Set GameObject Tag")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L130) | Changes the tag of a live GameObject and records the two ordinal tag strings.
`gameObject`:The live GameObject whose tag should change.
`tag`:The requested non-empty tag.
`historyName`:The user-facing history entry name. | — | `System.ArgumentNullException`:Thrown when gameObject is null.
`System.ArgumentException`:Thrown when tag or historyName is empty. | -| [`public void Inno.Editor.Scene.SceneEdits.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex, string historyName = "Reorder Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Hierarchy.cs#L30) | Moves a loaded scene to a hierarchy index and records the two integer positions.
`scene`:The loaded scene to reorder.
`sceneIndex`:The requested hierarchy index.
`historyName`:The user-facing history entry name. | — | — | -| [`public void Inno.Editor.Scene.SceneEdits.SetSystemIndex(Inno.Scene.GameScene scene, Inno.Scene.GameSystem system, int systemIndex, string historyName = "Move System")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L483) | Moves a registered system and records only its two display indices.
`scene`:The scene currently owning the system.
`system`:The registered system to move.
`systemIndex`:The requested display index.
`historyName`:The user-facing history entry name. | — | — | -| [`public sealed class Inno.Editor.Scene.SceneEdits : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L20) | Applies scene-document mutations and records compact, reload-safe inverse data in editor history. | — | — | - -### `Inno.Editor.Scene.SceneHierarchyEdit` - + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public void Inno.Editor.Scene.SceneHierarchyEdit.MoveToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneHierarchyEdit.cs#L34) | Moves a live GameObject subtree into another scene owned by the current editor world.
`gameObject`:The live root object to move.
`destination`:The loaded destination scene. | — | `System.ArgumentNullException`:Thrown when an argument is null.
`System.InvalidOperationException`:Thrown when either scene is not loaded by the editor world. | +| [`public static class Inno.Editor.Scene.EditorSceneWorkspaceFactory`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceFactory.cs#L15) | Creates explicitly owned editor scene workspaces for embedded editor hosts and command-line tooling. | — | — | +| [`public static Inno.Editor.Scene.EditorSceneWorkspaceHost Inno.Editor.Scene.EditorSceneWorkspaceFactory.Create(Inno.Runtime.RuntimeSession runtimeSession, Inno.Assets.Pipeline.AssetPipeline assets, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Logging.LogRouter logs, Inno.Editor.Interactions.IEditorSelectionCoordinator? selection = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceFactory.cs#L44) | Creates an unattached workspace over explicitly owned Edit-session services.
`runtimeSession`: The Edit session that owns the workspace scene world.
`assets`: The authoring asset pipeline used for scene and prefab documents.
`types`: The host type catalog used for scene extension generations.
`serialization`: The host serialization registry used for scene snapshots.
`logs`: The application log router used for workspace lifecycle diagnostics.
`selection`: The optional selection coordinator used by the standalone document workspace. | A host that owns both the document and Play Mode interfaces of the workspace. | `System.ArgumentNullException`: Thrown when a required owner is . | +| [`public sealed class Inno.Editor.Scene.EditorSceneWorkspaceHost : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L17) | Owns an editor scene workspace created outside the attribute-discovered editor application. | — | — | +| [`public Inno.Editor.Scene.IEditorSceneWorkspace Inno.Editor.Scene.EditorSceneWorkspaceHost.workspace { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L43) | Gets the Edit-or-Play scene presentation and persistence boundary owned by this host. | — | — | +| [`public Inno.Editor.Scene.IEditorGameScenePresentation Inno.Editor.Scene.EditorSceneWorkspaceHost.gamePresentation { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L48) | Gets the Edit-or-Play rendering presentation boundary owned by this host. | — | — | +| [`public Inno.Editor.Scene.IEditorScenePlayMode Inno.Editor.Scene.EditorSceneWorkspaceHost.playMode { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L53) | Gets the isolated Play Mode scene-session boundary owned by this host. | — | — | +| [`public void Inno.Editor.Scene.EditorSceneWorkspaceHost.Dispose()`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspaceHost.cs#L58) | Releases the workspace and any isolated scene session that it still owns. | — | — | +| [`public interface Inno.Editor.Scene.IEditorScenePlayMode`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorScenePlayMode.cs#L10) | Creates isolated runtime scene sessions from the current editable scene set. | — | — | +| [`System.IDisposable Inno.Editor.Scene.IEditorScenePlayMode.BeginPlayMode(Inno.Runtime.RuntimeSession runtimeSession)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorScenePlayMode.cs#L30) | Captures the editable scene set and materializes independent runtime copies in the supplied session.
`runtimeSession`: The isolated Play Mode session that receives the immutable start snapshot and owns editor-side operations performed against the runtime copies. | A lease that presents the runtime copies to editor features, prevents their persistence, and restores the Edit presentation when disposed. | `System.ArgumentNullException`: Thrown when is null.
`System.InvalidOperationException`: Thrown when another Play Mode lease is active, the target world is not empty, or the snapshot cannot be materialized atomically. | +| [`public interface Inno.Editor.Scene.IEditorSceneWorkspace`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L10) | Exposes the active Edit or Play scene presentation and the persistence operations available to it. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Editor.Scene.IEditorSceneWorkspace.scenes { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L15) | Gets the Edit scenes outside Play Mode or the isolated runtime copies while Play Mode is active. | — | — | +| [`Inno.Scene.GameScene? Inno.Editor.Scene.IEditorSceneWorkspace.activeScene { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L20) | Gets the active scene from the currently presented Edit or Play world. | — | — | +| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.canPersist { get; }`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L25) | Gets whether the currently presented scenes are authoring documents that may be persisted. | — | — | +| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.CanEdit(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L37) | Gets whether a loaded scene may be changed in the current Edit or isolated Play world.
`scene`: The scene to inspect. | for a writable Project scene or transient scene in the presented world. Changes made during Play remain in the runtime copy and are discarded on stop. | — | +| [`void Inno.Editor.Scene.IEditorSceneWorkspace.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L51) | Makes one presented scene active without changing scene order.
`scene`: The loaded scene to activate. | — | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the scene is not loaded by this workspace. | +| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.IsDirty(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L63) | Gets whether an Edit scene contains unsaved serialized changes.
`scene`: The scene to inspect. | when an Edit scene differs from its saved baseline; runtime copies always return because they cannot be persisted. | — | +| [`Inno.Scene.GameScene Inno.Editor.Scene.IEditorSceneWorkspace.Open(string relativePath)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L77) | Opens a scene asset additively as the active editor scene.
`relativePath`: The source-relative scene asset path. | The existing loaded instance or newly loaded scene. | `System.InvalidOperationException`: Thrown while Play Mode runtime copies are active or when the asset cannot be opened. | +| [`string Inno.Editor.Scene.IEditorSceneWorkspace.Save(Inno.Scene.GameScene scene, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L94) | Saves a scene to its existing path or into a fallback directory.
`scene`: The scene to save.
`currentDirectory`: The fallback asset directory for an unsaved scene. | The saved source-relative path. | `System.InvalidOperationException`: Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | +| [`string Inno.Editor.Scene.IEditorSceneWorkspace.SaveToDirectory(Inno.Scene.GameScene scene, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L114) | Saves a scene into the requested asset directory.
`scene`: The scene to save.
`currentDirectory`: The target asset directory. | The saved source-relative path. | `System.InvalidOperationException`: Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | +| [`string Inno.Editor.Scene.IEditorSceneWorkspace.SavePrefab(Inno.Scene.GameObject gameObject, string currentDirectory)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L134) | Captures a game object subtree as a prefab in the requested directory.
`gameObject`: The prefab root.
`currentDirectory`: The target asset directory. | The saved source-relative path. | `System.InvalidOperationException`: Thrown while Play Mode runtime copies are active or when the asset cannot be persisted. | +| [`bool Inno.Editor.Scene.IEditorSceneWorkspace.TryGetSourcePath(Inno.Scene.GameScene scene, out string relativePath)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/IEditorSceneWorkspace.cs#L151) | Tries to get the current source-relative asset path of a saved scene.
`scene`: The scene whose path is requested.
`relativePath`: The saved path when available. | when the scene is backed by a scene asset. | — | +| [`public sealed class Inno.Editor.Scene.SceneEdits : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L20) | Applies scene-document mutations and records compact, reload-safe inverse data in editor history. | — | — | +| [`public bool Inno.Editor.Scene.SceneEdits.CanEdit(Inno.Scene.GameScene scene)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L52) | Gets whether the scene is editable in the current Edit or isolated Play world.
`scene`: The loaded scene to inspect. | when scene commands may change this scene. Play changes use a temporary history branch and are discarded when the Play session ends. | — | +| [`public bool Inno.Editor.Scene.SceneEdits.CanEdit(Inno.Scene.EngineObject target)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L63) | Gets whether a scene object belongs to an editable presented scene.
`target`: The scene object to inspect. | when its owning scene is writable. | — | +| [`public Inno.Scene.GameScene Inno.Editor.Scene.SceneEdits.CreateScene(string historyName = "Create Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L87) | Creates an additive scene and records a reversible document change.
`historyName`: The user-facing history entry name. | The newly created active scene. | — | +| [`public bool Inno.Editor.Scene.SceneEdits.CloseScene(Inno.Scene.GameScene scene, string historyName = "Close Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.cs#L130) | Closes one loaded scene without deleting its source asset and records a reversible document change.
`scene`: The loaded scene to close.
`historyName`: The user-facing history entry name. | when the scene was closed and recorded. | — | +| [`public Inno.Scene.GameComponent Inno.Editor.Scene.SceneEdits.AddComponent(Inno.Scene.GameObject owner, System.Type componentType, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L31) | Adds one component and records its identity, stable type, index, and persistent properties.
`owner`: The live GameObject receiving the component.
`componentType`: The concrete component type to create.
`historyName`: An optional user-facing history entry name. | The newly attached component. | — | +| [`public bool Inno.Editor.Scene.SceneEdits.RemoveComponent(Inno.Scene.GameComponent component, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L87) | Removes the component from its scene owner and records a reversible serialized history change.
`component`: The attached non-Transform component to remove.
`historyName`: An optional user-facing history entry name. | when the component was removed and recorded. | — | +| [`public void Inno.Editor.Scene.SceneEdits.ResetComponent(Inno.Scene.GameComponent component, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L153) | Resets one component and records its compact property state before and after Reset.
`component`: The attached component to reset.
`historyName`: An optional user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.SetComponentIndex(Inno.Scene.GameComponent component, int componentIndex, string historyName = "Move Component")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L211) | Moves an attached component and records only its two attachment indices.
`component`: The attached component to move, including the mandatory Transform.
`componentIndex`: The requested attachment index.
`historyName`: The user-facing history entry name. | — | — | +| [`public Inno.Scene.GameSystem Inno.Editor.Scene.SceneEdits.AddSystem(Inno.Scene.GameScene scene, System.Type systemType, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L273) | Adds one scene system and records its identity, stable type, index, and persistent properties.
`scene`: The loaded scene receiving the system.
`systemType`: The concrete system type to create.
`historyName`: An optional user-facing history entry name. | The newly registered system. | — | +| [`public bool Inno.Editor.Scene.SceneEdits.RemoveSystem(Inno.Scene.GameSystem system, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L327) | Removes the system from its scene and records a reversible serialized history change.
`system`: The registered system to remove.
`historyName`: An optional user-facing history entry name. | when the system was removed and recorded. | — | +| [`public void Inno.Editor.Scene.SceneEdits.ResetSystem(Inno.Scene.GameSystem system, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L394) | Resets one scene system and records its compact property state before and after Reset.
`system`: The registered system to reset.
`historyName`: An optional user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.SetSystemIndex(Inno.Scene.GameScene scene, Inno.Scene.GameSystem system, int systemIndex, string historyName = "Move System")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs#L455) | Moves a registered system and records only its two display indices.
`scene`: The scene currently owning the system.
`system`: The registered system to move.
`systemIndex`: The requested display index.
`historyName`: The user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex, string historyName = "Reorder Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Hierarchy.cs#L30) | Moves a loaded scene to a hierarchy index and records the two integer positions.
`scene`: The loaded scene to reorder.
`sceneIndex`: The requested hierarchy index.
`historyName`: The user-facing history entry name. | — | — | +| [`public bool Inno.Editor.Scene.SceneEdits.ChangeHierarchy(Inno.Scene.GameObject gameObject, System.Action mutation, string historyName = "Move GameObject", System.Collections.Generic.IReadOnlyCollection? relatedObjects = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Hierarchy.cs#L80) | Applies a hierarchy mutation and records only the affected parent and sibling-index tuples.
`gameObject`: The primary GameObject being moved.
`mutation`: The hierarchy mutation to execute through the world-owned operation context.
`historyName`: The user-facing history entry name.
`relatedObjects`: Additional objects whose placements the mutation may change, such as promoted children. | when at least one placement changed. | — | +| [`public Inno.Scene.GameObject Inno.Editor.Scene.SceneEdits.CreateGameObject(Inno.Scene.GameScene scene, Inno.Scene.Components.Transform? parent = null, string historyName = "Create GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L33) | Creates a GameObject, optionally parents it, and records only the new subtree state.
`scene`: The loaded scene that will own the object.
`parent`: The optional parent transform.
`historyName`: The user-facing history entry name. | The newly created GameObject. | — | +| [`public Inno.Scene.GameObject Inno.Editor.Scene.SceneEdits.InstantiatePrefab(Inno.Scene.PrefabAsset prefab, Inno.Scene.GameScene scene, Inno.Scene.Components.Transform? parent = null, string historyName = "Instantiate Prefab")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L91) | Instantiates a prefab into a loaded scene and records the created subtree as one reversible edit.
`prefab`: The imported prefab asset to instantiate.
`scene`: The loaded scene that will own the instance.
`parent`: The optional parent transform for the instantiated root.
`historyName`: The user-facing history entry name. | The instantiated prefab root. | — | +| [`public bool Inno.Editor.Scene.SceneEdits.DeleteGameObject(Inno.Scene.GameObject gameObject, string historyName = "Delete GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Objects.cs#L150) | Deletes a GameObject subtree and records only that subtree plus incoming serialized references.
`gameObject`: The live subtree root to delete.
`historyName`: The user-facing history entry name. | when the subtree was deleted and recorded. | — | +| [`public void Inno.Editor.Scene.SceneEdits.RenameScene(Inno.Scene.GameScene scene, string name, string historyName = "Rename Scene")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L30) | Renames a loaded scene and records the two display strings.
`scene`: The loaded scene to rename.
`name`: The new display name.
`historyName`: The user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.RenameGameObject(Inno.Scene.GameObject gameObject, string name, string historyName = "Rename GameObject")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L61) | Renames a live GameObject and records the two display strings.
`gameObject`: The live GameObject to rename.
`name`: The new display name.
`historyName`: The user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectActive(Inno.Scene.GameObject gameObject, bool active, string? historyName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L92) | Changes the explicit active state of a GameObject and records the two Boolean values.
`gameObject`: The live GameObject whose active state should change.
`active`: The requested explicit active state.
`historyName`: An optional user-facing history entry name. | — | — | +| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectTag(Inno.Scene.GameObject gameObject, string tag, string historyName = "Set GameObject Tag")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L130) | Changes the tag of a live GameObject and records the two ordinal tag strings.
`gameObject`: The live GameObject whose tag should change.
`tag`: The requested non-empty tag.
`historyName`: The user-facing history entry name. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when or is empty. | +| [`public void Inno.Editor.Scene.SceneEdits.SetGameObjectLayer(Inno.Scene.GameObject gameObject, Inno.Scene.Layers.GameLayer layer, string historyName = "Set GameObject Layer")`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L169) | Changes the layer of a live GameObject and records the two stable numeric layer slots.
`gameObject`: The live GameObject whose layer should change.
`layer`: The requested project layer slot.
`historyName`: The user-facing history entry name. | — | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Scene.SceneEdits.ChangeProperty(Inno.Scene.EngineObject target, string propertyName, System.Action mutation, string historyName, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Properties.cs#L210) | Applies a mutation to one serializable scene property and records only its before and after values.
`target`: The live scene object containing the root serialized property.
`propertyName`: The exact root serialized member key.
`mutation`: The mutation that assigns the new value.
`historyName`: The user-facing history entry name.
`mergeKey`: An optional stable key for coalescing adjacent continuous edits. | when the property value changed and a history entry was recorded. | — | +| [`public void Inno.Editor.Scene.SceneEdits.DeleteGameObjects(System.Collections.Generic.IReadOnlyList targets)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs#L22) | Deletes selected subtrees atomically, omitting descendants already covered by a selected ancestor.
`targets`: Live objects to delete; every owner is validated before the first mutation. | — | `System.InvalidOperationException`: An owner is unavailable, read-only, or rollback fails. | +| [`public bool Inno.Editor.Scene.SceneEdits.ChangeProperties(System.Collections.Generic.IReadOnlyList targets, string propertyName, System.Action mutation, string historyName, string? mergeKey = null, string? componentName = null)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs#L72) | Applies one primary property edit to a selection using owner-resolved serialized values and one atomic History transaction.
`targets`: Ordered compatible scene objects; the first object supplies the edited value.
`propertyName`: The common root serialized property.
`mutation`: Changes the primary value synchronously; no callback is retained.
`historyName`: The user-facing batch edit name.
`mergeKey`: An optional active gesture identity; null keeps discrete edits separate.
`componentName`: An optional public scalar value component; Euler axes use euler.x, euler.y or euler.z. | True when at least one object changed; false when all values already matched. | `System.ArgumentException`: The selection is empty or a required common property is absent.
`System.InvalidOperationException`: An owner is unavailable, read-only, or rollback fails. | +| [`public bool Inno.Editor.Scene.SceneEdits.HaveSamePropertyValues(System.Collections.Generic.IReadOnlyList targets, string propertyName)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs#L141) | Compares common serialized property values using the complete owner serialization context.
`targets`: Live compatible scene objects to compare.
`propertyName`: The required common serialized member. | True when every encoded value matches, including an empty selection. | `System.InvalidOperationException`: A selected object does not expose the requested property. | +| [`public void Inno.Editor.Scene.SceneEdits.MoveGameObjects(System.Collections.Generic.IReadOnlyList targets, Inno.Scene.GameScene destination, Inno.Scene.GameObject? parent = null, int insertionIndex = 2147483647)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs#L184) | Moves minimal selected roots together, preserving descendants and recording only affected placements.
`targets`: Selected live objects in their required relative order.
`destination`: The loaded editable destination scene.
`parent`: The new parent, or null for scene roots.
`insertionIndex`: The insertion index after removing the selected roots; the default appends them. | — | `System.InvalidOperationException`: An owner is read-only, unavailable, or the move would form a cycle.
`System.ArgumentException`: The parent belongs to a different scene. | | [`public sealed class Inno.Editor.Scene.SceneHierarchyEdit`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneHierarchyEdit.cs#L10) | Exposes world-owned hierarchy operations inside one atomic scene history mutation. | — | — | +| [`public void Inno.Editor.Scene.SceneHierarchyEdit.MoveToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/composition/editor/features/Inno.Editor.Scene/Documents/SceneHierarchyEdit.cs#L34) | Moves a live GameObject subtree into another scene owned by the current editor world.
`gameObject`: The live root object to move.
`destination`: The loaded destination scene. | — | `System.ArgumentNullException`: Thrown when an argument is .
`System.InvalidOperationException`: Thrown when either scene is not loaded by the editor world. | +| [`public interface Inno.Editor.Scene.IEditorGameScenePresentation`](../../src/composition/editor/features/Inno.Editor.Scene/Presentation/IEditorGameScenePresentation.cs#L9) | Supplies the scene set that represents the game to Editor viewport consumers without exposing runtime-session ownership. | — | — | +| [`Inno.References.ContentReadScope Inno.Editor.Scene.IEditorGameScenePresentation.Capture()`](../../src/composition/editor/features/Inno.Editor.Scene/Presentation/IEditorGameScenePresentation.cs#L19) | Captures one coherent game-scene presentation for the current Editor frame. | A caller-owned identity scope for the Edit scenes while not playing and the isolated Play scenes after Play Mode commits. Dispose it after consumption; it retains no scene instances and stale identities never resolve to a replacement session or generation. | — | ## 项目依赖 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Coroutines](../core/Inno.Core.Coroutines.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene.Assets](../scene/Inno.Scene.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Scene](../scene/Inno.Scene.md):公开引用边界由实际签名核对。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):公开引用边界由实际签名核对。 -- [Inno.References](../references/Inno.References.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.Core.Coroutines` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Scene` | 公开签名依赖 | +| `Inno.Runtime` | 公开签名依赖 | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Core.Logging` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Editor.Interactions` | 公开签名依赖 | +| `Inno.References` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Scripting.md b/docs/editor/Inno.Editor.Scripting.md index 24742ec61..2b523c8b5 100644 --- a/docs/editor/Inno.Editor.Scripting.md +++ b/docs/editor/Inno.Editor.Scripting.md @@ -250,62 +250,28 @@ Serialized state 使用逐成员兼容迁移:成员类型保持兼容时恢复 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Scripting.EditorScriptCompilationState` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Scripting.EditorScriptCompilationState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L18) | A requested script generation is compiling, activating, or completing unload verification. | — | — | -| [`Inno.Editor.Scripting.EditorScriptCompilationState.Failed = 3`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L28) | Compilation, activation, or generation retirement failed, so game simulation must not start. | — | — | -| [`Inno.Editor.Scripting.EditorScriptCompilationState.Initializing = 0`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L13) | The script service has not produced its initial active generation. | — | — | -| [`Inno.Editor.Scripting.EditorScriptCompilationState.Ready = 2`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L23) | The most recent compilation succeeded, its generation is active, and retired generations passed GC verification. | — | — | -| [`public enum Inno.Editor.Scripting.EditorScriptCompilationState : int`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L8) | Identifies whether the active project script generation can safely start game simulation. | — | — | - -### `Inno.Editor.Scripting.IEditorScriptCompilation` - + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Editor.Scripting.IScriptCompilationTicket Inno.Editor.Scripting.IEditorScriptCompilation.RequestCompilation()`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L43) | Queues a fresh cache-aware compilation for a build or simulation workflow.
返回:A ticket bound to the exact request. Success requires activation and completed retirement verification; a faulted host returns a failed ticket and requires restart. | — | — | -| [`Inno.Editor.Scripting.IScriptCompilationTicket? Inno.Editor.Scripting.IEditorScriptCompilation.currentTicket { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L48) | Gets the most recently requested compilation ticket, or null when no explicit request exists. | — | — | -| [`Inno.Scripting.Compiler.ScriptCompilationResult? Inno.Editor.Scripting.IEditorScriptCompilation.lastCompilation { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L63) | Gets the most recently completed compilation, or null before the first attempt completes. | — | — | -| [`Inno.Editor.Scripting.EditorScriptCompilationState Inno.Editor.Scripting.IEditorScriptCompilation.state { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L53) | Gets the current script-generation readiness state. | — | — | -| [`string Inno.Editor.Scripting.IEditorScriptCompilation.status { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L58) | Gets a human-readable description of current compiler work or readiness. | — | — | -| [`public interface Inno.Editor.Scripting.IEditorScriptCompilation`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L34) | Exposes the minimal script-generation readiness contract required by editor workflows. | — | — | - -### `Inno.Editor.Scripting.IScriptCompilationTicket` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`bool Inno.Editor.Scripting.IScriptCompilationTicket.isCompleted { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L33) | Gets whether no further state transition is valid for this request. | — | — | -| [`long Inno.Editor.Scripting.IScriptCompilationTicket.requestId { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L13) | Gets the producer-local monotonic identifier of this request. | — | — | -| [`Inno.Scripting.Compiler.ScriptCompilationResult? Inno.Editor.Scripting.IScriptCompilationTicket.result { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L28) | Gets the completed compiler result, or null before the compiler finishes. | — | — | -| [`Inno.Editor.Scripting.ScriptCompilationTicketState Inno.Editor.Scripting.IScriptCompilationTicket.state { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L18) | Gets the current lifecycle state of this exact request. | — | — | -| [`string Inno.Editor.Scripting.IScriptCompilationTicket.status { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L23) | Gets a human-readable description of the current or terminal state. | — | — | -| [`public interface Inno.Editor.Scripting.IScriptCompilationTicket`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L8) | Exposes the immutable observer view of one exact script compilation and activation request. | — | — | - -### `Inno.Editor.Scripting.ScriptCompilationTicketState` - @@ -314,30 +280,50 @@ Serialized state 使用逐成员兼容迁移:成员类型保持兼容时恢复 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Canceled = 4`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L31) | The request was explicitly canceled before activation. | — | — | -| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L16) | The request owns the compiler pipeline or is activating its candidate generation. | — | — | -| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Failed = 3`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L26) | Compilation or candidate activation failed without replacing the active generation. | — | — | +| [`public enum Inno.Editor.Scripting.EditorScriptCompilationState : int`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L7) | Identifies whether the active project script generation can safely start game simulation. | — | — | +| [`Inno.Editor.Scripting.EditorScriptCompilationState.Initializing = 0`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L12) | The script service has not produced its initial active generation. | — | — | +| [`Inno.Editor.Scripting.EditorScriptCompilationState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L16) | A requested script generation is compiling, activating, or completing unload verification. | — | — | +| [`Inno.Editor.Scripting.EditorScriptCompilationState.Ready = 2`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L20) | The most recent compilation succeeded, its generation is active, and retired generations passed GC verification. | — | — | +| [`Inno.Editor.Scripting.EditorScriptCompilationState.Failed = 3`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L24) | Compilation, activation, or generation retirement failed, so game simulation must not start. | — | — | +| [`public interface Inno.Editor.Scripting.IEditorScriptCompilation`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L30) | Exposes the minimal script-generation readiness contract required by editor workflows. | — | — | +| [`bool Inno.Editor.Scripting.IEditorScriptCompilation.requiresExclusiveProgress { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L35) | Gets whether compiler or activation work currently requires a blocking progress presentation. | — | — | +| [`float Inno.Editor.Scripting.IEditorScriptCompilation.progress { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L40) | Gets the most recent compiler progress fraction in the inclusive range zero to one. | — | — | +| [`void Inno.Editor.Scripting.IEditorScriptCompilation.CancelCompilation()`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L45) | Requests cancellation while retaining ownership until compilation and retirement finish. | — | — | +| [`Inno.Editor.Scripting.IScriptCompilationTicket Inno.Editor.Scripting.IEditorScriptCompilation.RequestCompilation()`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L53) | Queues a fresh cache-aware compilation for a build or simulation workflow. | A ticket bound to the exact request. Success requires activation and completed retirement verification; a faulted host returns a failed ticket and requires restart. | — | +| [`Inno.Editor.Scripting.IScriptCompilationTicket? Inno.Editor.Scripting.IEditorScriptCompilation.currentTicket { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L57) | Gets the most recently requested compilation ticket, or when no explicit request exists. | — | — | +| [`Inno.Editor.Scripting.EditorScriptCompilationState Inno.Editor.Scripting.IEditorScriptCompilation.state { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L62) | Gets the current script-generation readiness state. | — | — | +| [`string Inno.Editor.Scripting.IEditorScriptCompilation.status { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L67) | Gets a human-readable description of current compiler work or readiness. | — | — | +| [`Inno.Scripting.Compiler.ScriptCompilationResult? Inno.Editor.Scripting.IEditorScriptCompilation.lastCompilation { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs#L72) | Gets the most recently completed compilation, or before the first attempt completes. | — | — | +| [`public interface Inno.Editor.Scripting.IScriptCompilationTicket`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L8) | Exposes the immutable observer view of one exact script compilation and activation request. | — | — | +| [`long Inno.Editor.Scripting.IScriptCompilationTicket.requestId { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L13) | Gets the producer-local monotonic identifier of this request. | — | — | +| [`Inno.Editor.Scripting.ScriptCompilationTicketState Inno.Editor.Scripting.IScriptCompilationTicket.state { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L18) | Gets the current lifecycle state of this exact request. | — | — | +| [`string Inno.Editor.Scripting.IScriptCompilationTicket.status { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L23) | Gets a human-readable description of the current or terminal state. | — | — | +| [`Inno.Scripting.Compiler.ScriptCompilationResult? Inno.Editor.Scripting.IScriptCompilationTicket.result { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L28) | Gets the completed compiler result, or before the compiler finishes. | — | — | +| [`bool Inno.Editor.Scripting.IScriptCompilationTicket.isCompleted { get; }`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/IScriptCompilationTicket.cs#L33) | Gets whether no further state transition is valid for this request. | — | — | +| [`public enum Inno.Editor.Scripting.ScriptCompilationTicketState : int`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L6) | Identifies the lifecycle state of one exact script compilation request. | — | — | | [`Inno.Editor.Scripting.ScriptCompilationTicketState.Queued = 0`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L11) | The request is queued behind the active compiler operation. | — | — | +| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Compiling = 1`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L16) | The request owns the compiler pipeline or is activating its candidate generation. | — | — | | [`Inno.Editor.Scripting.ScriptCompilationTicketState.Succeeded = 2`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L21) | The request compiled and atomically activated its candidate generation. | — | — | +| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Failed = 3`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L26) | Compilation or candidate activation failed without replacing the active generation. | — | — | +| [`Inno.Editor.Scripting.ScriptCompilationTicketState.Canceled = 4`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L31) | The request was explicitly canceled before activation. | — | — | | [`Inno.Editor.Scripting.ScriptCompilationTicketState.Superseded = 5`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L36) | A newer request replaced this request before its candidate could activate. | — | — | -| [`public enum Inno.Editor.Scripting.ScriptCompilationTicketState : int`](../../src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationTicketState.cs#L6) | Identifies the lifecycle state of one exact script compilation request. | — | — | ## 项目依赖 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Plugins.Authoring](../plugins/Inno.Plugins.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Reload](../scripting/Inno.Scripting.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Core](Inno.Editor.Core.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Compiler](../scripting/Inno.Scripting.Compiler.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Plugins.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Settings.md b/docs/editor/Inno.Editor.Settings.md index 105460607..9d5bfcb79 100644 --- a/docs/editor/Inno.Editor.Settings.md +++ b/docs/editor/Inno.Editor.Settings.md @@ -136,9 +136,7 @@ History 事务;这允许一个较大的运行时设置协议在 UI 中拆成 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Settings.EditorSetting` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -153,25 +151,6 @@ History 事务;这允许一个较大的运行时设置协议在 UI 中拆成 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Settings.EditorSetting.EditorSetting()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L23) | Creates a Settings definition. A field supplies its persisted default through Inno.Editor.Settings.EditorSetting.defaultValue; a page keeps the base implementation. | — | — | -| [`public bool Inno.Editor.Settings.EditorSetting.Draw(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L85) | Draws this field inside the frontend-managed content container.
返回:true when the staged object differs from the value it held when this frontend first drew that object.
`setting`:The isolated mutable object being edited. | — | `System.ArgumentNullException`:Thrown when setting is null. | -| [`public bool Inno.Editor.Settings.EditorSetting.IsDefault(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L110) | Determines whether a staged field object equals this definition's bound default value.
返回:true when every stored property equals the field default.
`setting`:The isolated staged object to compare. | — | `System.ArgumentNullException`:Thrown when setting is null.
`System.InvalidOperationException`:Thrown when this definition describes a page rather than a field. | -| [`protected virtual void Inno.Editor.Settings.EditorSetting.OnDraw(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L126) | Draws the field using the staged JSON object owned by the Settings frontend. A type that does not override this method describes a page.
`setting`:The isolated mutable object being edited. | — | — | -| [`public virtual Inno.Editor.Settings.EditorSettingObject Inno.Editor.Settings.EditorSetting.defaultValue { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L59) | Creates the default object for a field. Page definitions keep the base implementation, whose internal value is null. | Field implementations must return a newly owned object on every access. | — | -| [`public virtual string Inno.Editor.Settings.EditorSetting.description { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L70) | Gets the page or field explanation displayed by the Settings frontend. | — | — | -| [`public bool Inno.Editor.Settings.EditorSetting.hasValue { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L50) | Gets whether this definition owns one persisted JSON object. | — | — | -| [`public string Inno.Editor.Settings.EditorSetting.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L40) | Gets the display label derived from the final path segment. | — | — | -| [`public int Inno.Editor.Settings.EditorSetting.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L45) | Gets the stable order among fields with the same section and label. | — | — | -| [`public string Inno.Editor.Settings.EditorSetting.pagePath { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L35) | Gets the page that owns this field, or this page's own path for a page definition. | — | — | -| [`public string Inno.Editor.Settings.EditorSetting.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L30) | Gets the complete slash-delimited identity and placement path. | — | — | -| [`public virtual string Inno.Editor.Settings.EditorSetting.section { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L65) | Gets the section heading used to group fields alphabetically. Definitions without a section keep the base implementation, whose internal value is null. | — | — | -| [`public abstract class Inno.Editor.Settings.EditorSetting`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L10) | Defines one path-addressed Settings page or one custom-drawn Settings field. | — | — | - -### `Inno.Editor.Settings.EditorSettingObject` - @@ -202,76 +181,10 @@ History 事务;这允许一个较大的运行时设置协议在 UI 中拆成 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Settings.EditorSettingObject.EditorSettingObject()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L21) | Creates an empty Settings object. | — | — | -| [`public bool Inno.Editor.Settings.EditorSettingObject.GetAsBoolean(string name, bool defaultValue = false)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L42) | Gets a Boolean property, or a fallback when the property is absent.
返回:The stored Boolean or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public bool[] Inno.Editor.Settings.EditorSettingObject.GetAsBooleanArray(string name, bool[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L304) | Gets a Boolean array property, or a copied fallback when the property is absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public double Inno.Editor.Settings.EditorSettingObject.GetAsDouble(string name, double defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L235) | Gets a double-precision property, or a fallback when the property is absent.
返回:The stored number or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public double[] Inno.Editor.Settings.EditorSettingObject.GetAsDoubleArray(string name, double[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L428) | Gets a double-precision array property, or a copied fallback when absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public int Inno.Editor.Settings.EditorSettingObject.GetAsInt32(string name, int defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L73) | Gets a 32-bit signed integer property, or a fallback when the property is absent.
返回:The stored integer or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public int[] Inno.Editor.Settings.EditorSettingObject.GetAsInt32Array(string name, int[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L335) | Gets a 32-bit signed integer array property, or a copied fallback when absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public long Inno.Editor.Settings.EditorSettingObject.GetAsInt64(string name, long defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L135) | Gets a 64-bit signed integer property, or a fallback when the property is absent.
返回:The stored integer or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public float Inno.Editor.Settings.EditorSettingObject.GetAsSingle(string name, float defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L197) | Gets a single-precision property, or a fallback when the property is absent.
返回:The stored number or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public float[] Inno.Editor.Settings.EditorSettingObject.GetAsSingleArray(string name, float[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L397) | Gets a single-precision array property, or a copied fallback when absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public string? Inno.Editor.Settings.EditorSettingObject.GetAsString(string name, string? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L273) | Gets a string property, or a fallback when the property is absent or null.
返回:The stored string or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent or null. | — | — | -| [`public string?[] Inno.Editor.Settings.EditorSettingObject.GetAsStringArray(string name, string?[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L459) | Gets a nullable string array property, or a copied fallback when absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public uint Inno.Editor.Settings.EditorSettingObject.GetAsUInt32(string name, uint defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L104) | Gets a 32-bit unsigned integer property, or a fallback when the property is absent.
返回:The stored integer or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public uint[] Inno.Editor.Settings.EditorSettingObject.GetAsUInt32Array(string name, uint[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L366) | Gets a 32-bit unsigned integer array property, or a copied fallback when absent.
返回:An independently owned array.
`name`:The stable property name.
`defaultValue`:The array copied when the property is absent. | — | — | -| [`public ulong Inno.Editor.Settings.EditorSettingObject.GetAsUInt64(string name, ulong defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L166) | Gets a 64-bit unsigned integer property, or a fallback when the property is absent.
返回:The stored integer or defaultValue.
`name`:The stable property name.
`defaultValue`:The value returned when the property is absent. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsBoolean(string name, bool value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L56) | Sets a Boolean property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsBooleanArray(string name, bool[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L318) | Sets a Boolean array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsDouble(string name, double value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L252) | Sets a finite double-precision property.
`name`:The stable property name.
`value`:The finite value to store. | — | `System.ArgumentOutOfRangeException`:Thrown when value is not finite. | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsDoubleArray(string name, double[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L442) | Sets a double-precision array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt32(string name, int value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L87) | Sets a 32-bit signed integer property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt32Array(string name, int[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L349) | Sets a 32-bit signed integer array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt64(string name, long value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L149) | Sets a 64-bit signed integer property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsSingle(string name, float value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L214) | Sets a finite single-precision property.
`name`:The stable property name.
`value`:The finite value to store. | — | `System.ArgumentOutOfRangeException`:Thrown when value is not finite. | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsSingleArray(string name, float[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L411) | Sets a single-precision array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsString(string name, string? value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L287) | Sets a nullable string property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsStringArray(string name, string?[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L473) | Sets a nullable string array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt32(string name, uint value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L118) | Sets a 32-bit unsigned integer property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt32Array(string name, uint[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L380) | Sets a 32-bit unsigned integer array property by value.
`name`:The stable property name.
`value`:The array copied into the object. | — | — | -| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt64(string name, ulong value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L180) | Sets a 64-bit unsigned integer property.
`name`:The stable property name.
`value`:The value to store. | — | — | -| [`public sealed class Inno.Editor.Settings.EditorSettingObject : Inno.Core.Serialization.ISerializable`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L13) | Represents one isolated, strongly typed object exposed to a Settings field and its consumers. | — | — | - -### `Inno.Editor.Settings.EditorSettingPathAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Settings.EditorSettingPathAttribute.EditorSettingPathAttribute(string path, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L24) | Creates a path placement. A definition that overrides its drawing method becomes a field; a definition that keeps the default drawing method describes the page at the complete path.
`path`:The slash-delimited page and field path.
`order`:The stable order among fields with the same section and label. | — | `System.ArgumentException`:Thrown when path contains an empty segment or is outside the Editor root. | -| [`public int Inno.Editor.Settings.EditorSettingPathAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L48) | Gets the stable order among fields with the same section and label. | — | — | -| [`public string Inno.Editor.Settings.EditorSettingPathAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L43) | Gets the normalized path including the field label. | — | — | -| [`public sealed class Inno.Editor.Settings.EditorSettingPathAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L9) | Places an Inno.Editor.Settings.EditorSetting at an arbitrary string Settings path. | — | — | - -### `Inno.Editor.Settings.EditorSettings` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Action? Inno.Editor.Settings.EditorSettings.changed`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L72) | Occurs after a complete Settings Apply, Undo, or Redo changes the effective document. The committed Inno.Editor.Settings.EditorSettings service is the event's only argument. | — | — | -| [`public bool Inno.Editor.Settings.EditorSettings.Apply(System.Collections.Generic.IReadOnlyDictionary values, System.Collections.Generic.IReadOnlySet? resets = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L131) | Atomically applies staged field objects as one shared Undo and Redo history entry.
返回:true when the persisted document changed and one history entry was recorded.
`values`:The complete or partial path-addressed objects to apply from the active catalog.
`resets`:Optional paths whose persisted overrides are removed during the same Apply. | — | `System.ArgumentNullException`:Thrown when values is null or contains a null object.
`System.ArgumentException`:Thrown when a supplied path is invalid, absent, or describes a page.
`System.IO.IOException`:Thrown when the project-root document or its history payload cannot be written.
`System.UnauthorizedAccessException`:Thrown when the project-root document is inaccessible. | -| [`public Inno.Editor.Settings.EditorSettingObject Inno.Editor.Settings.EditorSettings.Get(string path)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L96) | Reads an isolated effective object from one complete Settings path.
返回:The stored object, or an isolated copy of the field's default object.
`path`:The slash-delimited Settings field path. | — | `System.ArgumentException`:Thrown when path is invalid, missing, or describes a page. | -| [`protected override void Inno.Editor.Settings.EditorSettings.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L209) | Releases resources retained by this feature after it has stopped. | — | — | -| [`public long Inno.Editor.Settings.EditorSettings.catalogRevision { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L77) | Gets the current discovered type-catalog revision. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Settings.EditorSettings.definitions { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L82) | Gets all discovered path definitions in deterministic path order. | — | — | -| [`public sealed class Inno.Editor.Settings.EditorSettings : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L17) | Owns the discovered Settings catalog and the project-root Settings document. | — | — | - -### `Inno.Editor.Settings.ProjectSettingEditor` - @@ -283,49 +196,20 @@ History 事务;这允许一个较大的运行时设置协议在 UI 中拆成 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Settings.ProjectSettingEditor.ProjectSettingEditor()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L13) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public bool Inno.Editor.Settings.ProjectSettingEditor.Draw(Inno.Core.Serialization.ISerializable value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L71) | Draws one isolated staged value through this presentation.
返回:true when the value differs from its first drawn state.
`value`:The exact setting type owned by this presentation. | — | `System.ArgumentException`:Thrown when value has another runtime type. | -| [`public bool Inno.Editor.Settings.ProjectSettingEditor.ValuesEqual(Inno.Core.Serialization.ISerializable left, Inno.Core.Serialization.ISerializable right)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L89) | Compares two exact-type values through their native serialized property data.
返回:true when both values contain equal serialized properties.
`left`:The first setting value.
`right`:The second setting value. | — | `System.ArgumentException`:Thrown when either value has another runtime type. | -| [`public virtual string Inno.Editor.Settings.ProjectSettingEditor.description { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L56) | Gets the explanation displayed by the unified Settings frontend. | — | — | -| [`public string Inno.Editor.Settings.ProjectSettingEditor.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L41) | Gets the display label derived from the final path segment. | — | — | -| [`public int Inno.Editor.Settings.ProjectSettingEditor.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L46) | Gets the stable order among fields in the same section. | — | — | -| [`public string Inno.Editor.Settings.ProjectSettingEditor.pagePath { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L36) | Gets the page that owns this field. | — | — | -| [`public string Inno.Editor.Settings.ProjectSettingEditor.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L31) | Gets the complete slash-delimited placement path. | — | — | -| [`public virtual string Inno.Editor.Settings.ProjectSettingEditor.section { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L51) | Gets the section heading used to group this field. | — | — | -| [`public Inno.Core.Settings.ProjectSettingId Inno.Editor.Settings.ProjectSettingEditor.settingId { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L26) | Gets the stable runtime project setting protocol read from the target setting type's Inno.Core.Settings.ProjectSettingDefinitionAttribute by the Editor catalog. | — | — | -| [`public abstract class Inno.Editor.Settings.ProjectSettingEditor`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L13) | Describes one Editor presentation for a strongly typed runtime project setting. | — | — | - -### `Inno.Editor.Settings.ProjectSettingEditor`1` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Settings.ProjectSettingEditor.ProjectSettingEditor()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L173) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract void Inno.Editor.Settings.ProjectSettingEditor.OnDraw(TSetting setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L184) | Draws the isolated staged value. Mutations remain local until the Settings frontend applies them.
`setting`:The isolated current-generation setting snapshot. | — | — | -| [`public abstract class Inno.Editor.Settings.ProjectSettingEditor where TSetting : class, Inno.Core.Serialization.ISerializable : Inno.Editor.Settings.ProjectSettingEditor`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L173) | Provides a typed drawing extension for one runtime project setting protocol. | — | — | - -### `Inno.Editor.Settings.ProjectSettingPathAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Settings.ProjectSettingPathAttribute.ProjectSettingPathAttribute(string path, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L23) | Creates a project setting placement.
`path`:The slash-delimited field path beginning with Project/.
`order`:The stable order among fields in the same section. | — | `System.ArgumentException`:Thrown when path is outside the Project root or contains an empty segment. | -| [`public int Inno.Editor.Settings.ProjectSettingPathAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L46) | Gets the stable order among fields in the same section. | — | — | -| [`public string Inno.Editor.Settings.ProjectSettingPathAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L41) | Gets the normalized complete placement path. | — | — | -| [`public sealed class Inno.Editor.Settings.ProjectSettingPathAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L9) | Places a strongly typed project setting editor under the Project root of the unified Settings window. | — | — | - -### `Inno.Editor.Settings.ProjectSettingsEditor` - + + + + + + + @@ -334,24 +218,99 @@ History 事务;这允许一个较大的运行时设置协议在 UI 中拆成 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public bool Inno.Editor.Settings.ProjectSettingsEditor.Apply(System.Collections.Generic.IReadOnlyDictionary values, System.Collections.Generic.IReadOnlySet? resets = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L137) | Atomically applies project-authored overrides and records one stable history entry.
返回:true when the project override document changed.
`values`:Staged exact-type values keyed by stable setting identity.
`resets`:Setting identities whose project overrides should be removed. | — | `System.ArgumentNullException`:Thrown when values is null. | -| [`public Inno.Core.Serialization.ISerializable Inno.Editor.Settings.ProjectSettingsEditor.Get(Inno.Editor.Settings.ProjectSettingEditor definition)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L81) | Creates an isolated editable snapshot for one registered presentation.
返回:An isolated current-generation value.
`definition`:The active presentation whose runtime protocol is requested. | — | `System.ArgumentNullException`:Thrown when definition is null.
`System.InvalidOperationException`:Thrown when the runtime setting definition is unavailable. | -| [`public Inno.Core.Serialization.ISerializable Inno.Editor.Settings.ProjectSettingsEditor.GetComposedDefault(Inno.Editor.Settings.ProjectSettingEditor definition)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L108) | Creates the composed host and Plugin default without the project override.
返回:An isolated composed default value.
`definition`:The active presentation whose default is requested. | — | `System.ArgumentNullException`:Thrown when definition is null.
`System.InvalidOperationException`:Thrown when the runtime setting definition is unavailable. | -| [`protected override void Inno.Editor.Settings.ProjectSettingsEditor.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L162) | Releases resources retained by this feature after it has stopped. | — | — | +| [`public abstract class Inno.Editor.Settings.EditorSetting`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L10) | Defines one path-addressed Settings page or one custom-drawn Settings field. | — | — | +| [`protected Inno.Editor.Settings.EditorSetting.EditorSetting()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L23) | Creates a Settings definition. A field supplies its persisted default through ; a page keeps the base implementation. | — | — | +| [`public string Inno.Editor.Settings.EditorSetting.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L30) | Gets the complete slash-delimited identity and placement path. | — | — | +| [`public string Inno.Editor.Settings.EditorSetting.pagePath { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L35) | Gets the page that owns this field, or this page's own path for a page definition. | — | — | +| [`public string Inno.Editor.Settings.EditorSetting.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L40) | Gets the display label derived from the final path segment. | — | — | +| [`public int Inno.Editor.Settings.EditorSetting.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L45) | Gets the stable order among fields with the same section and label. | — | — | +| [`public bool Inno.Editor.Settings.EditorSetting.hasValue { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L50) | Gets whether this definition owns one persisted JSON object. | — | — | +| [`public virtual Inno.Editor.Settings.EditorSettingObject Inno.Editor.Settings.EditorSetting.defaultValue { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L59) | Creates the default object for a field. Page definitions keep the base implementation, whose internal value is . | — | — | +| [`public virtual string Inno.Editor.Settings.EditorSetting.section { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L65) | Gets the section heading used to group fields alphabetically. Definitions without a section keep the base implementation, whose internal value is . | — | — | +| [`public virtual string Inno.Editor.Settings.EditorSetting.description { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L70) | Gets the page or field explanation displayed by the Settings frontend. | — | — | +| [`public bool Inno.Editor.Settings.EditorSetting.Draw(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L85) | Draws this field inside the frontend-managed content container.
`setting`: The isolated mutable object being edited. | when the staged object differs from the value it held when this frontend first drew that object. | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Editor.Settings.EditorSetting.IsDefault(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L110) | Determines whether a staged field object equals this definition's bound default value.
`setting`: The isolated staged object to compare. | when every stored property equals the field default. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when this definition describes a page rather than a field. | +| [`protected virtual void Inno.Editor.Settings.EditorSetting.OnDraw(Inno.Editor.Settings.EditorSettingObject setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSetting.cs#L126) | Draws the field using the staged JSON object owned by the Settings frontend. A type that does not override this method describes a page.
`setting`: The isolated mutable object being edited. | — | — | +| [`public sealed class Inno.Editor.Settings.EditorSettingObject : Inno.Core.Serialization.ISerializable`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L13) | Represents one isolated, strongly typed object exposed to a Settings field and its consumers. | — | — | +| [`public Inno.Editor.Settings.EditorSettingObject.EditorSettingObject()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L21) | Creates an empty Settings object. | — | — | +| [`public bool Inno.Editor.Settings.EditorSettingObject.GetAsBoolean(string name, bool defaultValue = false)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L42) | Gets a Boolean property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored Boolean or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsBoolean(string name, bool value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L56) | Sets a Boolean property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public int Inno.Editor.Settings.EditorSettingObject.GetAsInt32(string name, int defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L73) | Gets a 32-bit signed integer property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored integer or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt32(string name, int value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L87) | Sets a 32-bit signed integer property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public uint Inno.Editor.Settings.EditorSettingObject.GetAsUInt32(string name, uint defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L104) | Gets a 32-bit unsigned integer property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored integer or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt32(string name, uint value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L118) | Sets a 32-bit unsigned integer property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public long Inno.Editor.Settings.EditorSettingObject.GetAsInt64(string name, long defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L135) | Gets a 64-bit signed integer property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored integer or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt64(string name, long value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L149) | Sets a 64-bit signed integer property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public ulong Inno.Editor.Settings.EditorSettingObject.GetAsUInt64(string name, ulong defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L166) | Gets a 64-bit unsigned integer property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored integer or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt64(string name, ulong value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L180) | Sets a 64-bit unsigned integer property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public float Inno.Editor.Settings.EditorSettingObject.GetAsSingle(string name, float defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L197) | Gets a single-precision property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored number or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsSingle(string name, float value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L214) | Sets a finite single-precision property.
`name`: The stable property name.
`value`: The finite value to store. | — | `System.ArgumentOutOfRangeException`: Thrown when is not finite. | +| [`public double Inno.Editor.Settings.EditorSettingObject.GetAsDouble(string name, double defaultValue = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L235) | Gets a double-precision property, or a fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent. | The stored number or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsDouble(string name, double value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L252) | Sets a finite double-precision property.
`name`: The stable property name.
`value`: The finite value to store. | — | `System.ArgumentOutOfRangeException`: Thrown when is not finite. | +| [`public string? Inno.Editor.Settings.EditorSettingObject.GetAsString(string name, string? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L273) | Gets a string property, or a fallback when the property is absent or null.
`name`: The stable property name.
`defaultValue`: The value returned when the property is absent or null. | The stored string or . | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsString(string name, string? value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L287) | Sets a nullable string property.
`name`: The stable property name.
`value`: The value to store. | — | — | +| [`public bool[] Inno.Editor.Settings.EditorSettingObject.GetAsBooleanArray(string name, bool[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L304) | Gets a Boolean array property, or a copied fallback when the property is absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsBooleanArray(string name, bool[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L318) | Sets a Boolean array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public int[] Inno.Editor.Settings.EditorSettingObject.GetAsInt32Array(string name, int[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L335) | Gets a 32-bit signed integer array property, or a copied fallback when absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsInt32Array(string name, int[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L349) | Sets a 32-bit signed integer array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public uint[] Inno.Editor.Settings.EditorSettingObject.GetAsUInt32Array(string name, uint[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L366) | Gets a 32-bit unsigned integer array property, or a copied fallback when absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsUInt32Array(string name, uint[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L380) | Sets a 32-bit unsigned integer array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public float[] Inno.Editor.Settings.EditorSettingObject.GetAsSingleArray(string name, float[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L397) | Gets a single-precision array property, or a copied fallback when absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsSingleArray(string name, float[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L411) | Sets a single-precision array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public double[] Inno.Editor.Settings.EditorSettingObject.GetAsDoubleArray(string name, double[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L428) | Gets a double-precision array property, or a copied fallback when absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsDoubleArray(string name, double[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L442) | Sets a double-precision array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public string?[] Inno.Editor.Settings.EditorSettingObject.GetAsStringArray(string name, string?[]? defaultValue = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L459) | Gets a nullable string array property, or a copied fallback when absent.
`name`: The stable property name.
`defaultValue`: The array copied when the property is absent. | An independently owned array. | — | +| [`public void Inno.Editor.Settings.EditorSettingObject.SetAsStringArray(string name, string?[] value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingObject.cs#L473) | Sets a nullable string array property by value.
`name`: The stable property name.
`value`: The array copied into the object. | — | — | +| [`public sealed class Inno.Editor.Settings.EditorSettingPathAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L9) | Places an at an arbitrary string Settings path. | — | — | +| [`public Inno.Editor.Settings.EditorSettingPathAttribute.EditorSettingPathAttribute(string path, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L24) | Creates a path placement. A definition that overrides its drawing method becomes a field; a definition that keeps the default drawing method describes the page at the complete path.
`path`: The slash-delimited page and field path.
`order`: The stable order among fields with the same section and label. | — | `System.ArgumentException`: Thrown when contains an empty segment or is outside the Editor root. | +| [`public string Inno.Editor.Settings.EditorSettingPathAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L43) | Gets the normalized path including the field label. | — | — | +| [`public int Inno.Editor.Settings.EditorSettingPathAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/EditorSettingPathAttribute.cs#L48) | Gets the stable order among fields with the same section and label. | — | — | +| [`public abstract class Inno.Editor.Settings.ProjectSettingEditor`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L13) | Describes one Editor presentation for a strongly typed runtime project setting. | — | — | +| [`public Inno.Core.Settings.ProjectSettingId Inno.Editor.Settings.ProjectSettingEditor.settingId { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L26) | Gets the stable runtime project setting protocol read from the target setting type's by the Editor catalog. | — | — | +| [`public string Inno.Editor.Settings.ProjectSettingEditor.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L31) | Gets the complete slash-delimited placement path. | — | — | +| [`public string Inno.Editor.Settings.ProjectSettingEditor.pagePath { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L36) | Gets the page that owns this field. | — | — | +| [`public string Inno.Editor.Settings.ProjectSettingEditor.label { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L41) | Gets the display label derived from the final path segment. | — | — | +| [`public int Inno.Editor.Settings.ProjectSettingEditor.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L46) | Gets the stable order among fields in the same section. | — | — | +| [`public virtual string Inno.Editor.Settings.ProjectSettingEditor.section { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L51) | Gets the section heading used to group this field. | — | — | +| [`public virtual string Inno.Editor.Settings.ProjectSettingEditor.description { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L56) | Gets the explanation displayed by the unified Settings frontend. | — | — | +| [`public bool Inno.Editor.Settings.ProjectSettingEditor.Draw(Inno.Core.Serialization.ISerializable value)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L71) | Draws one isolated staged value through this presentation.
`value`: The exact setting type owned by this presentation. | when the value differs from its first drawn state. | `System.ArgumentException`: Thrown when has another runtime type. | +| [`public bool Inno.Editor.Settings.ProjectSettingEditor.ValuesEqual(Inno.Core.Serialization.ISerializable left, Inno.Core.Serialization.ISerializable right)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L89) | Compares two exact-type values through their native serialized property data.
`left`: The first setting value.
`right`: The second setting value. | when both values contain equal serialized properties. | `System.ArgumentException`: Thrown when either value has another runtime type. | +| [`protected Inno.Editor.Settings.ProjectSettingEditor.ProjectSettingEditor()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L13) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Editor.Settings.ProjectSettingEditor where TSetting : class, Inno.Core.Serialization.ISerializable : Inno.Editor.Settings.ProjectSettingEditor`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L173) | Provides a typed drawing extension for one runtime project setting protocol.
`TSetting`: The exact serializable setting type registered by the runtime Plugin or host. | — | — | +| [`protected abstract void Inno.Editor.Settings.ProjectSettingEditor.OnDraw(TSetting setting)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L184) | Draws the isolated staged value. Mutations remain local until the Settings frontend applies them.
`setting`: The isolated current-generation setting snapshot. | — | — | +| [`protected Inno.Editor.Settings.ProjectSettingEditor.ProjectSettingEditor()`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingEditor.cs#L173) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Settings.ProjectSettingPathAttribute : System.Attribute`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L9) | Places a strongly typed project setting editor under the Project root of the unified Settings window. | — | — | +| [`public Inno.Editor.Settings.ProjectSettingPathAttribute.ProjectSettingPathAttribute(string path, int order = 0)`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L23) | Creates a project setting placement.
`path`: The slash-delimited field path beginning with Project/.
`order`: The stable order among fields in the same section. | — | `System.ArgumentException`: Thrown when is outside the Project root or contains an empty segment. | +| [`public string Inno.Editor.Settings.ProjectSettingPathAttribute.path { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L41) | Gets the normalized complete placement path. | — | — | +| [`public int Inno.Editor.Settings.ProjectSettingPathAttribute.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Definitions/ProjectSettingPathAttribute.cs#L46) | Gets the stable order among fields in the same section. | — | — | +| [`public sealed class Inno.Editor.Settings.EditorSettings : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L16) | Owns the discovered Settings catalog and the project-root Settings document. | — | — | +| [`public System.IDisposable Inno.Editor.Settings.EditorSettings.ListenChanges(System.Action observer)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L80) | Observes committed Apply, Undo and Redo changes through an owned Core Event hub.
`observer`: Synchronous callback reading the current committed settings; observer failures are logged and isolated. | A subscription owner that must be disposed before its feature or extension generation retires. | `System.ArgumentNullException`: The observer is null. | +| [`public long Inno.Editor.Settings.EditorSettings.catalogRevision { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L103) | Gets the current discovered type-catalog revision. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Settings.EditorSettings.definitions { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L107) | Gets all discovered path definitions in deterministic path order. | — | — | +| [`public Inno.Editor.Settings.EditorSettingObject Inno.Editor.Settings.EditorSettings.Get(string path)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L121) | Reads an isolated effective object from one complete Settings path.
`path`: The slash-delimited Settings field path. | The stored object, or an isolated copy of the field's default object. | `System.ArgumentException`: Thrown when is invalid, missing, or describes a page. | +| [`public bool Inno.Editor.Settings.EditorSettings.Apply(System.Collections.Generic.IReadOnlyDictionary values, System.Collections.Generic.IReadOnlySet? resets = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L154) | Atomically applies staged field objects as one shared Undo and Redo history entry.
`values`: The complete or partial path-addressed objects to apply from the active catalog.
`resets`: Optional paths whose persisted overrides are removed during the same Apply. | when the persisted document changed and one history entry was recorded. | `System.ArgumentNullException`: Thrown when is or contains a null object.
`System.ArgumentException`: Thrown when a supplied path is invalid, absent, or describes a page.
`System.IO.IOException`: Thrown when the project-root document or its history payload cannot be written.
`System.UnauthorizedAccessException`: Thrown when the project-root document is inaccessible. | +| [`protected override void Inno.Editor.Settings.EditorSettings.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs#L231) | Releases resources retained by this feature after it has stopped. | — | — | +| [`public sealed class Inno.Editor.Settings.ProjectSettingsEditor : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L17) | Owns the reloadable Editor presentations and history-aware project override workflow for runtime settings. | — | — | | [`public long Inno.Editor.Settings.ProjectSettingsEditor.catalogRevision { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L59) | Gets the active project setting Editor catalog revision. | — | — | | [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Settings.ProjectSettingsEditor.definitions { get; }`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L64) | Gets all active strongly typed project setting presentations. | — | — | -| [`public sealed class Inno.Editor.Settings.ProjectSettingsEditor : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L17) | Owns the reloadable Editor presentations and history-aware project override workflow for runtime settings. | — | — | +| [`public Inno.Core.Serialization.ISerializable Inno.Editor.Settings.ProjectSettingsEditor.Get(Inno.Editor.Settings.ProjectSettingEditor definition)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L81) | Creates an isolated editable snapshot for one registered presentation.
`definition`: The active presentation whose runtime protocol is requested. | An isolated current-generation value. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the runtime setting definition is unavailable. | +| [`public Inno.Core.Serialization.ISerializable Inno.Editor.Settings.ProjectSettingsEditor.GetComposedDefault(Inno.Editor.Settings.ProjectSettingEditor definition)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L108) | Creates the composed host and Plugin default without the project override.
`definition`: The active presentation whose default is requested. | An isolated composed default value. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the runtime setting definition is unavailable. | +| [`public bool Inno.Editor.Settings.ProjectSettingsEditor.Apply(System.Collections.Generic.IReadOnlyDictionary values, System.Collections.Generic.IReadOnlySet? resets = null)`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L137) | Atomically applies project-authored overrides and records one stable history entry.
`values`: Staged exact-type values keyed by stable setting identity.
`resets`: Setting identities whose project overrides should be removed. | when the project override document changed. | `System.ArgumentNullException`: Thrown when is . | +| [`protected override void Inno.Editor.Settings.ProjectSettingsEditor.OnDispose()`](../../src/composition/editor/framework/Inno.Editor.Settings/Runtime/ProjectSettingsEditor.cs#L162) | Releases resources retained by this feature after it has stopped. | — | — | ## 项目依赖 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization.Generators](../core/Inno.Core.Serialization.Generators.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Events` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Core.Serialization.Generators` | 编译器 Analyzer;不进入运行时 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Core.Settings` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Shaders.ImGui.md b/docs/editor/Inno.Editor.Shaders.ImGui.md new file mode 100644 index 000000000..2333141e4 --- /dev/null +++ b/docs/editor/Inno.Editor.Shaders.ImGui.md @@ -0,0 +1,151 @@ +# Inno.Editor.Shaders.ImGui + +[Editor 索引](README.md) · [Wiki 首页](../README.md) · [UI 架构](../architecture/EDITOR_UI_ARCHITECTURE.md) + +## 职责与边界 + +Shader 与 Material 的可复用检查显示。依赖 headless Shaders/Rendering/Assets 以及 Inspection、ImGui;不引用 Inspector 或 ShaderEditor Panel。 + +## 依赖与初始化 + +产品通过 `EditorProduct.props` 显式部署此程序集。由当前 TypeRegistry/Editor Registry 发现声明并在候选成功后切换;不扫描目录、不建立独立注册表或全局状态。 + +## API 与扩展点 + +ShaderNodeDrawerAttribute、ShaderNodeDrawer、ShaderNodeDrawContext、ShaderNodePresentation 与 registry 描述节点显示;Material inspectors 使用共享草稿;ShaderPropertyInspector 处理参数类型和混合值。ShaderPreviews 只显示当前 preview service 的结果。 + +## 工作流 + +扩展继承 ShaderNodeDrawer,Attribute 携带稳定节点 ID。通过 DrawProperty 使用统一属性手势;自定义连续编辑通过当前手势 mergeKey 记录。不要持有 draw context 或 canonical asset。 + +## 错误、生命周期与热重载 + +控件只在当前 EditorUiSession 的 owner thread、作用域与原生上下文中使用。跨帧只保留稳定 ID 和中立状态;Missing、只读和未赋值状态保持区别。手势失败保留 History 所有权和诊断;扩展在所属 generation 退休,不保留插件 Type、delegate 或实例。 + +所有公开签名的当前映射见本页 API 清单;内部实现不构成稳定扩展契约。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public static class Inno.Editor.Shaders.ImGui.ShaderPropertyInspector`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs#L13) | Uses the shared Inspector drawers for both Shader defaults and Material overrides. | — | — | +| [`public static void Inno.Editor.Shaders.ImGui.ShaderPropertyInspector.Draw(Inno.Editor.Inspection.InspectionDrawContext context, object owner, string path, Inno.Rendering.Assets.ShaderPropertyDefinition property, Inno.Rendering.Assets.MaterialValue value, System.Action setter, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly, Inno.Editor.Shaders.ShaderParameterPresentation? presentation = null)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs#L45) | Draws a typed value without choosing its persistence or history policy.
`context`: Current Inspector invocation.
`owner`: Detached draft owner used for widget identity.
`path`: Stable draft property path.
`property`: Current Shader declaration.
`value`: Current default or override, never mutated.
`setter`: Writes only the host's detached draft.
`edits`: Host-owned gesture and history service.
`readOnly`: Whether this source is editable.
`presentation`: Optional Editor-only description and scalar bounds; it never changes inherited or stored values. | — | — | +| [`public static bool Inno.Editor.Shaders.ImGui.ShaderPropertyInspector.Compatible(Inno.Rendering.ShaderPropertyType type, Inno.Rendering.Assets.MaterialValueKind kind)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs#L95) | Tests whether a stored override has the exact representation expected by a declaration.
`type`: Declared material type.
`kind`: Stored representation. | True only for supported matching kinds; no lossy conversion is performed. | — | +| [`public static Inno.Rendering.Assets.MaterialValue Inno.Editor.Shaders.ImGui.ShaderPropertyInspector.ApplyEdit(Inno.Rendering.ShaderPropertyType type, Inno.Rendering.Assets.MaterialValue before, Inno.Rendering.Assets.MaterialValue edited, Inno.Rendering.Assets.MaterialValue target)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs#L125) | Applies the edited components of a representative value without replacing unrelated mixed components.
`type`: Common exact Shader property type.
`before`: Representative value before the control gesture sample.
`edited`: Representative value after this sample.
`target`: Another selected Material's current effective value. | A detached value preserving the target's untouched channels, texture or sampler. | — | +| [`public sealed class Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute : System.Attribute`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L13) | Registers Editor-only node controls independently from a node's shader compiler. | — | — | +| [`public Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.ShaderNodeDrawerAttribute(string definitionId, string displayName = "", string createPath = "", int createOrder = 0, bool separatorBefore = false)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L33) | Associates the drawer with one stable node definition.
`definitionId`: The compiler-independent graph node identity.
`displayName`: Optional artist-facing name used in node headers and creation menus.
`createPath`: Optional slash-delimited path below Create. Empty uses the host's built-in classification.
`createOrder`: Stable ordering value among sibling creation groups and nodes.
`separatorBefore`: Whether the contributed creation group is preceded by a separator at its own level. | — | — | +| [`public string Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.definitionId { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L51) | Gets the stable node identity handled by this drawer. | — | — | +| [`public string Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.displayName { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L55) | Gets the optional presentation-only name; it does not participate in Shader compilation or identity. | — | — | +| [`public string Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.createPath { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L59) | Gets the optional creation-menu path below Create. | — | — | +| [`public int Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.createOrder { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L63) | Gets the creation-menu order. | — | — | +| [`public bool Inno.Editor.Shaders.ImGui.ShaderNodeDrawerAttribute.separatorBefore { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L67) | Gets whether the contributed creation group starts a new visual section. | — | — | +| [`public readonly record struct Inno.Editor.Shaders.ImGui.ShaderNodePresentation : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Immutable authoring presentation contributed for one Shader node definition. | — | — | +| [`public Inno.Editor.Shaders.ImGui.ShaderNodePresentation.ShaderNodePresentation(string displayName, string createPath, int createOrder, bool separatorBefore)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L90) | Creates presentation metadata for one Shader node definition.
`displayName`: Optional artist-facing node name.
`createPath`: Optional slash-delimited creation-menu path.
`createOrder`: Stable ordering value within the creation menu.
`separatorBefore`: Whether the contributed group starts a visual section. | — | — | +| [`public string Inno.Editor.Shaders.ImGui.ShaderNodePresentation.displayName { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L105) | Gets the optional artist-facing node name. | — | — | +| [`public string Inno.Editor.Shaders.ImGui.ShaderNodePresentation.createPath { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L109) | Gets the optional slash-delimited creation-menu path. | — | — | +| [`public int Inno.Editor.Shaders.ImGui.ShaderNodePresentation.createOrder { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L113) | Gets the stable ordering value within the creation menu. | — | — | +| [`public bool Inno.Editor.Shaders.ImGui.ShaderNodePresentation.separatorBefore { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L117) | Gets whether the contributed group starts a visual section. | — | — | +| [`public override string Inno.Editor.Shaders.ImGui.ShaderNodePresentation.ToString()`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Shaders.ImGui.ShaderNodePresentation.operator !=(Inno.Editor.Shaders.ImGui.ShaderNodePresentation left, Inno.Editor.Shaders.ImGui.ShaderNodePresentation right)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Shaders.ImGui.ShaderNodePresentation.operator ==(Inno.Editor.Shaders.ImGui.ShaderNodePresentation left, Inno.Editor.Shaders.ImGui.ShaderNodePresentation right)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Shaders.ImGui.ShaderNodePresentation.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Shaders.ImGui.ShaderNodePresentation.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Shaders.ImGui.ShaderNodePresentation.Equals(Inno.Editor.Shaders.ImGui.ShaderNodePresentation other)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L73) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public abstract class Inno.Editor.Shaders.ImGui.ShaderNodeDrawer`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L123) | Provides reloadable Inspector controls; instances must not retain frame contexts or asset objects. | — | — | +| [`public abstract void Inno.Editor.Shaders.ImGui.ShaderNodeDrawer.Draw(Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext context)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L131) | Draws the selected node's controls using the shared Inspector styling.
`context`: Frame-scoped value access and undoable writes. | — | — | +| [`protected Inno.Editor.Shaders.ImGui.ShaderNodeDrawer.ShaderNodeDrawer()`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L123) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L137) | Exposes detached node values and one-gesture writes without exposing the live graph document. | — | — | +| [`public Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.ShaderNodeDrawContext(Inno.Core.Graphs.GraphNodeRecord node, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context, System.Action write, Inno.Editor.Rendering.IEditorPreviewService previews, Inno.Editor.Inspection.InspectionDrawContext inspection, bool readOnly)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L169) | Creates invocation-scoped access for a graph or Inspector host.
`node`: Detached node values for this invocation.
`serialization`: Current owner converter registry.
`context`: Complete owner reference context.
`write`: Host callback recording changes in the shared draft history.
`previews`: Current generation preview service.
`inspection`: Shared Inspector context, including native property controls and drag/drop.
`readOnly`: Whether the source belongs to an immutable installation. | — | — | +| [`public Inno.Editor.Rendering.IEditorPreviewService Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.previews { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L190) | Gets the shared generation-scoped preview service for optional inline texture previews; valid only during this draw. | — | — | +| [`public Inno.Core.Graphs.GraphNodeId Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.nodeId { get; }`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L194) | Gets the stable node identity for Editor widget IDs. | — | — | +| [`public T Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.Read(string key, T defaultValue)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L211) | Reads a detached property value; missing values use the node's declared default.
`key`: Stable node-local property key.
`defaultValue`: Default used only when the property is absent.
`T`: Native serializable property type. | The detached value. Malformed stored data is not silently replaced. | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.Write(string key, T value, string? mergeKey = null)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L230) | Records an unsaved draft edit through shared history; only an explicit document save applies it to the asset.
`key`: Stable node-local property key.
`value`: New detached value.
`mergeKey`: The current shared gesture key, or null for an independent edit.
`T`: Native serializable property type. | — | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext.DrawProperty(string key, string label, T defaultValue)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs#L255) | Draws a node property through the existing Inspector controls, including precise numbers and asset picking.
`key`: Stable node property identity.
`label`: Artist-facing property label.
`defaultValue`: Value used only when no property is stored.
`T`: Supported native property type. | — | — | +| [`public sealed class Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry : System.IDisposable`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L12) | Shares generation-scoped node presentation between graph and Inspector hosts. | — | — | +| [`public Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry.ShaderNodeDrawerRegistry(Inno.Extensibility.Types.TypeCatalog types)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L22) | Creates a presentation registry owned by the current editor feature lifetime.
`types`: Shared type catalog, which must outlive this registry. | — | — | +| [`public bool Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry.TryDraw(string definitionId, Inno.Editor.Shaders.ImGui.ShaderNodeDrawContext context)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L40) | Invokes optional node controls within one generation lease.
`definitionId`: Stable node definition identity.
`context`: Invocation-scoped host values, history callback and previews. | True when a registered drawer rendered the controls; false leaves presentation to the host. | — | +| [`public string? Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry.GetDisplayName(string definitionId)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L60) | Resolves an optional artist-facing name without executing a drawer or retaining its generation.
`definitionId`: Stable compiler-independent node identity. | The contributed name, or null when the host should use its generic presentation. | — | +| [`public bool Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry.TryGetPresentation(string definitionId, out Inno.Editor.Shaders.ImGui.ShaderNodePresentation presentation)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L78) | Gets optional creation-menu presentation without executing the drawer.
`definitionId`: Stable compiler-independent node identity.
`presentation`: Receives immutable creation-menu presentation when registered. | True when the current generation contributes presentation for the node; otherwise false. | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderNodeDrawerRegistry.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs#L89) | Retires the current presentation providers through the shared lifecycle. | — | — | +| [`public sealed class Inno.Editor.Shaders.ImGui.ShaderPreviews : Inno.Editor.Core.EditorModule`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L21) | Hosts isolated Shader and Material images using domain-contributed preview pipelines. | — | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderPreviews.DrawMaterial(System.Guid ownerId, Inno.Rendering.Assets.MaterialAsset material, float logicalSize)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L52) | Draws detached Material overrides using its Shader's compiled contract.
`ownerId`: Persistent document identity; no Material is retained under this identity.
`material`: Invocation-local Material values.
`logicalSize`: Positive square image size in logical pixels. | — | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderPreviews.Draw(System.Guid ownerId, Inno.Rendering.Assets.MaterialAsset material, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot compilation, float logicalSize)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L88) | Draws a compiled draft without modifying the source Shader or its canonical runtime publication.
`ownerId`: Persistent document identity, independent of the canonical Shader publication.
`material`: Detached defaults or overrides with the current Shader reference.
`compilation`: Isolated draft compiler result, including explicit last-good state.
`logicalSize`: Positive square image size in logical pixels. | — | — | +| [`public void Inno.Editor.Shaders.ImGui.ShaderPreviews.Release(System.Guid ownerId)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L149) | Releases this document's viewport and scoped GPU publication without touching other consumers.
`ownerId`: Closing or disabled preview owner. | — | — | +| [`protected override void Inno.Editor.Shaders.ImGui.ShaderPreviews.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L161) | Advances this feature using the current runtime state.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Shaders.ImGui.ShaderPreviews.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs#L174) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | + +## 项目依赖 + +| 工程 | 边界 | +| --- | --- | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Core.Graphs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Rendering.Assets` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Editor.Rendering` | 公开签名依赖 | +| `Inno.Editor.Shaders` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Inspection` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Shaders.md b/docs/editor/Inno.Editor.Shaders.md index 464034f5b..ee0592ce4 100644 --- a/docs/editor/Inno.Editor.Shaders.md +++ b/docs/editor/Inno.Editor.Shaders.md @@ -1,231 +1,56 @@ # Inno.Editor.Shaders -[Editor 索引](README.md) · [Wiki 首页](../README.md) · [Shader Editor](Inno.Editor.Panel.ShaderEditor.md) · [Shader 编译](../rendering/Inno.Rendering.Shaders.md) +[Editor 索引](README.md) · [Wiki 首页](../README.md) · [Shader 显示扩展](Inno.Editor.Shaders.ImGui.md) · [Rendering](Inno.Editor.Rendering.md) ## 职责与边界 -可复用的 Shader/Material 创作功能。节点编译属于 Rendering.Shaders;呈现协议独立于具体 Panel。节点配置在统一 Inspector 编辑,画布保留摘要与连接。 +本项目拥有无界面的 Material 源草稿与 Shader 创作数据服务。绘制、节点 Drawer 和预览控件由 Shaders.ImGui 持有;本项目不引用 ImGui、Panel 或原生 UI。 -优先用 `.ishader` 的 Node Inputs / Node Outputs 定义组合型多输入、多输出节点;宿主会自动提供创建菜单、端口 Inspector、依赖、内联和循环诊断。只有新增公共 IR 无法表达的原语时才实现 `IShaderNodeCompiler`,只有节点确实需要专用交互时才增加 `ShaderNodeDrawer`。 +## 初始化与依赖 -## 初始化与生命周期 +MaterialDocuments 由 Editor Module Registry 创建,依赖 AssetPipeline、SerializationRegistry、EditorInteractions 和中立 IEditorPreviewService。底层 AssetDraftDocuments 提供编解码、恢复、dirty 状态和中立 History。Shutdown 清理本代缓存并保留未保存的 recovery。 -宿主在 TypeCatalog 与预览服务建立后创建 ShaderNodeDrawerRegistry,在 Editor feature 停止时 Dispose。 -每次查询与绘制持有共享 generation operation;候选发现重复 ID 时失败,旧 provider 按 TypeRegistry 的事务与退休协议管理。 -Drawer 不得保存 DrawContext、PreviewHandle、Asset 或历史回调到下一帧/下一代。 +## 公开 API 与工作流 -## 公开 API +Open(path) 打开源,Read(id) 返回 detached Material,Replace(id, candidate, mergeKey) 立即记录修改。ReplaceMany 预验证并原子记录多文档 sample。连续输入由共享 EditorEditGesture 定义;mergeKey 不代表保存。Save/Revert/Close 通过统一 documents 服务完成。 -| API | 语义 | -| --- | --- | -| `ShaderNodeDrawerAttribute(definitionId, displayName, createPath, createOrder, separatorBefore)` | 稳定节点 ID、可选显示名称与节点创建菜单呈现;插件可贡献自己的分组、顺序和同级分隔,不参与编译语义 | -| `ShaderNodeDrawer.Draw(context)` | 统一 Inspector 中的帧内绘制入口 | -| `ShaderNodeDrawContext(node, serialization, context, write, previews, inspection, readOnly)` | 独立节点快照、完整 owner context、统一草稿 History 写入、预览与共享 Inspector 上下文 | -| `nodeId`、`previews` | 稳定控件身份和当前 generation 预览服务 | -| `Read(key, defaultValue)` | 解码独立属性,缺失使用声明默认值,损坏明确失败 | -| `Write(key, value, continuous)` | 向宿主提交中立值;进入草稿 History,不保存或发布 Asset | -| `DrawProperty(key, label, defaultValue)` | 复用现有属性 Drawer、资产选择/拖放和数值控件;受草稿 History 与只读检查约束 | -| `ShaderNodeDrawerRegistry(types)`、`TryDraw(id, context)`、`Dispose()` | 按 generation 发现、调用和退休呈现;缺少 Drawer 时返回 false | -| `GetDisplayName(definitionId)` | 获取本代际中立显示名称;未贡献名称时返回 null,由宿主提供通用名称 | -| `TryGetPresentation(definitionId, out presentation)` | 获取本代际不可变创建菜单呈现;未贡献时由 Shader Editor 使用内置中立分类 | -| `MaterialDocuments.Open(path)`、`Read(id)`、`Replace(id, material, finishGesture)`、`Commit(id)` | 原生 Material 草稿;读回的是独立可编辑对象,显式保存前不发布 | -| `MaterialDocuments.ReplaceMany(candidates, finishGesture)`、`CommitMany(ids)` | 一组兼容材质共享一次 History 事务,不提供跨文件原子保存承诺 | -| `ShaderPropertyInspector.Draw(...)`、`Compatible(type, kind)` | Shader 默认值与 Material 覆盖复用属性 Drawer;Float/向量/线性 HDR Color/Matrix/Texture/Sampler,精确类型匹配 | -| `ShaderParameterPresentation`:`group`、`description`、`visible`、`hasRange`、`minimum`、`maximum` | Editor-only 参数展示。按稳定绑定 ID 共享,不改变已有数值,不进入运行时定义或 Player | -| `ShaderParameterPresentation.Read(graph, propertyId, serialization, context)`、`Write(...)` | 读写独立图中的原生展示元数据;调用方负责共享 History。范围必须有限且有序;不是资产保存 API | -| `ShaderPreviewProviderAttribute(contractId)`、`contractId` | 按开放 Shader Contract 注册预览;重复契约阻止候选激活 | -| `ShaderPreviewProvider.CreateLayer(context)` | 插件贡献几何、环境、Pass 输入和 Pipeline,不修改 Scene 或 canonical Material | -| `ShaderPreviewContext(resourceId, material, artifact, definition, diagnostics, pixelWidth, pixelHeight)` 及同名只读属性 | 帧内的独立发布范围、草稿值、不可变编译产物、精确接口、隔离诊断和像素尺寸;禁止跨帧保留 | -| `ShaderPreviews.DrawMaterial(ownerId, material, logicalSize)` | 对独立 Material 覆盖值执行实际 GPU 预览,不保存或应用源资产 | -| `ShaderPreviews.Draw(ownerId, material, compilation, logicalSize)` | Shader 草稿编译结果的实际 GPU 预览;失败时明确标识 last-good | -| `ShaderPreviews.Release(ownerId)` | 关闭预览并释放对应视口和 GPU 程序;未绘制的预览自动退休 | - -公共签名涉及 Graph、Serialization、Rendering Preview 与 TypeCatalog,其项目引用向下游公开。 -节点编译与脚本导出依赖仅作为实现依赖。 +ShaderParameterPresentation 存储 Editor-only 的 group、description、visible 和有限范围。它使用原生 Serialization、稳定绑定 ID 和 owner reference context;不修改 runtime Shader 接口或现有参数值。 ```csharp +using System; using InnoEditor.Shaders; -[ShaderNodeDrawer("example.surface")] -public sealed class SurfaceDrawer : ShaderNodeDrawer -{ - public override void Draw(ShaderNodeDrawContext context) - { - context.DrawProperty("gain", "Gain", 1f); - } +static void ReadCurrentDraft( + MaterialDocuments documents, + Guid assetId +) { + _ = documents.Read(assetId); } ``` -Editor 脚本导出 Drawer、预览 Provider、标记、帧内 Context、MaterialDocuments、ShaderPreviews 和 ShaderPropertyInspector,命名空间为 `InnoEditor.Shaders`。Registry 由宿主管理。 - -## 实际渲染预览 - -Material Inspector 顶部独立的 Preview 区域与 Shader Inspector 中始终可见的 Preview 共用此模块。没有可编辑属性与关键词时,Parameters 区域显示空状态说明。输出预览表示整个 Shader 的结果,不冒充任意中间节点的数值可视化。 -领域通过 `ShaderPreviewProvider` 提供与 Shader Contract 匹配的预览,使用普通 Render Graph 和独立材质覆盖绘制;预览资源由该预览作用域持有。 -不同领域通过自己的 Contract 提供网格、环境与 Pass 参数,通用引擎没有 Sprite 分支。若多种可预览 Contract 同时匹配,要求选择 Technique,不按发现顺序猜测。 - -预览 Shader 使用隔离编译产物;Material 使用当前 Shader 产物和独立覆盖。GPU 创建、反射/能力校验、绑定以及 last-good 复用正式资源链,但缓存键包含独立 owner scope,错误只进入预览 reporter。 -预览帧只保留本帧的 Provider 结果;后续状态仅保存稳定 ID、计数和中立诊断。宿主 generation 切换时统一清理视口及注册,Provider 通过 TypeRegistry 候选事务退休。 - -## Material 工作流 - -File Browser 的 Create 菜单支持创建 Material 和从选中 Shader 创建 Material。单击材质进入 Inspector;Command/Ctrl+单击可选择多个材质,兼容参数在同一 Inspector 中共同编辑。 -继承值与独立 override 分开显示,支持单项/全部重置、关键词、Technique,以及更换 Shader 后保留的未解析参数。 -颜色值按线性 RGBA、允许 HDR 输入,不对底层浮点数作显示精度截断。纹理选择/拖放与采样器使用既有属性 Drawer。 +## 生命周期、错误与热重载 -参数节点 Inspector 可编辑分组、hover 说明、材质可见性与 Float 编辑范围。多选材质只显示共同可见且类型兼容的参数,数值范围使用交集;不把改动前已超界的值静默钳制。隐藏是创作 UI 策略,不禁止运行时通过稳定 ID 设置参数。 -展示记录保存在 `inno.editor.parameter.*` 图 metadata,由同一参数的所有节点共享。语义指纹排除此保留前缀;仅改展示不重编译 GPU 程序。Material 使用已导入的 Shader 创作快照,按资产 contentVersion 缓存,不每帧重读整个图;未保存 Shader 草稿不会改变 Material Inspector。 +Canonical Material、引用资产与运行时发布值始终由 Assets owner 管理。草稿只保存中立 bytes,显式保存前不发布。源冲突保留草稿与诊断;重开文档后 History 按 persistent ID 恢复。缺少源或类型形成可恢复 barrier。不要跨 callback 保存 detached Asset、Type、preview 或 Registry 实例。 -保存复用 headless Document Service;Inspector Header 提供 Save/Revert,关闭确认和全部保存仍走同一服务,不另建状态系统。MaterialDocuments 组合 [AssetDraftDocuments](Inno.Editor.Assets.md),恢复数据在 Library/Editor/AssetDrafts/inno.material;外部修改冲突保留两份内容,不覆盖外部源。 -资产/节点多选只保存稳定 ID;Drawer 重新查询当前 generation,不把节点选择当作 File Browser 选择。 -完整渲染预览及 UI 验收仍见[当前验收](../architecture/CURRENT_ACCEPTANCE.md),不能把编译预览等同为画面预览。 +公开签名与对应源码见下方清单;其他内部类型不是稳定 API。 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Editor.Shaders.MaterialDocuments` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + - - - - + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Shaders.MaterialDocuments.Commit(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L92) | Finishes a draft gesture without saving its source.
`assetId`:Open Material source identity. | — | — | -| [`public void Inno.Editor.Shaders.MaterialDocuments.CommitMany(System.Collections.Generic.IEnumerable assetIds)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L115) | Completes a multi-document gesture as one shared History transaction.
`assetIds`:Participating persistent source identities. | — | — | -| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L172) | Initializes this feature when its owning runtime becomes active.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L186) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L179) | Advances this feature using the current runtime state.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public System.Guid Inno.Editor.Shaders.MaterialDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L54) | Opens native source, including a source with failed import, without publishing its draft.
返回:The persistent identity used for subsequent draft operations.
`path`:Mounted Material source path. | — | — | -| [`public Inno.Rendering.Assets.MaterialAsset Inno.Editor.Shaders.MaterialDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L65) | Reads a detached current-generation Material; referenced assets remain canonical read-only inputs.
返回:The current unsaved Material value.
`assetId`:Open Material source identity. | — | — | -| [`public void Inno.Editor.Shaders.MaterialDocuments.Replace(System.Guid assetId, Inno.Rendering.Assets.MaterialAsset candidate, bool finishGesture = true)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L79) | Changes a draft through shared History without modifying its source or Scene/Game.
`assetId`:Open Material source identity.
`candidate`:Detached edited Material.
`finishGesture`:Whether this sample finishes the active gesture. | — | — | -| [`public void Inno.Editor.Shaders.MaterialDocuments.ReplaceMany(System.Collections.Generic.IReadOnlyDictionary candidates, bool finishGesture = true)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L103) | Changes compatible selected drafts within one shared gesture.
`candidates`:Detached Material values keyed by persistent source identity.
`finishGesture`:Whether this sample finishes the shared gesture. | — | — | -| [`public sealed class Inno.Editor.Shaders.MaterialDocuments : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L24) | Owns Material authoring sessions; native source editing uses the shared asset draft lifecycle. | — | — | - -### `Inno.Editor.Shaders.ShaderInspectionSelection` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderInspectionSelection.ShaderInspectionSelection(System.Guid assetId, System.Collections.Generic.IEnumerable nodes)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L23) | Creates a neutral Inspector selection separate from the Asset Browser's navigation.
`assetId`:Persistent shader asset identity.
`nodes`:Selected stable node identities; empty selects document settings. | — | — | -| [`public System.Guid Inno.Editor.Shaders.ShaderInspectionSelection.assetId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L36) | Gets the persistent shader identity. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Shaders.ShaderInspectionSelection.nodes { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L40) | Gets selected stable node identities. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderInspectionSelection`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L11) | Identifies a shader document and selected nodes without retaining a graph, extension object or delegate. | — | — | - -### `Inno.Editor.Shaders.ShaderNodeDrawContext` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderNodeDrawContext.ShaderNodeDrawContext(Inno.Core.Graphs.GraphNodeRecord node, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context, System.Action write, Inno.Editor.Rendering.IEditorPreviewService previews, Inno.Editor.Inspection.InspectionDrawContext inspection, bool readOnly)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L172) | Creates invocation-scoped access for a graph or Inspector host.
`node`:Detached node values for this invocation.
`serialization`:Current owner converter registry.
`context`:Complete owner reference context.
`write`:Host callback recording changes in the shared draft history.
`previews`:Current generation preview service.
`inspection`:Shared Inspector context, including native property controls and drag/drop.
`readOnly`:Whether the source belongs to an immutable installation. | — | — | -| [`public void Inno.Editor.Shaders.ShaderNodeDrawContext.DrawProperty(string key, string label, T defaultValue)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L261) | Draws a node property through the existing Inspector controls, including precise numbers and asset picking.
`key`:Stable node property identity.
`label`:Artist-facing property label.
`defaultValue`:Value used only when no property is stored. | — | — | -| [`public T Inno.Editor.Shaders.ShaderNodeDrawContext.Read(string key, T defaultValue)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L215) | Reads a detached property value; missing values use the node's declared default.
返回:The detached value. Malformed stored data is not silently replaced.
`key`:Stable node-local property key.
`defaultValue`:Default used only when the property is absent. | — | — | -| [`public void Inno.Editor.Shaders.ShaderNodeDrawContext.Write(string key, T value, bool continuous = false)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L236) | Records an unsaved draft edit through shared history; only an explicit document save applies it to the asset.
`key`:Stable node-local property key.
`value`:New detached value.
`continuous`:Whether this sample belongs to the active text or numeric gesture. | — | — | -| [`public Inno.Core.Graphs.GraphNodeId Inno.Editor.Shaders.ShaderNodeDrawContext.nodeId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L198) | Gets the stable node identity for Editor widget IDs. | — | — | -| [`public Inno.Editor.Rendering.IEditorPreviewService Inno.Editor.Shaders.ShaderNodeDrawContext.previews { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L193) | Gets the shared generation-scoped preview service for optional inline texture previews; valid only during this draw. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderNodeDrawContext`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L140) | Exposes detached node values and one-gesture writes without exposing the live graph document. | — | — | - -### `Inno.Editor.Shaders.ShaderNodeDrawer` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Shaders.ShaderNodeDrawer.ShaderNodeDrawer()`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L126) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract void Inno.Editor.Shaders.ShaderNodeDrawer.Draw(Inno.Editor.Shaders.ShaderNodeDrawContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L134) | Draws the selected node's controls using the shared Inspector styling.
`context`:Frame-scoped value access and undoable writes. | — | — | -| [`public abstract class Inno.Editor.Shaders.ShaderNodeDrawer`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L126) | Provides reloadable Inspector controls; instances must not retain frame contexts or asset objects. | — | — | - -### `Inno.Editor.Shaders.ShaderNodeDrawerAttribute` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderNodeDrawerAttribute.ShaderNodeDrawerAttribute(string definitionId, string displayName = "", string createPath = "", int createOrder = 0, bool separatorBefore = false)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L34) | Associates the drawer with one stable node definition.
`definitionId`:The compiler-independent graph node identity.
`displayName`:Optional artist-facing name used in node headers and creation menus.
`createPath`:Optional slash-delimited path below Create. Empty uses the host's built-in classification.
`createOrder`:Stable ordering value among sibling creation groups and nodes.
`separatorBefore`:Whether the contributed creation group is preceded by a separator at its own level. | — | — | -| [`public int Inno.Editor.Shaders.ShaderNodeDrawerAttribute.createOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L63) | Gets the creation-menu order. | — | — | -| [`public string Inno.Editor.Shaders.ShaderNodeDrawerAttribute.createPath { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L59) | Gets the optional creation-menu path below Create. | — | — | -| [`public string Inno.Editor.Shaders.ShaderNodeDrawerAttribute.definitionId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L51) | Gets the stable node identity handled by this drawer. | — | — | -| [`public string Inno.Editor.Shaders.ShaderNodeDrawerAttribute.displayName { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L55) | Gets the optional presentation-only name; it does not participate in Shader compilation or identity. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderNodeDrawerAttribute.separatorBefore { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L67) | Gets whether the contributed creation group starts a new visual section. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderNodeDrawerAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L14) | Registers Editor-only node controls independently from a node's shader compiler. | — | — | - -### `Inno.Editor.Shaders.ShaderNodeDrawerRegistry` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderNodeDrawerRegistry.ShaderNodeDrawerRegistry(Inno.Extensibility.Types.TypeCatalog types)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L23) | Creates a presentation registry owned by the current editor feature lifetime.
`types`:Shared type catalog, which must outlive this registry. | — | — | -| [`public void Inno.Editor.Shaders.ShaderNodeDrawerRegistry.Dispose()`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L90) | Retires the current presentation providers through the shared lifecycle. | — | — | -| [`public string? Inno.Editor.Shaders.ShaderNodeDrawerRegistry.GetDisplayName(string definitionId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L61) | Resolves an optional artist-facing name without executing a drawer or retaining its generation.
返回:The contributed name, or null when the host should use its generic presentation.
`definitionId`:Stable compiler-independent node identity. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderNodeDrawerRegistry.TryDraw(string definitionId, Inno.Editor.Shaders.ShaderNodeDrawContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L41) | Invokes optional node controls within one generation lease.
返回:True when a registered drawer rendered the controls; false leaves presentation to the host.
`definitionId`:Stable node definition identity.
`context`:Invocation-scoped host values, history callback and previews. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderNodeDrawerRegistry.TryGetPresentation(string definitionId, out Inno.Editor.Shaders.ShaderNodePresentation presentation)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L79) | Gets optional creation-menu presentation without executing the drawer.
返回:True when the current generation contributes presentation for the node; otherwise false.
`definitionId`:Stable compiler-independent node identity.
`presentation`:Receives immutable creation-menu presentation when registered. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderNodeDrawerRegistry : System.IDisposable`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs#L12) | Shares generation-scoped node presentation between graph and Inspector hosts. | — | — | - -### `Inno.Editor.Shaders.ShaderNodePresentation` - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderNodePresentation.ShaderNodePresentation(string displayName, string createPath, int createOrder, bool separatorBefore)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L90) | Creates presentation metadata for one Shader node definition.
`displayName`:Optional artist-facing node name.
`createPath`:Optional slash-delimited creation-menu path.
`createOrder`:Stable ordering value within the creation menu.
`separatorBefore`:Whether the contributed group starts a visual section. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderNodePresentation.Equals(Inno.Editor.Shaders.ShaderNodePresentation other)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Editor.Shaders.ShaderNodePresentation.Equals(object obj)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Editor.Shaders.ShaderNodePresentation.GetHashCode()`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Editor.Shaders.ShaderNodePresentation.operator ==(Inno.Editor.Shaders.ShaderNodePresentation left, Inno.Editor.Shaders.ShaderNodePresentation right)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Editor.Shaders.ShaderNodePresentation.operator !=(Inno.Editor.Shaders.ShaderNodePresentation left, Inno.Editor.Shaders.ShaderNodePresentation right)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Editor.Shaders.ShaderNodePresentation.ToString()`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public int Inno.Editor.Shaders.ShaderNodePresentation.createOrder { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L115) | Gets the stable ordering value within the creation menu. | — | — | -| [`public string Inno.Editor.Shaders.ShaderNodePresentation.createPath { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L110) | Gets the optional slash-delimited creation-menu path. | — | — | -| [`public string Inno.Editor.Shaders.ShaderNodePresentation.displayName { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L105) | Gets the optional artist-facing node name. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderNodePresentation.separatorBefore { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L120) | Gets whether the contributed group starts a visual section. | — | — | -| [`public readonly record struct Inno.Editor.Shaders.ShaderNodePresentation : System.IEquatable`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs#L73) | Immutable authoring presentation contributed for one Shader node definition. | — | — | - -### `Inno.Editor.Shaders.ShaderParameterPresentation` - @@ -236,22 +61,16 @@ File Browser 的 Create 菜单支持创建 Material 和从选中 Shader 创建 M - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderParameterPresentation.ShaderParameterPresentation()`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L12) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public static Inno.Editor.Shaders.ShaderParameterPresentation Inno.Editor.Shaders.ShaderParameterPresentation.Read(Inno.Core.Graphs.GraphDocument graph, Inno.Rendering.ShaderPropertyId propertyId, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L59) | Reads detached presentation for one binding, defaulting only when no presentation was authored.
返回:The authored presentation, or ordinary visible, unbounded defaults.
`graph`:Authoring graph, not a runtime Shader program.
`propertyId`:Stable parameter identity shared by all its nodes.
`serialization`:Current owner converters.
`context`:Complete owner serialization context. | — | — | -| [`public static void Inno.Editor.Shaders.ShaderParameterPresentation.Write(Inno.Core.Graphs.GraphDocument graph, Inno.Rendering.ShaderPropertyId propertyId, Inno.Editor.Shaders.ShaderParameterPresentation presentation, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L92) | Writes presentation to a detached graph without changing any default, Material, or compiled program.
`graph`:Detached graph owned by the caller's History transaction.
`propertyId`:Stable parameter identity.
`presentation`:Detached Inspector settings.
`serialization`:Current owner converters.
`context`:Complete owner serialization context. | — | `System.ArgumentException`:The identity or range is invalid, or a bound cannot be represented by a scalar Shader Float. | -| [`public string Inno.Editor.Shaders.ShaderParameterPresentation.description { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L23) | Gets or sets the hover description without adding a permanent paragraph. | — | — | -| [`public string Inno.Editor.Shaders.ShaderParameterPresentation.group { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L19) | Gets or sets the Material Inspector section; empty uses the standard Parameters section. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderParameterPresentation.hasRange { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L31) | Gets or sets whether a scalar Float control uses the declared editing bounds. | — | — | -| [`public double Inno.Editor.Shaders.ShaderParameterPresentation.maximum { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L39) | Gets or sets the inclusive editing maximum. | — | — | -| [`public double Inno.Editor.Shaders.ShaderParameterPresentation.minimum { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L35) | Gets or sets the inclusive editing minimum; existing values are not changed by presentation metadata. | — | — | -| [`public bool Inno.Editor.Shaders.ShaderParameterPresentation.visible { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L27) | Gets or sets whether this Material-owned parameter appears in ordinary Material Inspectors. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderParameterPresentation : Inno.Core.Serialization.ISerializable`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L12) | Stores Editor-only parameter presentation by stable binding identity in the Shader's authoring graph. | — | — | - -### `Inno.Editor.Shaders.ShaderPreviewContext` - + + + + + + + + + + @@ -262,96 +81,71 @@ File Browser 的 Create 菜单支持创建 Material 和从选中 Shader 创建 M -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderPreviewContext.ShaderPreviewContext(Inno.Rendering.Runtime.RenderPersistentResourceId resourceId, Inno.Rendering.Assets.MaterialAsset material, Inno.Rendering.Assets.RenderShaderArtifact artifact, Inno.Rendering.Assets.ShaderDefinition definition, Inno.Core.Diagnostics.IDiagnosticReporter diagnostics, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L72) | Creates a preview invocation from host-owned detached inputs.
`resourceId`:Isolated material publication and viewport resource identity.
`material`:Detached values with a current Shader reference; do not modify the reference.
`artifact`:Compiled candidate, never published as a canonical Shader.
`definition`:Detached contract captured by the candidate.
`diagnostics`:Preview-local reporter; errors must not contaminate project diagnostics.
`pixelWidth`:Positive target width.
`pixelHeight`:Positive target height. | — | — | +| [`public sealed class Inno.Editor.Shaders.MaterialDocuments : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L23) | Owns Material authoring sessions; native source editing uses the shared asset draft lifecycle. | — | — | +| [`public Inno.Assets.Pipeline.AssetPipeline Inno.Editor.Shaders.MaterialDocuments.assets { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L33) | Gets the authoritative asset catalog used by source-aware inspectors. | — | — | +| [`public Inno.Editor.Rendering.IEditorPreviewService Inno.Editor.Shaders.MaterialDocuments.previews { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L40) | Gets the generation-scoped preview service shared by material presentations. | — | — | +| [`public System.Guid Inno.Editor.Shaders.MaterialDocuments.Open(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L64) | Opens native source, including a source with failed import, without publishing its draft.
`path`: Mounted Material source path. | The persistent identity used for subsequent draft operations. | — | +| [`public Inno.Rendering.Assets.MaterialAsset Inno.Editor.Shaders.MaterialDocuments.Read(System.Guid assetId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L74) | Reads a detached current-generation Material; referenced assets remain canonical read-only inputs.
`assetId`: Open Material source identity. | The current unsaved Material value. | — | +| [`public void Inno.Editor.Shaders.MaterialDocuments.Replace(System.Guid assetId, Inno.Rendering.Assets.MaterialAsset candidate, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L87) | Changes a draft through shared History without modifying its source or Scene/Game.
`assetId`: Open Material source identity.
`candidate`: Detached edited Material.
`mergeKey`: The current gesture key, or null for a discrete edit. | — | — | +| [`public void Inno.Editor.Shaders.MaterialDocuments.ReplaceMany(System.Collections.Generic.IReadOnlyDictionary candidates, string? mergeKey = null)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L101) | Changes compatible selected drafts within one shared gesture.
`candidates`: Detached Material values keyed by persistent source identity.
`mergeKey`: The current gesture key, or null for a discrete edit. | — | — | +| [`public Inno.Editor.Assets.AssetDraftDocuments.Draft Inno.Editor.Shaders.MaterialDocuments.OpenDraft(Inno.Assets.AssetPath path)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L115) | Opens the current source draft without publishing it.
`path`: The isolated Material source path. | The generation-scoped draft; callers must not retain it across a reload. | — | +| [`public Inno.Editor.Assets.AssetDraftDocuments.Draft Inno.Editor.Shaders.MaterialDocuments.Open(Inno.Rendering.Assets.MaterialAsset material)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L126) | Resolves the source draft for a canonical Material identity.
`material`: The current canonical Material whose source is edited. | The generation-scoped editable source draft. | — | +| [`public Inno.Editor.Shaders.ShaderParameterPresentation Inno.Editor.Shaders.MaterialDocuments.Presentation(Inno.Rendering.Assets.ShaderAsset shader, Inno.Rendering.ShaderPropertyId propertyId, out string error)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L149) | Reads parameter presentation from the current authoring Shader graph.
`shader`: The canonical Shader used by the Material.
`propertyId`: The stable binding identity.
`error`: Receives the source diagnostic, or an empty string when presentation is available. | Detached presentation values; an unavailable graph uses the ordinary presentation defaults. | — | +| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnStart(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L195) | Initializes this feature when its owning runtime becomes active.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L202) | Advances this feature using the current runtime state.
`context`: The context that supplies state and services for this operation. | — | — | +| [`protected override void Inno.Editor.Shaders.MaterialDocuments.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs#L209) | Stops this feature before its owning runtime releases the active generation.
`context`: The context that supplies state and services for this operation. | — | — | +| [`public sealed class Inno.Editor.Shaders.ShaderParameterPresentation : Inno.Core.Serialization.ISerializable`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L12) | Stores Editor-only parameter presentation by stable binding identity in the Shader's authoring graph. | — | — | +| [`public string Inno.Editor.Shaders.ShaderParameterPresentation.group { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L19) | Gets or sets the Material Inspector section; empty uses the standard Parameters section. | — | — | +| [`public string Inno.Editor.Shaders.ShaderParameterPresentation.description { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L23) | Gets or sets the hover description without adding a permanent paragraph. | — | — | +| [`public bool Inno.Editor.Shaders.ShaderParameterPresentation.visible { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L27) | Gets or sets whether this Material-owned parameter appears in ordinary Material Inspectors. | — | — | +| [`public bool Inno.Editor.Shaders.ShaderParameterPresentation.hasRange { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L31) | Gets or sets whether a scalar Float control uses the declared editing bounds. | — | — | +| [`public double Inno.Editor.Shaders.ShaderParameterPresentation.minimum { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L35) | Gets or sets the inclusive editing minimum; existing values are not changed by presentation metadata. | — | — | +| [`public double Inno.Editor.Shaders.ShaderParameterPresentation.maximum { get; set; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L39) | Gets or sets the inclusive editing maximum. | — | — | +| [`public static Inno.Editor.Shaders.ShaderParameterPresentation Inno.Editor.Shaders.ShaderParameterPresentation.Read(Inno.Core.Graphs.GraphDocument graph, Inno.Rendering.ShaderPropertyId propertyId, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L59) | Reads detached presentation for one binding, defaulting only when no presentation was authored.
`graph`: Authoring graph, not a runtime Shader program.
`propertyId`: Stable parameter identity shared by all its nodes.
`serialization`: Current owner converters.
`context`: Complete owner serialization context. | The authored presentation, or ordinary visible, unbounded defaults. | — | +| [`public static void Inno.Editor.Shaders.ShaderParameterPresentation.Write(Inno.Core.Graphs.GraphDocument graph, Inno.Rendering.ShaderPropertyId propertyId, Inno.Editor.Shaders.ShaderParameterPresentation presentation, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Serialization.SerializationContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L92) | Writes presentation to a detached graph without changing any default, Material, or compiled program.
`graph`: Detached graph owned by the caller's History transaction.
`propertyId`: Stable parameter identity.
`presentation`: Detached Inspector settings.
`serialization`: Current owner converters.
`context`: Complete owner serialization context. | — | `System.ArgumentException`: The identity or range is invalid, or a bound cannot be represented by a scalar Shader Float. | +| [`public Inno.Editor.Shaders.ShaderParameterPresentation.ShaderParameterPresentation()`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderParameterPresentation.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Shaders.ShaderInspectionSelection`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L11) | Identifies a shader document and selected nodes without retaining a graph, extension object or delegate. | — | — | +| [`public Inno.Editor.Shaders.ShaderInspectionSelection.ShaderInspectionSelection(System.Guid assetId, System.Collections.Generic.IEnumerable nodes)`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L23) | Creates a neutral Inspector selection separate from the Asset Browser's navigation.
`assetId`: Persistent shader asset identity.
`nodes`: Selected stable node identities; empty selects document settings. | — | — | +| [`public System.Guid Inno.Editor.Shaders.ShaderInspectionSelection.assetId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L36) | Gets the persistent shader identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Shaders.ShaderInspectionSelection.nodes { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderInspectionSelection.cs#L40) | Gets selected stable node identities. | — | — | +| [`public sealed class Inno.Editor.Shaders.ShaderPreviewProviderAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L17) | Associates a reloadable preview implementation with one open Shader contract.
`contractId`: Non-empty domain-owned contract identity. | — | — | +| [`public Inno.Editor.Shaders.ShaderPreviewProviderAttribute.ShaderPreviewProviderAttribute(string contractId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L17) | Associates a reloadable preview implementation with one open Shader contract.
`contractId`: Non-empty domain-owned contract identity. | — | — | +| [`public string Inno.Editor.Shaders.ShaderPreviewProviderAttribute.contractId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L22) | Gets the non-empty Shader contract consumed by this preview. | — | — | +| [`public abstract class Inno.Editor.Shaders.ShaderPreviewProvider`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L29) | Builds domain-owned preview geometry and pass inputs without changing a scene or canonical asset. | — | — | +| [`public abstract Inno.Editor.Rendering.EditorViewportLayer Inno.Editor.Shaders.ShaderPreviewProvider.CreateLayer(Inno.Editor.Shaders.ShaderPreviewContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L40) | Creates one frame-local rendering layer through the ordinary Render Graph.
`context`: Detached Material, immutable candidate and isolated diagnostics for this invocation. | A domain-selected pipeline and frame payload; no instance may be retained beyond the submitted frame. | — | +| [`protected Inno.Editor.Shaders.ShaderPreviewProvider.ShaderPreviewProvider()`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L29) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Shaders.ShaderPreviewContext`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L46) | Carries frame-only preview inputs; persistent provider state must retain stable values rather than this object. | — | — | +| [`public Inno.Editor.Shaders.ShaderPreviewContext.ShaderPreviewContext(Inno.Rendering.Runtime.RenderPersistentResourceId resourceId, Inno.Rendering.Assets.MaterialAsset material, Inno.Rendering.Assets.RenderShaderArtifact artifact, Inno.Rendering.Assets.ShaderDefinition definition, Inno.Core.Diagnostics.IDiagnosticReporter diagnostics, int pixelWidth, int pixelHeight)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L72) | Creates a preview invocation from host-owned detached inputs.
`resourceId`: Isolated material publication and viewport resource identity.
`material`: Detached values with a current Shader reference; do not modify the reference.
`artifact`: Compiled candidate, never published as a canonical Shader.
`definition`: Detached contract captured by the candidate.
`diagnostics`: Preview-local reporter; errors must not contaminate project diagnostics.
`pixelWidth`: Positive target width.
`pixelHeight`: Positive target height. | — | — | +| [`public Inno.Rendering.Runtime.RenderPersistentResourceId Inno.Editor.Shaders.ShaderPreviewContext.resourceId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L92) | Gets the publication scope released by the host when this preview closes. | — | — | +| [`public Inno.Rendering.Assets.MaterialAsset Inno.Editor.Shaders.ShaderPreviewContext.material { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L96) | Gets the invocation-local Material values. | — | — | | [`public Inno.Rendering.Assets.RenderShaderArtifact Inno.Editor.Shaders.ShaderPreviewContext.artifact { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L100) | Gets the complete immutable GPU candidate. | — | — | | [`public Inno.Rendering.Assets.ShaderDefinition Inno.Editor.Shaders.ShaderPreviewContext.definition { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L104) | Gets the candidate's exact contract, not the latest uncompiled source interface. | — | — | | [`public Inno.Core.Diagnostics.IDiagnosticReporter Inno.Editor.Shaders.ShaderPreviewContext.diagnostics { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L108) | Gets the host-owned, isolated diagnostic producer. | — | — | -| [`public Inno.Rendering.Assets.MaterialAsset Inno.Editor.Shaders.ShaderPreviewContext.material { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L96) | Gets the invocation-local Material values. | — | — | -| [`public int Inno.Editor.Shaders.ShaderPreviewContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L116) | Gets target height in physical pixels. | — | — | | [`public int Inno.Editor.Shaders.ShaderPreviewContext.pixelWidth { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L112) | Gets target width in physical pixels. | — | — | -| [`public Inno.Rendering.Runtime.RenderPersistentResourceId Inno.Editor.Shaders.ShaderPreviewContext.resourceId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L92) | Gets the publication scope released by the host when this preview closes. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderPreviewContext`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L46) | Carries frame-only preview inputs; persistent provider state must retain stable values rather than this object. | — | — | - -### `Inno.Editor.Shaders.ShaderPreviewProvider` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Editor.Shaders.ShaderPreviewProvider.ShaderPreviewProvider()`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L29) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract Inno.Editor.Rendering.EditorViewportLayer Inno.Editor.Shaders.ShaderPreviewProvider.CreateLayer(Inno.Editor.Shaders.ShaderPreviewContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L40) | Creates one frame-local rendering layer through the ordinary Render Graph.
返回:A domain-selected pipeline and frame payload; no instance may be retained beyond the submitted frame.
`context`:Detached Material, immutable candidate and isolated diagnostics for this invocation. | — | — | -| [`public abstract class Inno.Editor.Shaders.ShaderPreviewProvider`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L29) | Builds domain-owned preview geometry and pass inputs without changing a scene or canonical asset. | — | — | - -### `Inno.Editor.Shaders.ShaderPreviewProviderAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Editor.Shaders.ShaderPreviewProviderAttribute.ShaderPreviewProviderAttribute(string contractId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L17) | Associates a reloadable preview implementation with one open Shader contract.
`contractId`:Non-empty domain-owned contract identity. | — | — | -| [`public string Inno.Editor.Shaders.ShaderPreviewProviderAttribute.contractId { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L22) | Gets the non-empty Shader contract consumed by this preview. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderPreviewProviderAttribute : System.Attribute`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L17) | Associates a reloadable preview implementation with one open Shader contract.
`contractId`:Non-empty domain-owned contract identity. | — | — | - -### `Inno.Editor.Shaders.ShaderPreviews` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Editor.Shaders.ShaderPreviews.Draw(System.Guid ownerId, Inno.Rendering.Assets.MaterialAsset material, Inno.Editor.Rendering.EditorShaderDraftCompilationSnapshot compilation, float logicalSize)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L88) | Draws a compiled draft without modifying the source Shader or its canonical runtime publication.
`ownerId`:Persistent document identity, independent of the canonical Shader publication.
`material`:Detached defaults or overrides with the current Shader reference.
`compilation`:Isolated draft compiler result, including explicit last-good state.
`logicalSize`:Positive square image size in logical pixels. | — | — | -| [`public void Inno.Editor.Shaders.ShaderPreviews.DrawMaterial(System.Guid ownerId, Inno.Rendering.Assets.MaterialAsset material, float logicalSize)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L53) | Draws detached Material overrides using its Shader's compiled contract.
`ownerId`:Persistent document identity; no Material is retained under this identity.
`material`:Invocation-local Material values.
`logicalSize`:Positive square image size in logical pixels. | — | — | -| [`protected override void Inno.Editor.Shaders.ShaderPreviews.OnStop(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L170) | Stops this feature before its owning runtime releases the active generation.
`context`:The context that supplies state and services for this operation. | — | — | -| [`protected override void Inno.Editor.Shaders.ShaderPreviews.OnUpdate(Inno.Editor.Core.EditorContext context)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L158) | Advances this feature using the current runtime state.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public void Inno.Editor.Shaders.ShaderPreviews.Release(System.Guid ownerId)`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L146) | Releases this document's viewport and scoped GPU publication without touching other consumers.
`ownerId`:Closing or disabled preview owner. | — | — | -| [`public sealed class Inno.Editor.Shaders.ShaderPreviews : Inno.Editor.Core.EditorModule`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs#L21) | Hosts isolated Shader and Material images using domain-contributed preview pipelines. | — | — | - -### `Inno.Editor.Shaders.ShaderPropertyInspector` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Rendering.Assets.MaterialValue Inno.Editor.Shaders.ShaderPropertyInspector.ApplyEdit(Inno.Rendering.ShaderPropertyType type, Inno.Rendering.Assets.MaterialValue before, Inno.Rendering.Assets.MaterialValue edited, Inno.Rendering.Assets.MaterialValue target)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs#L111) | Applies the edited components of a representative value without replacing unrelated mixed components.
返回:A detached value preserving the target's untouched channels, texture or sampler.
`type`:Common exact Shader property type.
`before`:Representative value before the control gesture sample.
`edited`:Representative value after this sample.
`target`:Another selected Material's current effective value. | — | — | -| [`public static bool Inno.Editor.Shaders.ShaderPropertyInspector.Compatible(Inno.Rendering.ShaderPropertyType type, Inno.Rendering.Assets.MaterialValueKind kind)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs#L80) | Tests whether a stored override has the exact representation expected by a declaration.
返回:True only for supported matching kinds; no lossy conversion is performed.
`type`:Declared material type.
`kind`:Stored representation. | — | — | -| [`public static void Inno.Editor.Shaders.ShaderPropertyInspector.Draw(Inno.Editor.Inspection.InspectionDrawContext context, object owner, string path, Inno.Rendering.Assets.ShaderPropertyDefinition property, Inno.Rendering.Assets.MaterialValue value, System.Action setter, Inno.Editor.Inspection.IInspectionPropertyEditService edits, bool readOnly, Inno.Editor.Shaders.ShaderParameterPresentation? presentation = null)`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs#L44) | Draws a typed value without choosing its persistence or history policy.
`context`:Current Inspector invocation.
`owner`:Detached draft owner used for widget identity.
`path`:Stable draft property path.
`property`:Current Shader declaration.
`value`:Current default or override, never mutated.
`setter`:Writes only the host's detached draft.
`edits`:Host-owned gesture and history service.
`readOnly`:Whether this source is editable.
`presentation`:Optional Editor-only description and scalar bounds; it never changes inherited or stored values. | — | — | -| [`public static class Inno.Editor.Shaders.ShaderPropertyInspector`](../../src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs#L12) | Uses the shared Inspector drawers for both Shader defaults and Material overrides. | — | — | +| [`public int Inno.Editor.Shaders.ShaderPreviewContext.pixelHeight { get; }`](../../src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviewProvider.cs#L116) | Gets target height in physical pixels. | — | — | ## 项目依赖 -- [Inno.Editor.Assets](Inno.Editor.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Shaders](../rendering/Inno.Rendering.Shaders.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets.Authoring](../rendering/Inno.Rendering.Assets.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Rendering.Assets](../rendering/Inno.Rendering.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Inspection](Inno.Editor.Inspection.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Core](Inno.Editor.Core.md):公开引用边界由实际签名核对。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Core.Graphs](../core/Inno.Core.Graphs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Editor.Rendering](Inno.Editor.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Rendering.Runtime](../rendering/Inno.Rendering.Runtime.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Assets.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Rendering.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Core.Graphs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Rendering.Assets` | 公开签名依赖 | +| `Inno.Rendering.Runtime` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Editor.Core` | 公开签名依赖 | +| `Inno.Editor.Assets` | 公开签名依赖 | +| `Inno.Editor.Rendering` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Panel.Global.md b/docs/editor/Inno.Editor.Workbench.md similarity index 75% rename from docs/editor/Inno.Editor.Panel.Global.md rename to docs/editor/Inno.Editor.Workbench.md index fb662da00..8b9ecccac 100644 --- a/docs/editor/Inno.Editor.Panel.Global.md +++ b/docs/editor/Inno.Editor.Workbench.md @@ -1,4 +1,4 @@ -# Inno.Editor.Panel.Global +# Inno.Editor.Workbench ## Editor / Rendering @@ -6,12 +6,12 @@ Settings 新增 `Editor/Rendering` 页面。`Vertical Sync` 默认关闭;`Maxi [Editor 索引](README.md) · [Settings API](Inno.Editor.Settings.md) · [Interactions](Inno.Editor.Interactions.md) · [Wiki 首页](../README.md) -`Inno.Editor.Panel.Global` 是 Editor Application 的全局 feature composition 项目。它不提供一个可停靠 Panel,也没有公开 API;它只放置需要由宿主统一发现、但不应属于 Settings 或 Interactions 基础程序集的内建定义。 +`Inno.Editor.Workbench` 是 Editor Application 的全局 feature composition 项目。它不提供一个可停靠 Panel,也没有公开 API;它只放置需要由宿主统一发现、但不应属于 Settings 或 Interactions 基础程序集的内建定义。 ## 职责与边界 ```text -Inno.Editor.Panel.Global/ +Inno.Editor.Workbench/ ├─ Actions/ │ ├─ HistoryActions.cs │ ├─ SelectionActions.cs @@ -19,6 +19,8 @@ Inno.Editor.Panel.Global/ ├─ Settings/ │ ├─ GlobalSettingsPages.cs │ └─ IconSettings.cs +├─ Exporting/ # Forms and progress presentation +├─ Scripting/ # Blocking compilation presentation └─ Zoom/ ├─ EditorZoomModule.cs ├─ ZoomActions.cs @@ -82,20 +84,32 @@ Application 在创建 TypeCache 之前加载本程序集。之后 Settings Catal [上一页:Inno.Editor.Panel.FileBrowser](Inno.Editor.Panel.FileBrowser.md) · [下一页:Inno.Editor.Panel.Hierarchy](Inno.Editor.Panel.Hierarchy.md) + + +## 宿主工作流呈现 + +Exporting 的 modal 只捕获表单与显示状态,异步资源属于 EditorExportService。Scripting modal 从 IEditorScriptCompilation 读取独占进度和取消能力。两者均通过 runtime 注入,不在无界面 feature 内创建窗口。 + +Command、menu、shortcut 与 toolbar 仍复用同一 Action Query/Execute。上下文搜索在每次打开时清空,popup/owner 退休时释放字符串缓存;隐藏或禁用来自当前模型,不由 Workbench 维护另一份业务判断。 + ## 当前源码公开 API 清单 -只列当前源码的 public/protected 表面;内部实现不作为稳定 API。参数、返回、失败和 owner 以英文 XML 为准。 +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -本项目没有公开入口,通过组合或扩展发现使用内部实现。 +本程序集没有对外公开类型;扩展由当前产品的 Registry 在内部发现。 ## 项目依赖 -- [Inno.Platform](../platform/Inno.Platform.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Input](../core/Inno.Core.Input.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](../backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.ImGui](../backends/ImGui/Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.ImGui](Inno.Editor.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Interactions](Inno.Editor.Interactions.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Editor.Settings](Inno.Editor.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Editor.Exporting` | 实现依赖;不传递 compile surface | +| `Inno.Build` | 实现依赖;不传递 compile surface | +| `Inno.Build.Managed` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scripting` | 实现依赖;不传递 compile surface | +| `Inno.Platform` | 实现依赖;不传递 compile surface | +| `Inno.Core.Input` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | diff --git a/docs/editor/README.md b/docs/editor/README.md index 00f5fed69..eb01e10ef 100644 --- a/docs/editor/README.md +++ b/docs/editor/README.md @@ -21,8 +21,8 @@ Editor 采用“被动核心 → 后端无关交互 → ImGui 表现 → 独立 | [Inno.Editor.Inspection](Inno.Editor.Inspection.md) | InspectionDrawer、PropertyDrawer、Registry 与 serialized property renderer。 | | [Inno.Editor.Scripting](Inno.Editor.Scripting.md) | Asset-backed Roslyn 编译、facade、IDE 工程与热重载。 | | [Inno.Editor.Exporting](Inno.Editor.Exporting.md) | File 菜单 Plugin/Game 导出、编译门禁、平台 Player 发布与原子输出。 | -| [Inno.Editor.Panel.FileBrowser](Inno.Editor.Panel.FileBrowser.md) | AssetEditor、文件浏览、Asset 操作与 Asset-side drag/drop。 | -| [Inno.Editor.Panel.Global](Inno.Editor.Panel.Global.md) | internal 全局 Action、Editor/Appearance 页面、Icon 与 Zoom setting definitions。 | +| [Inno.Editor.Panel.FileBrowser](Inno.Editor.Panel.FileBrowser.md) | 文件浏览、导航与 Asset-side drag/drop;编辑由 Assets feature 负责。 | +| [Inno.Editor.Workbench](Inno.Editor.Workbench.md) | internal 全局 Action、Editor/Appearance 页面、Icon 与 Zoom setting definitions。 | | [Inno.Editor.Panel.Hierarchy](Inno.Editor.Panel.Hierarchy.md) | Scene workspace、Hierarchy、Scene/GameObject 操作与排序。 | | [Inno.Editor.Panel.Inspector](Inno.Editor.Panel.Inspector.md) | Inspector Panel、Scene Drawer 与 Component/System 操作。 | | [Inno.Editor.Panel.Logging](Inno.Editor.Panel.Logging.md) | Editor 日志/诊断缓冲与 Console Panel。 | @@ -30,11 +30,14 @@ Editor 采用“被动核心 → 后端无关交互 → ImGui 表现 → 独立 | [Inno.Editor.Panel.Stats](Inno.Editor.Panel.Stats.md) | 平滑后的帧统计与 Stats Panel。 | | [Inno.Editor.Panel.SceneView](Inno.Editor.Panel.SceneView.md) | 不含 Camera/Picking 假设的 Plugin 驱动 Scene viewport host。 | | [Inno.Editor.Panel.GameView](Inno.Editor.Panel.GameView.md) | 不含运行时世界观的 Plugin 驱动 Game viewport host。 | -| [Inno.Editor.Shaders](Inno.Editor.Shaders.md) | 可复用 Shader 节点呈现协议与 generation-scoped Drawer 注册。 | -| [Inno.Editor.Assets](Inno.Editor.Assets.md) | Material/Pipeline 共用的原生资产草稿、显式保存与中立 History 生命周期。 | +| [Inno.Editor.Shaders](Inno.Editor.Shaders.md) | 无界面的 Material 草稿、Shader 创作服务与缓存。 | +| [Inno.Editor.Assets](Inno.Editor.Assets.md) | 可复用 Asset 编辑、源操作、导入设置、草稿、显式保存与中立 History。 | | [Inno.Editor.Panel.ShaderEditor](Inno.Editor.Panel.ShaderEditor.md) | Shader 图画布、模板创建菜单、手势历史与显式 Save/Revert;完整验收进行中。 | | [Inno.Editor.Hosting](Inno.Editor.Hosting.md) | 共享启动、EngineHost/Edit Session 与全部功能生命周期;平台产品注入后端、系统规则和构建组合。 | | [Inno.Build.Cli](../build/Inno.Build.Cli.md) | 与 Editor 共用作者端脚本 API 和构建服务的无界面入口。 | +| [Inno.Editor.Assets.ImGui](Inno.Editor.Assets.ImGui.md) | Asset Browser 状态、图标发现和基础资源检查显示。 | +| [Inno.Editor.Shaders.ImGui](Inno.Editor.Shaders.ImGui.md) | Shader 节点控件、Material Inspector 和预览呈现。 | +| [Inno.Editor.Graph.ImGui](Inno.Editor.Graph.ImGui.md) | 不含 Shader 语义的坐标、网格、连线与命中测试。 | ## 依赖方向 @@ -61,10 +64,11 @@ flowchart TD Interactions --> Panels ImGui --> Panels Core --> Scripting["Inno.Editor.Scripting"] - ImGui --> Scripting Scripting --> PlayMode Scripting --> Exporting["Inno.Editor.Exporting"] - Settings --> Exporting + Exporting --> Workbench["Inno.Editor.Workbench"] + ImGui --> Workbench + Scripting --> Workbench Exporting --> Application PlayMode --> Application Audio --> Application diff --git a/docs/testing/Inno.Editor.Interactions.Tests.md b/docs/testing/Inno.Editor.Interactions.Tests.md index c8018b3b2..bb99ae0e8 100644 --- a/docs/testing/Inno.Editor.Interactions.Tests.md +++ b/docs/testing/Inno.Editor.Interactions.Tests.md @@ -22,7 +22,7 @@ - [Inno.Editor.PlayMode](../editor/Inno.Editor.PlayMode.md) - [Inno.Editor.Settings](../editor/Inno.Editor.Settings.md) - [Inno.Editor.Panel.FileBrowser](../editor/Inno.Editor.Panel.FileBrowser.md) -- [Inno.Editor.Panel.Global](../editor/Inno.Editor.Panel.Global.md) +- [Inno.Editor.Workbench](../editor/Inno.Editor.Workbench.md) - [Inno.Editor.Panel.Logging](../editor/Inno.Editor.Panel.Logging.md) - [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md) - [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md) diff --git a/docs/testing/README.md b/docs/testing/README.md index f50bd9c58..0300cc1db 100644 --- a/docs/testing/README.md +++ b/docs/testing/README.md @@ -99,3 +99,6 @@ Native 测试部署在成功 Build 后执行;CI 的目标 Build 明确设置 I `BgfxShutdownTests.ShutdownAfterAnOffscreenGraphReleasesTheProcessOwner` 使用真实窗口、离屏 Graph 与设备所有资源,验证关闭后的进程 owner 可再次取得且借用窗口仍有效。默认使用当前测试宿主的 D3D11 / Metal;显式设置 `INNO_TEST_GRAPHICS_API=Vulkan` 或 `Direct3D12` 可在对应设备单独执行同一回归。该 ID 必须与实际能力一致,不提供失败后切换后端的 fallback。 脚本测试覆盖 C# 14 的 `field`、空条件赋值、Span、开放泛型 `nameof`、推断类型 Lambda 参数修饰符,以及跨插件扩展成员的引擎/IDE 一致性;另验证 framework reference 内容变化和 SDK target 生成符号的缓存失效。 + + +隔离测试入口保留每个程序集的 `.discovery.log`,发现阶段的 stderr 异常即使伴随零退出码也会失败。程序集模式由选定 `DOTNET_HOST_PATH` 让 VSTest 推导运行根,避免重复传入同一环境键。`Invoke-EditorSmoke.ps1 -GraphicsApi ` 通过正式 Editor CLI 选择图形 API,不在脚本中维护平台列表。 diff --git a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md new file mode 100644 index 000000000..e0e491a57 --- /dev/null +++ b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md @@ -0,0 +1,54 @@ +# Editor UI implementation ledger + +This ledger tracks the approved Editor UI plan against the current `editor-ui` branch. It is an implementation record, not acceptance evidence. + +Baseline: `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`. Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. + +## Required outcomes + +- Preserve the existing ImGui visual identity while unifying layout, controls and feedback. +- Repair History rejection atomicity and unify continuous edit boundaries. +- Move reusable asset operations out of File Browser; remove Panel-to-Panel dependencies. +- Keep domain editing independent from native UI and platform implementations. +- Give UI state an explicit session and retirement owner. +- Unify identity-aware selection, focus, queued actions and multi-object operations. +- Unify Action availability, menus, shortcuts, toolbars and command search. +- Separate reusable Graph presentation from Shader semantics. +- Update every affected caller, scripting export, project, Solution entry and document. +- Run meaningful correctness, native widget, reload, product and performance checks. +- Commit as Aaron Liao without co-authors on the current branch and inspect its CI. + +## Stage ledger + +| Stage | State | Evidence | +| --- | --- | --- | +| Baseline and ownership | Recorded | External baseline inventory and SDK information | +| History atomicity | Implemented; local regressions passed | LIFO rejection, shared gestures, immediate draft records and generation-only queries | +| Domain and presentation boundaries | Implemented | Headless features; Assets/Shaders/Graph presentation; no Panel references | +| UI session and styles | Implemented | Session/owner state, pointer-free facade, native borrow guards; 49 native tests passed | +| Selection, gestures and commands | Implemented; local regressions passed | Ordered identities, mixed values, precise scalar components and atomic batch operations | +| Panel integration | Implemented | Inspector, Hierarchy, File Browser, SceneView, ShaderEditor and Workbench | +| Architecture, scripting and documentation | Passed locally | 241 SDK projects evaluated; current API mappings synchronized; final XML formatting, API signatures and compiled Wiki examples passed | +| Product, performance and final acceptance | Local product checks passed | Final Debug/Release builds, 15,000-frame FlappyBird run, 300 Play frames and renderer matrix; isolated UTF-8 comparison recorded | + +## Acceptance rules + +Tests use real public contracts and existing replaceable boundaries. No IVT, private reflection, test-only production entry points, compatibility wrappers, generated binding edits or vendor changes are permitted. Current Core IO, Events, Identity, Serialization, History and candidate-generation lifecycles remain authoritative. + +The final report must assess architecture, readability, state ownership, abstraction, reuse, styling, extensibility, interaction, Undo/Redo, menu clarity and component granularity individually. Passed checks, failures and unexecuted checks must remain distinct. + +## Execution observations + +- Native fixtures require an explicit product target and its Editor product properties. Ordinary managed compilation alone does not establish a matched native deployment. Scripting and PlayMode fixtures now declare their actual native product dependency; ImGui fixtures consume the selected target instead of a Windows default. +- The initial full target source build used base product properties and correctly rejected missing ImGui shader inputs. The corrected complete run was cancelled when the component-edit contract changed; it is not a passing result. Final focused UI builds and remote CI must use the frozen final contract. +- Computer Use captured the real FlappyBird Scene/Game view but failed to activate the owned window twice. Manual mouse interaction is unexecuted; native input tests and bounded product smoke remain separate evidence. +- Final acceptance must replace these intermediate observations with actual final results, retaining failed/cancelled command provenance in the evidence owner. + + +## Closed local regressions + +- 553 shared Editor/tooling tests and one real isolated viewport-retirement fact passed. Three further isolated Core/compiler/build facts passed through the corrected SDK test runner. Rendering2D authoring has 42 passing tests. +- Temporary Play identity routing now has an explicit Host lease and retains Missing selections when a domain retires. The real FlappyBird probe verified the same persistent ID in Edit/Play and restoration to the original domain. +- Pure label formatting belongs to Editor Core; menu queries require no native drawing context. All engine/plugin callers use the shared formatter. +- VSTest discovery errors are no longer treated as successful empty discovery; raw discovery logs are preserved. +- Native vendor/cold SDK diagnostics and automatic maintenance-budget deferrals remain visible in their original logs. They were not suppressed. diff --git a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md new file mode 100644 index 000000000..18984d4a7 --- /dev/null +++ b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md @@ -0,0 +1,120 @@ +# Editor UI acceptance — 2026-10-27 + +[Current acceptance](../../../docs/architecture/CURRENT_ACCEPTANCE.md) · [Architecture](../../../docs/architecture/EDITOR_UI_ARCHITECTURE.md) · [Implementation ledger](../EDITOR_UI_IMPLEMENTATION_PLAN.md) + +## Scope and conclusion + +This report covers the approved Editor UI refactor on `editor-ui`, starting at `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`, together with the Rendering2D authoring caller updates and the officially exported FlappyBird package. It does not certify the entire engine, every platform or every interaction as perfect. + +The implementation and local correctness checks are complete, including the final SDK Wiki gate. Remote CI is tracked on the pull request for the committed branch; local results do not substitute for its complete matrix. + +## Implemented boundaries and behavior + +- Headless Assets, Shaders, Rendering, Scripting and Exporting features own domain operations, drafts, compilation and export lifetime. ImGui presentation owns windows, controls and transient state. Panels do not reference other Panels. +- Reusable asset editing and its neutral History protocols belong to `Inno.Editor.Assets`; Browser presentation belongs to `Inno.Editor.Assets.ImGui`. Shader presentation belongs to `Inno.Editor.Shaders.ImGui`. Generic graph coordinates, curves and hit tests belong to `Inno.Editor.Graph.ImGui`. +- `EditorUiSession` owns controls, styles and per-owner transient data. Native pointers, generated binding types and native option enums stay inside the ImGui adapter and product hosting boundary. Draw-list borrows reject use outside their frame/window/context. +- Continuous input uses `EditorEditGesture` over the existing History transaction. Each sample records immediately. Release commits, Escape rolls back, and commands or document operations finish the gesture before their own transaction. Failed completion preserves ownership. +- Ordered selection resolves Core identities, retains Missing references and supports anchors/ranges/toggles. Batch asset and scene edits preflight the whole selection and use the existing atomic History protocols. Mixed vector edits affect only the selected scalar component. +- Menus, shortcuts, toolbars and command search use the same Action availability and execution path. Deferred targets resolve Core identities instead of retaining live scene objects. +- Temporary Play identity domains have a Host routing lease over the existing Core allocator. Selection, focus, queued actions and drag/drop use the same routing; Edit selection is restored before retiring the domain. No second object registry was created. +- Play Mode notifications and settings/UI lifecycle notifications use Core Events with explicit subscription ownership. No parallel event dispatcher implementation, Undo stack, object identity registry or persistence format was added. +- Rendering2D uses the current session and edit contract. Its package in FlappyBird was produced by the official exporter, never edited as an archive. + +- Pure identifier formatting moved out of session-bound widgets into `EditorDisplayNames` in Editor Core. Menu metadata can be queried without a native UI context. The old widget member was removed and every engine/plugin caller updated. + +The full current file tree and execution flow are in the architecture document. Solution registration, project reference visibility, scripting exports, English XML and individual Editor Wiki pages were updated together. + +## Verification + +Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. + +| Check | Final result | Evidence | +| --- | --- | --- | +| Windows UI source build | Passed; zero compiler warnings/errors | `ui-tests2-build.log`, `contract-tests-build.log`, binlogs | +| Shared UI contracts | 553 passed; 0 failed/skipped | `closed-tests/` TRX and execution summary | +| Isolated contracts | 4 passed; 0 failed/skipped | One native retirement case in `closed-tests/`; three Core/compiler/build cases in `isolation-final-tests/` | +| Rendering2D authoring | 42 passed, 0 failed/skipped | `rendering2d-final-tests/results/authoring.trx` | +| Official package export | Passed | `rendering2d-export-accepted.log` | +| Architecture and public XML | Passed; final Wiki graph also checks evaluated ownership | `architecture-final.log`, `xml-domain.log`; new public source compiles with XML warnings as errors | +| SDK Wiki graph, signatures, links and examples | Passed; 241 project ownership evaluations, 34,493 API entries and 197 examples | `wiki-final-accepted.log`, `wiki-final-api.json`, `wiki-final-examples.json` | +| Editor Debug and Release | Passed; zero errors, one automatic maintenance-budget warning each | `editor-debug-final.*`, `editor-release-final.*`; cold Release vendor warnings remain in `editor-release.log` | +| FlappyBird product | Debug: 15,000 frames, 300 Play frames and Edit return; Release: 1,000 frames; D3D12 and Vulkan: 1,000 frames each; all orderly exits | `flappy-debug-closed*`, `flappy-Release-d3d11.log`, `flappy-Debug-d3d12.log`, `flappy-Debug-vulkan.log` | +| GitHub CI | Tracked separately on the pull request for this branch | Its exact head revision, run links and final results accompany delivery; this report does not claim an unexecuted remote pass | + +Initial failures remain in the evidence owner: the wrong native product context, a missing direct test annotation dependency, fixtures missing the new session/presentation boundary, and an EventHub whose dispatcher was not retained. Their corrective runs passed; these failures are not labeled as upstream BGCS defects. The full SourceDebug run interrupted during contract changes is not counted as a pass. + +## Performance and resource ownership + +The isolated UTF-8 benchmark uses .NET SDK 10.0.402, Release, the same text/input, 1,000 warm-up operations and seven samples of 10,000 operations. Hardware: AMD Ryzen 9 6900HX, 8 cores / 16 logical processors. Results are medians for the encoding buffer operation, not whole-editor frame performance. + +| Buffer capacity | Previous allocation/op | Current allocation/op | Previous time/10,000 | Current time/10,000 | +| --- | --- | --- | --- | --- | +| 256 | 328 B | 0 B | 1.158 ms | 0.440 ms | +| 1,024 | 1,096 B | 0 B | 1.472 ms | 0.588 ms | +| 4,096 | 4,168 B | 0 B | 5.853 ms | 1.773 ms | + +This gain comes from the implementation's stack/pooled buffer and span encoding. It is not attributed to a runtime upgrade. Native unchanged-input allocation checks are separate evidence. No claim is made about long-duration frame/GC performance or a whole-editor speedup. + +Native preparation recorded `processes=0`, `bindingGenerations=0` and `materializedBytes=0` on fixed inputs. Its complete input hashing still has a substantial cost; this refactor does not claim to solve the separately tracked native build performance work. + +## Deep self-review + +The review follows real call chains, not only folders: input → widget/session → Action or feature operation → owner serialization context → shared History → reload-safe handler. It also covers dispatcher ownership, observer release, failed rollback, per-axis batch changes, deferred identity resolution, session/owner retirement and native borrowed memory. Existing Core Events, Identity, Serialization and IO remain authoritative. + +BGCS is pinned to 4.1.2. The directly restored SDK package payloads matched their original NuGet archives; the BGCS repository remains at its baseline with no changes. No backend/platform/native/generated source changes were made by this refactor. A direct SDK payload comparison covered 122 files with zero mismatches. No global/implicit using, IVT, private reflection access, migration reader, compatibility overload or placeholder project was introduced. + +## Individual quality assessment + +These assessments describe verified engineering properties. “Enterprise-grade” and “phenomenal” are not automated test results. + +| Dimension | Assessment | Evidence and remaining improvement | +| --- | --- | --- | +| Architecture | Enforceable ownership; local architecture passed | Headless feature boundaries and no Panel-to-Panel dependencies are validator rules. ShaderEditor remains a substantial domain coordinator. | +| Code organization | Improved | Feature-oriented files, project namespaces and visible dependency groups. Further shorten the largest domain-specific canvas workflows when a concrete reusable boundary emerges. | +| State/lifetime cleanliness | Explicit ownership; local lifecycle tests passed | Explicit session/owner, subscriptions, identities and borrow guards. Continue covering future extension failure paths with the same ownership rules. | +| Abstraction | Purposeful, bounded | Domain mechanisms are separated from ImGui adaptation. This is not a promise that replacing the entire UI backend requires no presentation work. | +| Reuse | Core capabilities reused | Events, Identity, Serialization, IO, Graph and History remain shared mechanisms. No replacement infrastructure was added. | +| Visual consistency | Shared foundation; complete visual certification unavailable | Common metrics, fonts, icons, rows, mixed states and feedback; the existing dark ImGui identity is preserved. Full keyboard/DPI/accessibility review remains unexecuted. | +| Extensibility | Improved and guarded | Existing Registry/TypeCache and generation transactions remain the extension boundary; plugins use the current session and neutral operation contracts. | +| Interaction logic | Improved; product/manual limits apply | Ordered multi-selection, common properties, precise axes and batch preflight. Long real-user keyboard/drag/focus journeys remain a future acceptance exercise. | +| Undo/Redo | Strengthened; local tests passed | One authoritative History, immediate samples, rejected LIFO preservation, atomic batches and Missing barriers. No second undo stack. | +| Menu clarity | More consistent | One Action model across search/menu/shortcut/toolbar; enabled/checked rules are shared. Label taxonomy can still benefit from user observation. | +| Component granularity | Improved without empty abstractions | Assets/Shader/Graph presentation assemblies have real consumers. The largest ShaderEditor view remains an honest refinement opportunity. | + +## Unexecuted or limited acceptance + +- No macOS device is available; Metal and real macOS Editor behavior are unexecuted. +- Computer Use captured the real FlappyBird Scene/Game view, but window activation failed twice. Following the tool's recovery limit, further manual input was stopped. Mouse interaction was not certified; the owned visual session was terminated. Separate bounded runs verify orderly exit. +- Full accessibility, all focus/DPI combinations, audible audio quality, extended usability sessions and long-duration GPU/GC behavior are not certified by native layout tests or bounded smoke runs. +- Existing engine issues outside this UI plan remain independently tracked. This refactor cannot establish that the entire engine is perfect. + +The configured macOS `Glass.aiff` is unavailable on this Windows host; the completion sound cannot be played. + + +## Necessary public API changes + +- `EditorUiSession`, its controls, pointer-free options/font/input/layout values and guarded `EditorDrawList` express explicit host/session/frame ownership without generated native types in consumer APIs. +- `EditorSelectionSnapshot`, ordered selection operations and `EditorEditGesture` provide shared selection and continuous History boundaries. The inspection edit contract carries an optional scalar component and merge key so batch edits preserve untouched values. +- Asset editing, export ownership and generic graph geometry now have reusable feature/presentation entry points. Existing registries remain their discovery mechanism. +- `RegisterIdentityDomain` extends the Host selection coordinator for temporary runtime worlds. The allocator remains Core-owned; callers retire the routing lease on the owner thread after restoring Edit selection. This coordinator is Host-only and is no longer a project scripting export. Scripts use `EditorInteractions` without registering domains. +- `EditorDisplayNames.Nicify` provides pure label formatting for menu metadata, Inspector and plugins. The session-bound widget method was removed. +- Play Mode and settings notifications return disposable Core Events subscriptions. The Console releases its subscription explicitly. + +## Verification entry and retained diagnostics + +VSTest assembly discovery derives `DOTNET_ROOT` from the selected `DOTNET_HOST_PATH`. Passing both as command-line environment settings caused a duplicate-key discovery exception with a zero exit code in this SDK. The shared runner now supplies the supported host input without the duplicate root, preserves discovery output and treats stderr discovery errors as failures. Four isolated facts were actually executed, rather than inferred from a zero exit code. + +One attempted Build-entry rebuild overlapped a running Wiki CLI and encountered the ordinary Windows lock on that executing CLI DLL. Rebuilding after the owned process exited passed; no production copy/loading workaround was added. + +Explicit owner-aware cache maintenance reclaimed 20,782,799,012 logical bytes and protected 174,384,757 bytes while active verification was running. C: free space rose from about 92.5 to 95.5 GiB; logical bytes and physical volume recovery are different measurements. SDK and NuGet global caches were not cleaned. + +The full local SourceDebug build interrupted during contract changes remains uncounted. The final focused UI graph, three additional isolation projects and standard Debug/Release product entries passed; the complete SourceDebug matrix belongs to remote CI. No test filter excludes an unresolved UI failure. + + +## Companion source commits + +- Rendering2D: `067d4eb023b4dea0df114872247d9b44f69fe20f` — current-session controls, precise edit contract, Core label formatting and generation-only History queries. +- Samples: `3e331fd0fae08491fe11938faa5955c6e79e5089` — officially exported Rendering2D package in FlappyBird. +- Canvas and BGCS repositories have no task changes. Companion commits were created on their existing local branches; no remote main branch was updated. + +Both author and committer are Aaron Liao ; no co-author trailer is used. diff --git a/src/composition/editor/EditorProduct.props b/src/composition/editor/EditorProduct.props index b6c3ede3b..5f01c81e3 100644 --- a/src/composition/editor/EditorProduct.props +++ b/src/composition/editor/EditorProduct.props @@ -1,6 +1,9 @@ + + + @@ -14,7 +17,7 @@ - + diff --git a/src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs b/src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs index 5fa84e4e2..46af4351e 100644 --- a/src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Documents/AssetDraftDocuments.cs @@ -10,7 +10,6 @@ using Inno.Editor.Interactions; namespace Inno.Editor.Assets; - /// /// Owns detached native asset drafts with shared document, History, recovery and explicit-save semantics. /// @@ -31,9 +30,6 @@ public sealed class AssetDraftDocuments : IDisposable where TAsset : Ass private readonly string m_label; private IDisposable? m_provider; private long m_sourceRevision = -1; - private long m_updateSerial; - private Guid[] m_activeGroup = []; - /// /// Creates a feature-owned draft store; call Start after document services become available. /// @@ -77,9 +73,14 @@ string label throw new ArgumentException("A native file extension is required.", nameof(extension)); if (providerId is "." or ".." || providerId.IndexOfAny(['/', '\\', ':']) >= 0) throw new ArgumentException("The provider identifier must be a safe single directory name.", nameof(providerId)); - m_assets = assets; m_interactions = interactions; m_serialization = serialization; - m_sources = m_assets.CreateSourceStore(); m_providerId = providerId; m_historyKind = historyKind; - m_extension = extension; m_label = label; + m_assets = assets; + m_interactions = interactions; + m_serialization = serialization; + m_sources = m_assets.CreateSourceStore(); + m_providerId = providerId; + m_historyKind = historyKind; + m_extension = extension; + m_label = label; } /// @@ -92,15 +93,6 @@ string label /// Read-only presentation state; it contains no live asset or extension objects. /// public Draft GetDraft(Guid assetId) => m_drafts[assetId]; - - /// - /// Marks a document as inspected in this frame so a lost gesture can be completed. - /// - /// - /// Persistent source identity. - /// - public void TouchInspection(Guid assetId) => m_drafts[assetId].lastInspection = m_updateSerial; - /// /// Opens a native asset source in the shared document service without revealing a second Inspector. /// @@ -130,7 +122,6 @@ public Guid Open(AssetPath path) /// A detached editable asset whose changes have not been published. /// public TAsset Read(Guid assetId) => m_sources.Decode(m_drafts[assetId].bytes); - /// /// Updates the open draft through shared History without saving or publishing a runtime asset. /// @@ -140,107 +131,67 @@ public Guid Open(AssetPath path) /// /// Detached edited asset. /// - /// - /// True completes one history transaction; false continues the same active gesture. + /// + /// The current gesture key, or null for an independent discrete edit. /// public void Replace( Guid assetId, TAsset candidate, - bool finishGesture = true - ) => Edit(m_drafts[assetId], candidate, finishGesture); - - /// - /// Finishes the active edit gesture without saving or applying its draft. - /// - /// - /// Open asset identity. - /// - public void Commit(Guid assetId) => Commit(m_drafts[assetId]); + string? mergeKey = null + ) { + Draft draft = m_drafts[assetId]; + if (draft.readOnly) + throw new InvalidOperationException("Copy this installed asset to the project before editing."); + RecordReplace(draft, m_sources.Encode(candidate), mergeKey); + } /// - /// Edits compatible selected drafts as one gesture without saving any source. + /// Records a fully validated selection sample atomically in the shared History. /// /// - /// Detached candidate assets indexed by their open asset identities. + /// Detached candidates keyed by their persistent source identity. /// - /// - /// Whether this sample completes the shared gesture. + /// + /// The owning gesture key, or null for a discrete edit. /// public void ReplaceMany( IReadOnlyDictionary candidates, - bool finishGesture = true + string? mergeKey = null ) { ArgumentNullException.ThrowIfNull(candidates); - if (m_activeGroup.Length != 0 && !m_activeGroup.ToHashSet().SetEquals(candidates.Keys)) - CommitMany(m_activeGroup); var encoded = new Dictionary(); foreach (var pair in candidates) { - Draft draft = m_drafts[pair.Key]; - if (draft.readOnly) + if (m_drafts[pair.Key].readOnly) throw new InvalidOperationException("The selection contains a read-only asset."); encoded.Add(pair.Key, m_sources.Encode(pair.Value)); } - foreach (var pair in encoded) - { - Draft draft = m_drafts[pair.Key]; - if (draft.bytes.AsSpan().SequenceEqual(pair.Value)) - continue; - draft.gestureBefore ??= draft.bytes; - draft.bytes = pair.Value; - Changed(draft); - } - m_activeGroup = candidates.Keys.ToArray(); - if (finishGesture) - CommitMany(candidates.Keys); - } - /// - /// Commits all selected draft samples in a single shared history transaction. - /// - /// - /// Open asset identities belonging to this gesture. - /// - public void CommitMany(IEnumerable assetIds) - { - ArgumentNullException.ThrowIfNull(assetIds); - using var transaction = m_interactions.history.BeginTransaction("Edit " + m_label + " Selection"); - foreach (Guid id in assetIds) - Commit(m_drafts[id]); + using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction("Edit " + m_label + " Selection"); + foreach (var pair in encoded) + RecordReplace(m_drafts[pair.Key], pair.Value, mergeKey); transaction.Commit(); - m_activeGroup = []; } - private void Edit( + private void RecordReplace( Draft draft, - TAsset candidate, - bool finishGesture + byte[] next, + string? mergeKey = null ) { - if (draft.readOnly) - throw new InvalidOperationException("Copy this installed asset to the project before editing."); - byte[] next = m_sources.Encode(candidate); - if (!draft.bytes.AsSpan().SequenceEqual(next)) - { - draft.gestureBefore ??= draft.bytes; - draft.bytes = next; - Changed(draft); - } - if (finishGesture) - Commit(draft); - } - - private void Commit(Draft draft) - { - byte[]? before = draft.gestureBefore; - if (before is null) - return; - draft.gestureBefore = null; - if (before.AsSpan().SequenceEqual(draft.bytes)) + byte[] before = draft.bytes; + if (before.AsSpan().SequenceEqual(next)) return; - var payload = new ChangeData { assetId = draft.id, before = before, after = draft.bytes }; - var change = new EditorHistoryChange(m_historyKind, EditorHistoryPayload.FromBytes(m_serialization.Serialize(payload))); + var payload = new ChangeData + { + assetId = draft.id, + before = before, + after = next + }; + var change = new EditorHistoryChange(m_historyKind, EditorHistoryPayload.FromBytes(m_serialization.Serialize(payload)), mergeKey); + draft.bytes = next; try { + Changed(draft); m_interactions.history.RecordApplied("Edit " + m_label, change); } catch @@ -252,6 +203,43 @@ private void Commit(Draft draft) } } + /// + /// Combines adjacent samples of the same document and gesture without retaining live assets. + /// + /// + /// The preceding owned record. + /// + /// + /// The subsequent owned record. + /// + /// + /// Receives a new independent payload when the records form a contiguous edit. + /// + /// + /// True when both records describe the same continuous value change. + /// + public bool TryMergeHistory( + EditorHistoryChange older, + EditorHistoryChange newer, + out EditorHistoryChange? merged + ) { + merged = null; + if (older.kind != m_historyKind || newer.kind != m_historyKind || older.mergeKey is null || older.mergeKey != newer.mergeKey) + return false; + ChangeData first = m_serialization.Deserialize(older.payload.ReadBytes()); + ChangeData last = m_serialization.Deserialize(newer.payload.ReadBytes()); + if (first.assetId != last.assetId || !first.after.AsSpan().SequenceEqual(last.before)) + return false; + var combined = new ChangeData + { + assetId = first.assetId, + before = first.before, + after = last.after + }; + merged = new EditorHistoryChange(m_historyKind, EditorHistoryPayload.FromBytes(m_serialization.Serialize(combined)), older.mergeKey); + return true; + } + /// /// Validates a feature's neutral History record without changing the draft. /// @@ -265,7 +253,9 @@ public void ValidateHistory( EditorHistoryChange change, EditorHistoryDirection direction ) { - _ = ResolveHistory(change, direction); + ChangeData data = m_serialization.Deserialize(change.payload.ReadBytes()); + if (!m_drafts.ContainsKey(data.assetId)) + throw new InvalidOperationException("Reopen the asset document to use its history."); } /// @@ -293,8 +283,6 @@ EditorHistoryDirection direction ChangeData data = m_serialization.Deserialize(change.payload.ReadBytes()); if (!m_drafts.TryGetValue(data.assetId, out Draft? draft)) throw new InvalidOperationException("Reopen the asset document to use its history."); - if (draft.gestureBefore is not null) - throw new InvalidOperationException("Finish the active asset gesture first."); byte[] expected = direction == EditorHistoryDirection.Undo ? data.after : data.before; if (!draft.bytes.AsSpan().SequenceEqual(expected)) throw new InvalidOperationException("The asset draft changed outside this history entry."); @@ -311,8 +299,11 @@ private void Changed(Draft draft) DeleteRecovery(draft.id); return; } - AtomicFile.WriteAllBytes(RecoveryPath(draft.id), m_serialization.Serialize(new Recovery - { assetId = draft.id, hash = draft.hash, baseline = draft.baseline, bytes = draft.bytes })); + + AtomicFile.WriteAllBytes( + RecoveryPath(draft.id), + m_serialization.Serialize(new Recovery { assetId = draft.id, hash = draft.hash, baseline = draft.baseline, bytes = draft.bytes }) + ); if (draft.error.StartsWith("Recovery write failed:", StringComparison.Ordinal)) draft.error = ""; } @@ -326,7 +317,6 @@ private bool Save(Draft draft) { try { - Commit(draft); RefreshPath(draft); draft.hash = m_sources.Save(draft.path, draft.bytes, draft.hash); draft.baseline = draft.bytes; @@ -346,13 +336,9 @@ private bool Revert(Draft draft) { try { - Commit(draft); RefreshPath(draft); AssetSourceSnapshot source = m_sources.Read(draft.path); - byte[] before = draft.bytes; - draft.gestureBefore = before; - draft.bytes = source.bytes; - Commit(draft); + RecordReplace(draft, source.bytes); draft.hash = source.contentHash; draft.baseline = draft.bytes; draft.readOnly = source.isReadOnly; @@ -382,8 +368,7 @@ private void Open(EditorDocumentContext document) AssetPath path = AssetPath.Parse(document.assetPath); if (m_assets.TryGetInfo(document.assetId, out AssetInfo? info) && info is not null) path = info.assetPath; - Recovery? recovery = File.Exists(RecoveryPath(document.assetId)) - ? m_serialization.Deserialize(File.ReadAllBytes(RecoveryPath(document.assetId))) : null; + Recovery? recovery = File.Exists(RecoveryPath(document.assetId)) ? m_serialization.Deserialize(File.ReadAllBytes(RecoveryPath(document.assetId))) : null; if (recovery is not null && recovery.assetId != document.assetId) throw new InvalidDataException("Asset recovery identity does not match its document."); AssetSourceSnapshot? source = null; @@ -391,9 +376,19 @@ private void Open(EditorDocumentContext document) { source = m_sources.Read(path); } - catch (Exception error) when (recovery is not null && Recoverable(error)) { } - var draft = new Draft(document.assetId, document.documentId, path, recovery?.bytes ?? source!.bytes, - recovery?.baseline ?? source!.bytes, recovery?.hash ?? source!.contentHash, source?.isReadOnly ?? true); + catch (Exception error) when (recovery is not null && Recoverable(error)) + { + } + + var draft = new Draft( + document.assetId, + document.documentId, + path, + recovery?.bytes ?? source!.bytes, + recovery?.baseline ?? source!.bytes, + recovery?.hash ?? source!.contentHash, + source?.isReadOnly ?? true + ); if (recovery is not null && source?.contentHash != recovery.hash) draft.error = "Source changed externally. Recovery retained; resolve or Revert explicitly."; m_drafts.Add(draft.id, draft); @@ -412,16 +407,10 @@ public void Start() } /// - /// Completes abandoned gestures, imports explicit saves and reconciles source changes. + /// Imports explicit saves and reconciles source changes without owning UI gesture lifetimes. /// public void Update() { - m_updateSerial++; - if (m_activeGroup.Any(id => m_drafts[id].lastInspection >= 0 && m_drafts[id].lastInspection < m_updateSerial - 1)) - CommitMany(m_activeGroup); - foreach (Draft draft in m_drafts.Values) - if (draft.gestureBefore is not null && draft.lastInspection >= 0 && draft.lastInspection < m_updateSerial - 1) - Commit(draft); foreach (AssetPath path in m_imports.ToArray()) { m_imports.Remove(path); @@ -429,8 +418,14 @@ public void Update() { if (!m_assets.Import(path)) { - string diagnostics = m_assets.TryGetInfo(path, out AssetInfo? info) && info is not null - ? string.Join("\n", info.diagnostics) : "The asset importer did not publish this asset."; + string diagnostics = m_assets.TryGetInfo( + path, + out AssetInfo? info + ) + && info is not null ? string.Join( + "\n", + info.diagnostics + ) : "The asset importer did not publish this asset."; foreach (Draft draft in m_drafts.Values.Where(value => value.path == path)) draft.error = "Saved; import failed: " + diagnostics; } @@ -441,6 +436,7 @@ public void Update() draft.error = "Saved; import failed: " + error.Message; } } + if (m_sourceRevision != m_assets.revision) { foreach (Draft draft in m_drafts.Values) @@ -458,11 +454,12 @@ private void SynchronizeSource(Draft draft) draft.readOnly = source.isReadOnly; if (draft.hash == source.contentHash) return; - if (draft.isDirty || draft.gestureBefore is not null) + if (draft.isDirty) { draft.error = "Source changed externally. Your draft is retained; Revert reloads it without overwriting the external edit."; return; } + draft.bytes = source.bytes; draft.baseline = source.bytes; draft.hash = source.contentHash; @@ -480,13 +477,12 @@ private void SynchronizeSource(Draft draft) /// public void Dispose() { - CommitMany(m_activeGroup); foreach (Draft draft in m_drafts.Values) { - Commit(draft); if (draft.isDirty) Changed(draft); } + m_provider?.Dispose(); m_provider = null; } @@ -498,15 +494,16 @@ private void DeleteRecovery(Guid id) if (File.Exists(path)) File.Delete(path); } + private static bool Recoverable(Exception error) => error is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException or FormatException && Inno.Core.Execution.RetirementPendingException.Find(error) is null; - private sealed class Provider(AssetDraftDocuments owner) : EditorDocumentProvider { /// /// Gets the stable identity used to reference this value across subsystem boundaries. /// -public override string id => owner.m_providerId; + public override string id => owner.m_providerId; + /// /// Checks whether this document handler supports the selected asset. /// @@ -516,14 +513,14 @@ private sealed class Provider(AssetDraftDocuments owner) : EditorDocumen /// /// when the documented condition is satisfied; otherwise, . /// -public override bool CanOpen(string assetPath) => assetPath.EndsWith(owner.m_extension, StringComparison.OrdinalIgnoreCase); + public override bool CanOpen(string assetPath) => assetPath.EndsWith(owner.m_extension, StringComparison.OrdinalIgnoreCase); /// /// Opens the requested resource and establishes its active lifetime. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -public override void Open(EditorDocumentContext context) => owner.Open(context); + public override void Open(EditorDocumentContext context) => owner.Open(context); /// /// Persists the supplied value through the configured storage contract. /// @@ -533,7 +530,7 @@ private sealed class Provider(AssetDraftDocuments owner) : EditorDocumen /// /// when the documented condition is satisfied; otherwise, . /// -public override bool Save(EditorDocumentContext context) => owner.Save(owner.m_drafts[context.assetId]); + public override bool Save(EditorDocumentContext context) => owner.Save(owner.m_drafts[context.assetId]); /// /// Restores the draft from the latest imported asset state. /// @@ -543,16 +540,15 @@ private sealed class Provider(AssetDraftDocuments owner) : EditorDocumen /// /// when the documented condition is satisfied; otherwise, . /// -public override bool Revert(EditorDocumentContext context) => owner.Revert(owner.m_drafts[context.assetId]); + public override bool Revert(EditorDocumentContext context) => owner.Revert(owner.m_drafts[context.assetId]); /// /// Closes the active resource and releases its operation-scoped state. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -public override void Close(EditorDocumentContext context) + public override void Close(EditorDocumentContext context) { - owner.CommitMany(owner.m_activeGroup); owner.DeleteRecovery(context.assetId); owner.m_drafts.Remove(context.assetId); } @@ -566,8 +562,6 @@ public sealed class Draft internal byte[] bytes; internal byte[] baseline; internal string hash; - internal byte[]? gestureBefore; - internal long lastInspection = -1; internal Draft( Guid id, Guid documentId, @@ -585,6 +579,7 @@ bool readOnly this.hash = hash; this.readOnly = readOnly; } + /// /// Gets the persistent source identity. /// @@ -613,16 +608,28 @@ bool readOnly private sealed class Recovery : ISerializable { - [SerializableProperty] internal Guid assetId { get; set; } - [SerializableProperty] internal string hash { get; set; } = ""; - [SerializableProperty] internal byte[] baseline { get; set; } = []; - [SerializableProperty] internal byte[] bytes { get; set; } = []; + [SerializableProperty] + internal Guid assetId { get; set; } + + [SerializableProperty] + internal string hash { get; set; } = ""; + + [SerializableProperty] + internal byte[] baseline { get; set; } = []; + + [SerializableProperty] + internal byte[] bytes { get; set; } = []; } private sealed class ChangeData : ISerializable { - [SerializableProperty] internal Guid assetId { get; set; } - [SerializableProperty] internal byte[] before { get; set; } = []; - [SerializableProperty] internal byte[] after { get; set; } = []; + [SerializableProperty] + internal Guid assetId { get; set; } + + [SerializableProperty] + internal byte[] before { get; set; } = []; + + [SerializableProperty] + internal byte[] after { get; set; } = []; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetCreationRegistry.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetCreationRegistry.cs similarity index 53% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetCreationRegistry.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetCreationRegistry.cs index 22337749c..a6f4895c8 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetCreationRegistry.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetCreationRegistry.cs @@ -6,12 +6,10 @@ using Inno.Editor.Assets; using Inno.Extensibility.Types; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal sealed class AssetCreationRegistry : TypeRegistry { - internal AssetCreationRegistry(TypeCatalog types) - : base(types) + internal AssetCreationRegistry(TypeCatalog types) : base(types) { } @@ -21,7 +19,6 @@ internal bool TryGet( string id, out Registration? registration ) => current.byId.TryGetValue(id, out registration); - /// /// Builds a validated result from the current immutable input snapshot. /// @@ -31,52 +28,44 @@ out Registration? registration /// /// The validated snapshot that represents the completed operation. /// -protected override Snapshot Build(TypeCacheSnapshot types) + protected override Snapshot Build(TypeCacheSnapshot types) { var registrations = new List(); var byId = new Dictionary(StringComparer.Ordinal); var byMenuPath = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (Type type in types.GetSubTypesOf() - .Select(typeRef => typeRef.Resolve(types)) - .Where(static type => !type.IsAbstract) - .OrderBy(static type => type.FullName, StringComparer.Ordinal)) + foreach (Type type in types.GetSubTypesOf().Select(typeRef => typeRef.Resolve(types)).Where(static type => !type.IsAbstract).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + )) { - AssetCreationMenuAttribute declaration = type.GetCustomAttribute(false) - ?? throw new InvalidOperationException( - $"Asset creation template '{type.FullName}' requires " + - $"'{typeof(AssetCreationMenuAttribute).FullName}' source metadata."); + AssetCreationMenuAttribute declaration = type.GetCustomAttribute(false) ?? throw new InvalidOperationException($"Asset creation template '{type.FullName}' requires " + $"'{typeof(AssetCreationMenuAttribute).FullName}' source metadata."); AssetCreationTemplate template = CreateExtension(type); - if (!typeof(AssetObject).IsAssignableFrom(template.assetType) - || template.assetType.IsAbstract) + if (!typeof(AssetObject).IsAssignableFrom(template.assetType) || template.assetType.IsAbstract) { - throw new InvalidOperationException( - $"Asset creation template '{type.FullName}' declares invalid asset type " + - $"'{template.assetType.FullName}'."); + throw new InvalidOperationException($"Asset creation template '{type.FullName}' declares invalid asset type " + $"'{template.assetType.FullName}'."); } var registration = new Registration(type, template, declaration); if (!byId.TryAdd(declaration.id, registration)) { - throw new InvalidOperationException( - $"Asset creation template id '{declaration.id}' is registered by both " + - $"'{byId[declaration.id].implementationType.FullName}' and '{type.FullName}'."); + throw new InvalidOperationException($"Asset creation template id '{declaration.id}' is registered by both " + $"'{byId[declaration.id].implementationType.FullName}' and '{type.FullName}'."); } + if (!byMenuPath.TryAdd(declaration.menuPath, registration)) { - throw new InvalidOperationException( - $"Asset creation menu path '{declaration.menuPath}' is registered by both " + - $"'{byMenuPath[declaration.menuPath].implementationType.FullName}' and '{type.FullName}'."); + throw new InvalidOperationException($"Asset creation menu path '{declaration.menuPath}' is registered by both " + $"'{byMenuPath[declaration.menuPath].implementationType.FullName}' and '{type.FullName}'."); } + registrations.Add(registration); } - Registration[] ordered = registrations - .OrderBy(static value => value.declaration.groupOrder) - .ThenBy(static value => value.declaration.menuPath, StringComparer.Ordinal) - .ThenBy(static value => value.declaration.itemOrder) - .ThenBy(static value => value.declaration.id, StringComparer.Ordinal) - .ToArray(); + Registration[] ordered = registrations.OrderBy(static value => value.declaration.groupOrder).ThenBy( + static value => value.declaration.menuPath, + StringComparer.Ordinal + ).ThenBy(static value => value.declaration.itemOrder).ThenBy( + static value => value.declaration.id, + StringComparer.Ordinal + ).ToArray(); return new Snapshot(ordered, byId); } @@ -86,15 +75,12 @@ protected override Snapshot Build(TypeCacheSnapshot types) /// /// The immutable state snapshot consumed by this operation. /// -protected override void DisposeSnapshot(Snapshot snapshot) - => DisposeExtensions(snapshot.registrations.Select(static value => value.template)); - + protected override void DisposeSnapshot(Snapshot snapshot) => DisposeExtensions(snapshot.registrations.Select(static value => value.template)); internal sealed record Registration( Type implementationType, AssetCreationTemplate template, AssetCreationMenuAttribute declaration ); - internal sealed record Snapshot( Registration[] registrations, IReadOnlyDictionary byId diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs similarity index 54% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs index 44513cce6..7775c5406 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.Creation.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Creation.cs @@ -6,25 +6,52 @@ using Inno.Core.IO; using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Handles asset creation actions in the Editor file browser. /// -public sealed partial class AssetEditorModule +public sealed partial class AssetEditingModule { /// - /// Selects a newly created source and starts its shared inline rename interaction. + /// Creates a uniquely named directory and records its removal and restoration in shared History. /// - /// - /// The newly indexed writable source. + /// + /// An existing writable source directory. /// - public void BeginCreatedSourceRename(AssetFileEntry entry) + /// + /// The indexed identity of the newly created directory. + /// + /// + /// The source is read-only, unavailable, or cannot retain the History entry. + /// + public AssetFileEntry CreateDirectory(AssetPath parent) { - ArgumentNullException.ThrowIfNull(entry); - EditorInteraction interaction = m_interactions.For(FileBrowserInteractionIds.C_AREA, entry); - _ = interaction.Select(); - _ = interaction.Execute(FileBrowserInteractionIds.C_RENAME); + AssetSourceMount mount = m_pipeline.sourceMounts.SingleOrDefault(source => source.id == parent.source) ?? throw new InvalidOperationException("The asset source mount is unavailable."); + if (mount.isReadOnly) + throw new InvalidOperationException("Installed asset sources are read-only."); + string prefix = parent.localPath.Length == 0 ? string.Empty : parent.localPath + "/"; + int suffix = 0; + AssetPath candidate; + do + { + string name = suffix == 0 ? "New Folder" : $"New Folder {suffix}"; + candidate = new AssetPath(parent.source, prefix + name); + suffix++; + } + while (m_pipeline.TryGetFileSystemEntry(candidate, out _)); + m_pipeline.CreateDirectory(candidate); + var data = new AssetHistoryData(AssetHistoryOperationKind.CreateDirectory, candidate.ToString(), string.Empty, true, []); + using var change = new EditorHistoryChange(AssetHistoryKinds.SourceOperation, EditorHistoryPayload.FromBytes(data.Encode())); + try + { + m_interactions.history.RecordApplied("Create Folder", change); + } + catch (Exception failure) + { + RollbackAndRethrow(failure, () => m_pipeline.Delete(candidate)); + } + + return m_pipeline.TryGetFileSystemEntry(candidate, out AssetFileEntry entry) ? entry : throw new InvalidOperationException("The created directory was not indexed."); } /// @@ -49,8 +76,7 @@ public AssetFileEntry CreateSource( AssetPath path, ReadOnlySpan bytes ) { - AssetSourceMount mount = m_pipeline.sourceMounts.SingleOrDefault(source => source.id == path.source) - ?? throw new InvalidOperationException("The asset source mount is unavailable."); + AssetSourceMount mount = m_pipeline.sourceMounts.SingleOrDefault(source => source.id == path.source) ?? throw new InvalidOperationException("The asset source mount is unavailable."); if (mount.isReadOnly) throw new InvalidOperationException("Installed asset sources are read-only."); string destination = mount.Resolve(path.localPath); @@ -80,7 +106,7 @@ ReadOnlySpan bytes m_pipeline.Delete(path); throw; } - return m_pipeline.TryGetFileSystemEntry(path, out AssetFileEntry entry) ? entry - : throw new InvalidOperationException("The created asset was not indexed."); + + return m_pipeline.TryGetFileSystemEntry(path, out AssetFileEntry entry) ? entry : throw new InvalidOperationException("The created asset was not indexed."); } } diff --git a/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs new file mode 100644 index 000000000..7b440e600 --- /dev/null +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.Selection.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Assets.Pipeline; +using Inno.Editor.Core; +using Inno.Editor.Interactions; +using Inno.Scripting.Api; + +namespace Inno.Editor.Assets; +/// +/// Applies validated selection operations through the authoritative asset and History owners. +/// +public sealed partial class AssetEditingModule +{ + /// + /// Checks the entire source selection before a batch mutation, including extension validation and read-only ownership. + /// + /// + /// The immediate Editor context. + /// + /// + /// Fresh source entries in selection order. + /// + /// + /// The first rejection, or a valid result when every source can be deleted. + /// + [ScriptingApiIgnore] + public AssetOperationValidation ValidateDeleteSelection( + EditorContext editor, + IReadOnlyList targets + ) { + ArgumentNullException.ThrowIfNull(editor); + ArgumentNullException.ThrowIfNull(targets); + if (targets.Count == 0) + return AssetOperationValidation.Invalid("The selection contains unavailable entries."); + foreach (AssetFileEntry target in targets) + { + if (target.isReadOnly + || target.identity.runtimeIdentity is null + || !TryCreateContext( + editor, + target.assetPath.ToString(), + out AssetEditorContext? context + ) + || context is null) + return AssetOperationValidation.Invalid("Every selected source must be available and writable."); + AssetOperationValidation validation = m_editors.Resolve(context.assetType).ValidateDelete(context); + if (!validation.isValid) + return validation; + } + + return AssetOperationValidation.valid; + } + + /// + /// Deletes minimal selected source roots in one History transaction, restoring earlier deletions if a later one fails. + /// + /// + /// The immediate Editor context. + /// + /// + /// Fresh selected source entries; nested entries are covered by their selected directory. + /// + /// + /// Validation or a mutation fails; rollback failures remain explicit. + /// + [ScriptingApiIgnore] + public void DeleteSelection( + EditorContext editor, + IReadOnlyList targets + ) { + AssetOperationValidation validation = ValidateDeleteSelection(editor, targets); + if (!validation.isValid) + throw new InvalidOperationException(validation.message); + string[] roots = SelectedRoots(targets); + using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction("Delete Assets"); + foreach (string path in roots) + { + if (!TryCreateContext(editor, path, out AssetEditorContext? context) || context is null || !DeleteWithHistory(context, m_interactions.history)) + throw new InvalidOperationException($"Selected source '{path}' could not be deleted."); + } + + transaction.Commit(); + } + + /// + /// Validates every minimal selected source and rejects destination collisions before a batch move. + /// + /// + /// Live source entries in selection order. + /// + /// + /// The writable project destination. + /// + /// + /// True when every source can move to a distinct destination. + /// + [ScriptingApiIgnore] + public bool CanMoveSelection( + IReadOnlyList targets, + string targetDirectory + ) { + ArgumentNullException.ThrowIfNull(targets); + if (targets.Count == 0 || targets.Any(value => value.isReadOnly || value.identity.runtimeIdentity is null)) + return false; + var destinations = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (string source in SelectedRoots(targets)) + if (!TryPrepareMove(source, targetDirectory, true, out _, out _, out string destination) || !destinations.Add(destination)) + return false; + return true; + } + + /// + /// Moves selected source roots in one transaction; a later failure restores earlier moves. + /// + /// + /// Live entries, including descendants covered by a selected directory. + /// + /// + /// The writable project destination. + /// + /// + /// Any source or destination rejects the operation. + /// + [ScriptingApiIgnore] + public void MoveSelection( + IReadOnlyList targets, + string targetDirectory + ) { + if (!CanMoveSelection(targets, targetDirectory)) + throw new InvalidOperationException("The selected assets cannot move to this directory."); + string[] roots = SelectedRoots(targets); + using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction("Move Assets"); + foreach (string source in roots) + _ = MoveToDirectoryWithHistory(source, targetDirectory, m_interactions.history); + transaction.Commit(); + } + + private static string[] SelectedRoots(IReadOnlyList targets) + { + string[] paths = targets.Select(value => value.assetPath.ToString()).Distinct(StringComparer.Ordinal).ToArray(); + return paths.Where(path => !paths.Any(parent => parent != path && path.StartsWith(parent + "/", StringComparison.Ordinal))).ToArray(); + } +} diff --git a/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs new file mode 100644 index 000000000..6f10882ca --- /dev/null +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditingModule.cs @@ -0,0 +1,651 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.ExceptionServices; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Core.Logging; +using Inno.Extensibility.Types; +using Inno.Editor.Core; +using Inno.Editor.Assets; +using Inno.Editor.Interactions; +using Inno.Plugins.Authoring; + +namespace Inno.Editor.Assets; +/// +/// Owns source mutations, asset extension dispatch and neutral shared-history records independently of presentation. +/// +[EditorModule("assets.editing", order: 90)] +public sealed partial class AssetEditingModule : EditorModule +{ + private readonly AssetPipeline m_pipeline; + private readonly AssetEditorRegistry m_editors; + private readonly AssetCreationRegistry m_creations; + private readonly EditorInteractions m_interactions; + private readonly Logger m_log; + private readonly PluginEnvironment m_plugins; + private EditorContext? m_context; + /// + /// Creates the asset editing service for one authoring generation. + /// + /// + /// The active editor interaction entry point. + /// + /// + /// The isolated authoring asset pipeline used by browser operations. + /// + /// + /// The active Plugin catalog that authoritatively identifies Plugin-owned source mounts. + /// + /// + /// The active type catalog used to build extension registry snapshots. + /// + /// + /// The application log router used for Asset Browser operation diagnostics. + /// + /// + /// Thrown when any required service is . + /// + public AssetEditingModule( + EditorInteractions interactions, + AssetPipeline pipeline, + PluginEnvironment plugins, + TypeCatalog types, + LogRouter logs + ) { + m_interactions = interactions ?? throw new ArgumentNullException(nameof(interactions)); + m_pipeline = pipeline ?? throw new ArgumentNullException(nameof(pipeline)); + m_plugins = plugins ?? throw new ArgumentNullException(nameof(plugins)); + ArgumentNullException.ThrowIfNull(types); + ArgumentNullException.ThrowIfNull(logs); + m_log = logs.CreateLogger(); + m_editors = new AssetEditorRegistry(types); + m_creations = new AssetCreationRegistry(types); + } + + /// + /// Gets the editor interaction owner used by source operations. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public EditorInteractions interactions => m_interactions; + + /// + /// Gets the authoritative authoring pipeline; mutations must use its existing ownership protocol. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetPipeline pipeline => m_pipeline; + /// + /// Gets the current immutable creation menu declarations without retaining template instances. + /// + public IReadOnlyList creationTemplates => m_creations.templates.Select(static registration => registration.declaration).ToArray(); + + /// + /// Checks that a creation template and writable destination are available. + /// + /// + /// The source-relative writable destination directory. + /// + /// + /// The stable identity of a discovered creation template. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool CanCreateAsset( + string directory, + string templateId + ) { + if (!m_pipeline.isInitialized || !m_creations.TryGet(templateId, out _)) + return false; + AssetPath path = AssetPath.Parse(directory); + AssetSourceMount? mount = m_pipeline.sourceMounts.FirstOrDefault(source => source.id == path.source); + return mount is { isReadOnly: false } && (string.IsNullOrEmpty(path.localPath) || m_pipeline.TryGetFileSystemEntry(path, out AssetFileEntry entry) && entry.isDirectory); + } + + /// + /// Creates and imports a source using a discovered template and records the committed change. + /// + /// + /// The source-relative writable destination directory. + /// + /// + /// The stable identity of a discovered creation template. + /// + /// + /// The committed source identity, or a validation result describing why the operation is unavailable. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetFileEntry CreateAsset( + string directory, + string templateId + ) { + if (!m_creations.TryGet(templateId, out AssetCreationRegistry.Registration? registration) || registration is null) + { + throw new InvalidOperationException($"Asset creation template '{templateId}' is unavailable."); + } + + if (!CanCreateAsset(directory, templateId)) + throw new InvalidOperationException("The selected Asset directory is not writable."); + AssetCreationMenuAttribute declaration = registration.declaration; + AssetPath parent = AssetPath.Parse(directory); + AssetSourceMount mount = m_pipeline.sourceMounts.Single(source => source.id == parent.source); + string prefix = parent.localPath.TrimEnd('/'); + if (prefix.Length != 0) + prefix += "/"; + int suffix = 1; + AssetPath path; + do + { + string number = suffix == 1 ? string.Empty : " " + suffix; + path = new AssetPath(parent.source, prefix + declaration.defaultName + number + declaration.extension); + suffix++; + } + while (File.Exists(mount.Resolve(path.localPath)) || File.Exists(mount.Resolve(path.localPath) + ".imeta")); + AssetObject candidate = registration.template.Create() ?? throw new InvalidOperationException($"Asset creation template '{templateId}' returned no asset."); + if (candidate.GetType() != registration.template.assetType) + { + throw new InvalidOperationException($"Asset creation template '{templateId}' returned '{candidate.GetType().FullName}' " + $"instead of '{registration.template.assetType.FullName}'."); + } + + AssetSourceStore sources = m_pipeline.CreateSourceStore(); + byte[] source = registration.template.Encode( + new AssetCreationContext(sources), + candidate + ) ?? throw new InvalidOperationException($"Asset creation template '{templateId}' returned no source data."); + return CreateSource(path, source); + } + + /// + /// Determines whether the active Plugin catalog owns a source mount. + /// + /// + /// The source mount identity to query. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool IsPluginSource(AssetSourceId source) => m_plugins.TryGet(source, out _); + /// + /// Determines whether an indexed entry is the root of an installed Plugin. + /// + /// + /// The currently indexed source identity. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool IsPluginRoot(AssetFileEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + return string.IsNullOrEmpty(entry.assetPath.localPath) && IsPluginSource(entry.source); + } + + /// + /// Captures the currently indexed source and extension metadata for one immediate operation. + /// + /// + /// The current editor context used only during this invocation. + /// + /// + /// The isolated source-relative path to resolve. + /// + /// + /// Receives the current source snapshot, or null when the source is unavailable. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool TryCreateContext( + EditorContext editor, + string relativePath, + out AssetEditorContext? context + ) { + if (!m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry)) + { + context = null; + return false; + } + + _ = m_pipeline.TryGetInfo(AssetPath.Parse(relativePath), out AssetInfo? info); + _ = m_pipeline.TryGetAssetType(AssetPath.Parse(relativePath), out Type? assetType); + context = new AssetEditorContext(editor, m_interactions, entry.assetPath.ToString(), entry.name, entry.isDirectory, info, assetType, () => CreateDefaultDragData(entry)); + return true; + } + + /// + /// Opens a source using its current asset editor extension and reports rejected or failed opens. + /// + /// + /// The immutable metadata captured for this immediate source operation. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool Open(AssetEditorContext context) + { + AssetEditor editor = m_editors.Resolve(context.assetType); + if (!editor.CanOpen(context)) + return false; + try + { + editor.Open(context); + return true; + } + catch (Exception exception) + { + m_log.Write(LogLevel.Error, "Failed to open asset '{0}': {1}", [context.relativePath, exception]); + return false; + } + } + + /// + /// Validates a requested name without modifying the source. + /// + /// + /// The immutable metadata captured for this immediate source operation. + /// + /// + /// The requested entry name without its protected file extension. + /// + /// + /// The committed source identity, or a validation result describing why the operation is unavailable. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public EditorValidationResult ValidateRename( + AssetEditorContext context, + string newName + ) { + AssetEditor editor = m_editors.Resolve(context.assetType); + return ToEditorValidation(ValidateRename(editor, context, Normalize(context.relativePath), newName, validateEditor: true, out _)); + } + + /// + /// Renames a source atomically and records its reversible history payload. + /// + /// + /// The immutable metadata captured for this immediate source operation. + /// + /// + /// The requested entry name without its protected file extension. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public void Rename( + AssetEditorContext context, + string newName + ) { + AssetEditor editor = m_editors.Resolve(context.assetType); + AssetOperationValidation validation = ValidateRename(editor, context, Normalize(context.relativePath), newName, validateEditor: false, out string targetPath); + if (!validation.isValid) + throw new InvalidOperationException(validation.message); + string sourcePath = Normalize(context.relativePath); + EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); + if (!result.succeeded) + throw new InvalidOperationException(result.message); + var data = new AssetHistoryData(AssetHistoryOperationKind.Move, sourcePath, targetPath, isDirectory: context.isDirectory, archive: []); + try + { + m_interactions.history.RecordApplied("Rename Asset", new EditorHistoryChange(AssetHistoryKinds.SourceOperation, EditorHistoryPayload.FromBytes(data.Encode()))); + } + catch (Exception exception) + { + RollbackAndRethrow(exception, () => RequireMove(editor, context, targetPath, sourcePath)); + } + } + + /// + /// Checks whether a source can move into the destination without a cycle or name collision. + /// + /// + /// The current isolated source-relative path. + /// + /// + /// The isolated destination directory. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool CanMoveToDirectory( + string sourcePath, + string targetDirectory + ) => TryPrepareMove(sourcePath, targetDirectory, validateEditor: true, out _, out _, out _); + /// + /// Moves a source and records the committed mutation in the supplied history. + /// + /// + /// The current isolated source-relative path. + /// + /// + /// The isolated destination directory. + /// + /// + /// The authoritative history that receives the committed mutation. + /// + /// + /// The committed source identity, or a validation result describing why the operation is unavailable. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetFileEntry MoveToDirectoryWithHistory( + string sourcePath, + string targetDirectory, + IEditorHistory history + ) { + ArgumentNullException.ThrowIfNull(history); + if (!TryPrepareMove( + sourcePath, + targetDirectory, + validateEditor: true, + out AssetEditorContext? context, + out AssetEditor? editor, + out string targetPath + ) + || context is null + || editor is null) + { + throw new InvalidOperationException($"Asset '{sourcePath}' cannot be moved to '{targetDirectory}'."); + } + + string normalizedSource = Normalize(sourcePath); + EditorHistoryResult result = MoveAsset(editor, context, normalizedSource, targetPath); + if (!result.succeeded) + throw new InvalidOperationException(result.message); + var data = new AssetHistoryData(AssetHistoryOperationKind.Move, normalizedSource, targetPath, context.isDirectory, archive: []); + try + { + history.RecordApplied("Move Asset", new EditorHistoryChange(AssetHistoryKinds.SourceOperation, EditorHistoryPayload.FromBytes(data.Encode()))); + } + catch (Exception exception) + { + RollbackAndRethrow(exception, () => RequireMove(editor, context, targetPath, normalizedSource)); + } + + return m_pipeline.TryGetFileSystemEntry( + AssetPath.Parse(targetPath), + out AssetFileEntry moved + ) ? moved : throw new InvalidOperationException($"Moved asset '{targetPath}' is unavailable after the transaction."); + } + + internal bool Delete(AssetEditorContext context) + { + AssetEditor editor = m_editors.Resolve(context.assetType); + AssetOperationValidation validation = editor.ValidateDelete(context); + if (!validation.isValid) + { + m_log.Write(LogLevel.Warn, "Asset delete was rejected for '{0}': {1}", [context.relativePath, validation.message]); + return false; + } + + string path = context.relativePath; + m_pipeline.Delete(AssetPath.Parse(path)); + try + { + editor.OnDeleted(context); + } + catch (Exception exception) + { + m_log.Write(LogLevel.Error, "Asset editor delete hook failed for '{0}': {1}", [path, exception]); + } + + return true; + } + + /// + /// Deletes a source with a recoverable archive and records the committed mutation. + /// + /// + /// The captured source metadata to validate and delete. + /// + /// + /// The authoritative History receiving this mutation. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool DeleteWithHistory( + AssetEditorContext asset, + IEditorHistory history + ) { + ArgumentNullException.ThrowIfNull(history); + ArgumentNullException.ThrowIfNull(asset); + byte[] archive = AssetSourceArchive.Capture(m_pipeline, asset.relativePath, out bool isDirectory); + if (!Delete(asset)) + return false; + var data = new AssetHistoryData(AssetHistoryOperationKind.Delete, Normalize(asset.relativePath), string.Empty, isDirectory, archive); + try + { + history.RecordApplied("Delete Asset", new EditorHistoryChange(AssetHistoryKinds.SourceOperation, EditorHistoryPayload.FromBytes(data.Encode()))); + } + catch (Exception exception) + { + RollbackAndRethrow(exception, () => AssetSourceArchive.Restore(m_pipeline, Normalize(asset.relativePath), isDirectory, archive)); + } + + return true; + } + + internal void MoveFromHistory( + string sourcePath, + string targetPath + ) { + EditorContext editorContext = m_context ?? throw new InvalidOperationException("Asset editor module is not attached."); + if (!TryCreateContext(editorContext, sourcePath, out AssetEditorContext? context) || context is null) + throw new InvalidOperationException($"Asset '{sourcePath}' is no longer available."); + AssetEditor editor = m_editors.Resolve(context.assetType); + EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); + if (!result.succeeded) + throw new InvalidOperationException(result.message); + } + + internal void DeleteFromHistory(string relativePath) + { + EditorContext editorContext = m_context ?? throw new InvalidOperationException("Asset editor module is not attached."); + if (!TryCreateContext(editorContext, relativePath, out AssetEditorContext? context) || context is null) + throw new InvalidOperationException($"Asset '{relativePath}' is no longer available."); + if (!Delete(context)) + throw new InvalidOperationException($"Asset '{relativePath}' could not be deleted."); + } + + /// + /// Selects the current source identity without retaining an indexed entry across generations. + /// + /// + /// The isolated source-relative path to resolve. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public void SelectPath(string relativePath) + { + object? target = m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry) ? entry : null; + m_interactions.SetSelection(target); + } + + /// + /// Creates identity-based drag data when the current extension permits dragging. + /// + /// + /// The immutable metadata captured for this immediate source operation. + /// + /// + /// Receives identity-based drag data, or null when dragging is unavailable. + /// + /// + /// True when the request is accepted; otherwise false. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public bool TryCreateDragData( + AssetEditorContext context, + out EditorDragData? data + ) { + AssetEditor editor = m_editors.Resolve(context.assetType); + if (!editor.CanStartDrag(context)) + { + data = null; + return false; + } + + data = editor.CreateDragData(context); + return data is not null; + } + + /// + /// Initializes this feature when its owning runtime becomes active. + /// + /// + /// The context that supplies state and services for this operation. + /// + protected override void OnStart(EditorContext context) + { + m_context = context ?? throw new ArgumentNullException(nameof(context)); + } + + /// + /// Stops this feature before its owning runtime releases the active generation. + /// + /// + /// The context that supplies state and services for this operation. + /// + protected override void OnStop(EditorContext context) + { + m_context = null; + } + + /// + /// Releases resources retained by this feature after it has stopped. + /// + protected override void OnDispose() + { + m_creations.Dispose(); + m_editors.Dispose(); + } + + private AssetOperationValidation ValidateRename( + AssetEditor editor, + AssetEditorContext context, + string sourcePath, + string newName, + bool validateEditor, + out string targetPath + ) { + newName = newName.Trim(); + targetPath = string.Empty; + if (string.IsNullOrEmpty(newName)) + return AssetOperationValidation.Invalid("An asset name is required."); + if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || newName.Contains('/') || newName.Contains('\\')) + { + return AssetOperationValidation.Invalid("Asset names cannot contain path separators or invalid file-name characters."); + } + + string renamedEntry = context.isDirectory ? newName : newName + Path.GetExtension(sourcePath); + string parent = Normalize(Path.GetDirectoryName(sourcePath)); + targetPath = string.IsNullOrEmpty(parent) ? renamedEntry : $"{parent}/{renamedEntry}"; + if (!string.Equals(sourcePath, targetPath, StringComparison.OrdinalIgnoreCase) && m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) + { + return AssetOperationValidation.Invalid($"Asset '{targetPath}' already exists."); + } + + return validateEditor ? editor.ValidateRename(context, targetPath) : AssetOperationValidation.valid; + } + + private bool TryPrepareMove( + string sourcePath, + string targetDirectory, + bool validateEditor, + out AssetEditorContext? context, + out AssetEditor? editor, + out string targetPath + ) { + context = null; + editor = null; + targetPath = string.Empty; + string source = Normalize(sourcePath); + string directory = Normalize(targetDirectory); + AssetPath isolatedSource = AssetPath.Parse(source); + AssetPath isolatedDirectory = AssetPath.Parse(directory); + if (isolatedSource.source != AssetSourceId.project || isolatedDirectory.source != AssetSourceId.project) + { + return false; + } + + EditorContext? editorContext = m_context; + if (editorContext is null || string.IsNullOrEmpty(source) || !TryCreateContext(editorContext, source, out context) || context is null) + { + return false; + } + + if (!string.IsNullOrEmpty(directory) && (!m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(directory), out AssetFileEntry destination) || !destination.isDirectory)) + { + return false; + } + + string parent = Normalize(Path.GetDirectoryName(source)); + if (string.Equals(parent, directory, StringComparison.OrdinalIgnoreCase)) + return false; + if (context.isDirectory && (string.Equals(source, directory, StringComparison.OrdinalIgnoreCase) || directory.StartsWith(source + "/", StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + string name = Path.GetFileName(source); + targetPath = string.IsNullOrEmpty(directory) ? name : $"{directory}/{name}"; + if (m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) + return false; + editor = m_editors.Resolve(context.assetType); + return !validateEditor || editor.ValidateRename(context, targetPath).isValid; + } + + private EditorHistoryResult MoveAsset( + AssetEditor editor, + AssetEditorContext context, + string sourcePath, + string targetPath + ) { + m_pipeline.Move(AssetPath.Parse(sourcePath), AssetPath.Parse(targetPath)); + try + { + editor.OnRenamed(context, sourcePath, targetPath); + } + catch (Exception exception) + { + m_log.Write(LogLevel.Error, "Asset editor rename hook failed for '{0}': {1}", [targetPath, exception]); + } + + return EditorHistoryResult.Success(); + } + + private void RequireMove( + AssetEditor editor, + AssetEditorContext context, + string sourcePath, + string targetPath + ) { + EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); + if (!result.succeeded) + throw new InvalidOperationException(result.message); + } + + private static void RollbackAndRethrow( + Exception failure, + Action rollback + ) { + try + { + rollback(); + } + catch (Exception rollbackException) + { + throw new AggregateException("An Asset mutation could not be recorded and its compensation also failed.", failure, rollbackException); + } + + ExceptionDispatchInfo.Capture(failure).Throw(); + } + + private static EditorValidationResult ToEditorValidation(AssetOperationValidation validation) => new(validation.isValid, validation.message); + private static string Normalize(string? path) => string.IsNullOrWhiteSpace(path) ? string.Empty : path.Replace('\\', '/').Trim('/'); + private static EditorDragData CreateDefaultDragData(AssetFileEntry entry) => new(entry, entry.name); +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs similarity index 98% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs index ceeed0b70..97199c762 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditor.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditor.cs @@ -1,7 +1,6 @@ using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Customizes editor interactions for one imported asset type. /// @@ -17,7 +16,6 @@ public abstract class AssetEditor /// when can handle the entry; otherwise, . /// public virtual bool CanOpen(AssetEditorContext context) => false; - /// /// Opens the supplied asset entry when no more specific typed open action handled it. /// @@ -44,7 +42,6 @@ public virtual AssetOperationValidation ValidateRename( AssetEditorContext context, string targetPath ) => AssetOperationValidation.valid; - /// /// Runs after an asset move transaction commits successfully. /// @@ -74,7 +71,6 @@ string newPath /// A valid result when the transaction may proceed, or a diagnostic describing the rejection. /// public virtual AssetOperationValidation ValidateDelete(AssetEditorContext context) => AssetOperationValidation.valid; - /// /// Runs after an asset deletion transaction commits successfully. /// @@ -95,7 +91,6 @@ public virtual void OnDeleted(AssetEditorContext context) /// when drag data can be created; otherwise, . /// public virtual bool CanStartDrag(AssetEditorContext context) => true; - /// /// Creates the managed source, preview label, and validity predicate for an asset drag. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs similarity index 97% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs index c3543b7c5..c770bfcf0 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorAttribute.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorAttribute.cs @@ -1,7 +1,6 @@ using System; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Associates an asset editor with an imported asset type. /// @@ -37,12 +36,10 @@ public AssetEditorAttribute( /// Gets the imported asset type handled by the editor. /// public Type assetType { get; } - /// /// Gets whether derived asset types are accepted. /// public bool useForChildren { get; } - /// /// Gets the tie-breaking priority. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs similarity index 89% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs index 48fb24597..4d5c0fc25 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorContext.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorContext.cs @@ -1,18 +1,15 @@ using System; - using Inno.Assets; using Inno.Editor.Core; using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Provides an immutable snapshot for an asset editor operation. /// public sealed class AssetEditorContext { private readonly Func m_createDefaultDragData; - /// /// Creates an immutable snapshot used by one asset-editor operation. /// @@ -41,7 +38,7 @@ public sealed class AssetEditorContext /// The generation-bound factory for the built-in drag payload. /// /// - /// Thrown when , , , or is . + /// Thrown when , , , or is . /// internal AssetEditorContext( EditorContext editorContext, @@ -60,40 +57,33 @@ Func createDefaultDragData this.isDirectory = isDirectory; this.info = info; this.assetType = assetType; - m_createDefaultDragData = createDefaultDragData ?? - throw new ArgumentNullException(nameof(createDefaultDragData)); + m_createDefaultDragData = createDefaultDragData ?? throw new ArgumentNullException(nameof(createDefaultDragData)); } /// /// Gets the shared editor context. /// public EditorContext editorContext { get; } - /// /// Gets the active editor interaction entry point. /// public EditorInteractions interactions { get; } - /// /// Gets the source-relative path. /// public string relativePath { get; } - /// /// Gets the final source path segment. /// public string name { get; } - /// /// Gets whether the source represents a directory. /// public bool isDirectory { get; } - /// /// Gets the cataloged asset information when available. /// public AssetInfo? info { get; } - /// /// Gets the resolved imported asset type when available. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorRegistry.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorRegistry.cs similarity index 61% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorRegistry.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorRegistry.cs index 506d99ff8..36398d252 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorRegistry.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetEditorRegistry.cs @@ -1,18 +1,15 @@ +using Inno.Editor.Core; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - using Inno.Extensibility.Types; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal sealed class AssetEditorRegistry : TypeRegistry { private static readonly AssetEditor S_DEFAULT_EDITOR = new DefaultAssetEditor(); - - internal AssetEditorRegistry(TypeCatalog types) - : base(types) + internal AssetEditorRegistry(TypeCatalog types) : base(types) { } @@ -24,17 +21,17 @@ internal AssetEditor Resolve(Type? assetType) int bestDistance = int.MaxValue; foreach (Registration registration in current.registrations) { - if (!AssetTypeDistance.TryGet(assetType, registration.assetType, out int distance)) + if (!EditorTypeSpecificity.TryGet(assetType, registration.assetType, out int distance)) continue; if (distance > 0 && !registration.useForChildren) continue; - if (best is null || distance < bestDistance || - distance == bestDistance && registration.priority > best.priority) + if (best is null || distance < bestDistance || distance == bestDistance && registration.priority > best.priority) { best = registration; bestDistance = distance; } } + return best?.editor ?? S_DEFAULT_EDITOR; } @@ -51,33 +48,24 @@ protected override Snapshot Build(TypeCacheSnapshot types) { var instances = new Dictionary(); var registrations = new List(); - foreach (Type type in types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static value => value.FullName, StringComparer.Ordinal)) + foreach (Type type in types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static value => value.FullName, + StringComparer.Ordinal + )) { - AssetEditor editor = instances.TryGetValue(type, out AssetEditor? existing) - ? existing - : CreateExtension(type); + AssetEditor editor = instances.TryGetValue(type, out AssetEditor? existing) ? existing : CreateExtension(type); instances[type] = editor; foreach (AssetEditorAttribute attribute in type.GetCustomAttributes(false)) { - if (registrations.Any(value => - value.assetType == attribute.assetType && - value.useForChildren == attribute.useForChildren && - value.priority == attribute.priority)) + if (registrations.Any(value => value.assetType == attribute.assetType && value.useForChildren == attribute.useForChildren && value.priority == attribute.priority)) { - throw new InvalidOperationException( - $"Asset editor registration for '{attribute.assetType.FullName}' conflicts at " + - $"priority {attribute.priority}."); + throw new InvalidOperationException($"Asset editor registration for '{attribute.assetType.FullName}' conflicts at " + $"priority {attribute.priority}."); } - registrations.Add(new Registration( - attribute.assetType, - attribute.useForChildren, - attribute.priority, - type, - editor)); + + registrations.Add(new Registration(attribute.assetType, attribute.useForChildren, attribute.priority, type, editor)); } } + return new Snapshot(registrations.ToArray()); } @@ -87,11 +75,8 @@ protected override Snapshot Build(TypeCacheSnapshot types) /// /// The immutable state snapshot consumed by this operation. /// - protected override void DisposeSnapshot(Snapshot snapshot) - => DisposeExtensions(snapshot.registrations.Select(static registration => registration.editor)); - + protected override void DisposeSnapshot(Snapshot snapshot) => DisposeExtensions(snapshot.registrations.Select(static registration => registration.editor)); internal sealed record Snapshot(Registration[] registrations); - internal sealed record Registration( Type assetType, bool useForChildren, @@ -99,6 +84,5 @@ internal sealed record Registration( Type implementationType, AssetEditor editor ); - private sealed class DefaultAssetEditor : AssetEditor; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs similarity index 97% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs rename to src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs index dd8c2c42c..7715e2c0c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetOperationValidation.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Editing/AssetOperationValidation.cs @@ -1,5 +1,4 @@ -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Describes whether an editor asset operation may proceed. /// @@ -26,12 +25,10 @@ string message /// Gets whether the operation may proceed. /// public bool isValid { get; } - /// /// Gets the validation diagnostic. /// public string message { get; } - /// /// Gets a successful validation result. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryData.cs b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryData.cs similarity index 97% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryData.cs rename to src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryData.cs index 01d420da4..7142d0fb5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryData.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryData.cs @@ -2,15 +2,15 @@ using System.IO; using System.Text; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal sealed record AssetHistoryData( AssetHistoryOperationKind operationKind, string sourcePath, string targetPath, bool isDirectory, byte[] archive -) { +) +{ internal byte[] Encode() { using var stream = new MemoryStream(); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryHandler.cs b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryHandler.cs similarity index 75% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryHandler.cs rename to src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryHandler.cs index 6d51f7246..ee2638df3 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryHandler.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryHandler.cs @@ -1,20 +1,16 @@ using System; - using Inno.Assets; using Inno.Core.Logging; using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; [EditorHistoryHandler(AssetHistoryKinds.SourceOperation)] internal sealed class AssetHistoryHandler( - AssetEditorModule assets, + AssetEditingModule assets, LogRouter logs ) : EditorHistoryHandler { - private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))) - .CreateLogger(); - + private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))).CreateLogger(); /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -44,8 +40,7 @@ EditorHistoryDirection direction AssetHistoryOperationKind.CreateDirectory => QueryCreateDirectory(data, direction), AssetHistoryOperationKind.Delete => QueryDelete(data, direction), AssetHistoryOperationKind.CreateAsset => QueryCreateAsset(data, direction), - _ => EditorHistoryAvailability.Unavailable("Unknown asset history operation.") - }; + _ => EditorHistoryAvailability.Unavailable("Unknown asset history operation.")}; } catch (Exception exception) { @@ -81,12 +76,8 @@ EditorHistoryDirection direction switch (data.operationKind) { case AssetHistoryOperationKind.Move: - string moveSource = direction == EditorHistoryDirection.Undo - ? data.targetPath - : data.sourcePath; - string moveTarget = direction == EditorHistoryDirection.Undo - ? data.sourcePath - : data.targetPath; + string moveSource = direction == EditorHistoryDirection.Undo ? data.targetPath : data.sourcePath; + string moveTarget = direction == EditorHistoryDirection.Undo ? data.sourcePath : data.targetPath; result = ApplyMove(moveSource, moveTarget); selectionPath = moveTarget; break; @@ -102,6 +93,7 @@ EditorHistoryDirection direction default: return EditorHistoryResult.Failure("Unknown asset history operation."); } + if (!result.succeeded) return result; try @@ -112,6 +104,7 @@ EditorHistoryDirection direction { m_log.Write(LogLevel.Error, "Asset history selection notification failed: {0}", [exception]); } + return EditorHistoryResult.Success(); } catch (Exception exception) @@ -131,8 +124,7 @@ string targetPath } catch (Exception exception) { - if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(sourcePath), out _) && - assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) + if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(sourcePath), out _) && assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) { try { @@ -140,11 +132,10 @@ string targetPath } catch (Exception rollbackException) { - return StateIntegrityFailure( - $"Asset move failed: {exception.Message} " + - $"Rollback failed: {rollbackException.Message}"); + return StateIntegrityFailure($"Asset move failed: {exception.Message} " + $"Rollback failed: {rollbackException.Message}"); } } + return EditorHistoryResult.Failure(exception.Message); } } @@ -177,10 +168,9 @@ EditorHistoryDirection direction } catch (Exception rollbackException) { - return StateIntegrityFailure( - $"Asset folder operation failed: {exception.Message} " + - $"Rollback failed: {rollbackException.Message}"); + return StateIntegrityFailure($"Asset folder operation failed: {exception.Message} " + $"Rollback failed: {rollbackException.Message}"); } + return EditorHistoryResult.Failure(exception.Message); } } @@ -213,10 +203,9 @@ EditorHistoryDirection direction } catch (Exception rollbackException) { - return StateIntegrityFailure( - $"Asset delete operation failed: {exception.Message} " + - $"Rollback failed: {rollbackException.Message}"); + return StateIntegrityFailure($"Asset delete operation failed: {exception.Message} " + $"Rollback failed: {rollbackException.Message}"); } + return EditorHistoryResult.Failure(exception.Message); } } @@ -249,10 +238,9 @@ EditorHistoryDirection direction } catch (Exception rollbackException) { - return StateIntegrityFailure( - $"Asset creation history failed: {exception.Message} " + - $"Rollback failed: {rollbackException.Message}"); + return StateIntegrityFailure($"Asset creation history failed: {exception.Message} " + $"Rollback failed: {rollbackException.Message}"); } + return EditorHistoryResult.Failure(exception.Message); } } @@ -265,44 +253,40 @@ EditorHistoryDirection direction string target = direction == EditorHistoryDirection.Undo ? data.sourcePath : data.targetPath; if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(source), out _)) return EditorHistoryAvailability.Unavailable($"Asset '{source}' no longer exists."); - return !assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(target), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Asset '{target}' already exists."); + return !assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(target), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Asset '{target}' already exists."); } private EditorHistoryAvailability QueryCreateDirectory( AssetHistoryData data, EditorHistoryDirection direction - ) - => direction == EditorHistoryDirection.Undo - ? assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Folder '{data.sourcePath}' no longer exists.") - : !assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Folder '{data.sourcePath}' already exists."); - + ) => direction == EditorHistoryDirection.Undo ? assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Folder '{data.sourcePath}' no longer exists.") : !assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Folder '{data.sourcePath}' already exists."); private EditorHistoryAvailability QueryDelete( AssetHistoryData data, EditorHistoryDirection direction - ) - => direction == EditorHistoryDirection.Undo - ? !assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' already exists.") - : assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' no longer exists."); - + ) => direction == EditorHistoryDirection.Undo ? !assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' already exists.") : assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' no longer exists."); private EditorHistoryAvailability QueryCreateAsset( AssetHistoryData data, EditorHistoryDirection direction - ) - => direction == EditorHistoryDirection.Undo - ? assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' no longer exists.") - : !assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(data.sourcePath), out _) - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' already exists."); + ) => direction == EditorHistoryDirection.Undo ? assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' no longer exists.") : !assets.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(data.sourcePath), + out _ + ) ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable($"Asset '{data.sourcePath}' already exists."); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryKinds.cs b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryKinds.cs similarity index 74% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryKinds.cs rename to src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryKinds.cs index 3a83e080a..645f029df 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryKinds.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryKinds.cs @@ -1,5 +1,4 @@ -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal static class AssetHistoryKinds { internal const string SourceOperation = "inno.assets/source-operation"; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryOperationKind.cs b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryOperationKind.cs similarity index 75% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryOperationKind.cs rename to src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryOperationKind.cs index 90f971f5c..a9a478a7e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetHistoryOperationKind.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/History/AssetHistoryOperationKind.cs @@ -1,5 +1,4 @@ -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal enum AssetHistoryOperationKind : byte { Move = 1, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetSourceArchive.cs b/src/composition/editor/features/Inno.Editor.Assets/History/AssetSourceArchive.cs similarity index 91% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetSourceArchive.cs rename to src/composition/editor/features/Inno.Editor.Assets/History/AssetSourceArchive.cs index e671d8264..ea5a253ae 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/History/AssetSourceArchive.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/History/AssetSourceArchive.cs @@ -2,17 +2,14 @@ using System.IO; using System.IO.Compression; using System.Threading; - using Inno.Assets; using Inno.Assets.Pipeline; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; internal static class AssetSourceArchive { private const string C_SOURCE_ENTRY = "source"; private const string C_META_ENTRY = "source.imeta"; - internal static byte[] Capture( AssetPipeline assets, string relativePath, @@ -23,7 +20,6 @@ out bool isDirectory isDirectory = Directory.Exists(source); if (!isDirectory && !File.Exists(source)) throw new FileNotFoundException($"Asset source '{relativePath}' does not exist.", source); - return CapturePhysical(source, isDirectory, CancellationToken.None); } @@ -46,6 +42,7 @@ CancellationToken cancellationToken string local = Path.GetRelativePath(source, directories[i]).Replace('\\', '/'); _ = archive.CreateEntry($"{C_SOURCE_ENTRY}/{local}/"); } + string[] files = Directory.GetFiles(source, "*", SearchOption.AllDirectories); Array.Sort(files, StringComparer.Ordinal); for (int i = 0; i < files.Length; i++) @@ -63,6 +60,7 @@ CancellationToken cancellationToken if (File.Exists(meta)) WriteEntry(archive, meta, C_META_ENTRY, cancellationToken); } + return stream.ToArray(); } @@ -98,11 +96,7 @@ ReadOnlySpan data string? parent = Path.GetDirectoryName(target); if (!string.IsNullOrEmpty(parent)) Directory.CreateDirectory(parent); - - string stagingDirectory = Path.Combine( - Path.GetTempPath(), - "InnoAssetHistory", - Guid.NewGuid().ToString("N")); + string stagingDirectory = Path.Combine(Path.GetTempPath(), "InnoAssetHistory", Guid.NewGuid().ToString("N")); string stagingSource = Path.Combine(stagingDirectory, C_SOURCE_ENTRY); string stagingMeta = Path.Combine(stagingDirectory, C_META_ENTRY); bool sourceCommitted = false; @@ -117,11 +111,7 @@ ReadOnlySpan data for (int i = 0; i < archive.Entries.Count; i++) { ZipArchiveEntry entry = archive.Entries[i]; - string destination = ResolveDestination( - stagingSource, - stagingMeta, - isDirectory, - entry.FullName); + string destination = ResolveDestination(stagingSource, stagingMeta, isDirectory, entry.FullName); string fullDestination = Path.GetFullPath(destination); if (!fullDestination.StartsWith(stagingRoot, StringComparison.Ordinal)) throw new InvalidDataException("Asset history archive escapes its staging directory."); @@ -130,6 +120,7 @@ ReadOnlySpan data Directory.CreateDirectory(fullDestination); continue; } + string? destinationParent = Path.GetDirectoryName(fullDestination); if (!string.IsNullOrEmpty(destinationParent)) Directory.CreateDirectory(destinationParent); @@ -146,6 +137,7 @@ ReadOnlySpan data File.Move(stagingMeta, target + ".imeta"); metaCommitted = true; } + assets.Rescan(); assets.WaitForIdle(); } @@ -166,6 +158,7 @@ ReadOnlySpan data { } } + throw; } finally @@ -188,10 +181,9 @@ string entryName string prefix = C_SOURCE_ENTRY + "/"; if (isDirectory && entryName.StartsWith(prefix, StringComparison.Ordinal)) { - return Path.Combine( - stagingSource, - entryName[prefix.Length..].Replace('/', Path.DirectorySeparatorChar)); + return Path.Combine(stagingSource, entryName[prefix.Length..].Replace('/', Path.DirectorySeparatorChar)); } + throw new InvalidDataException($"Unknown asset history archive entry '{entryName}'."); } @@ -200,9 +192,7 @@ private static string GetSourcePath( string relativePath ) { string root = Path.GetFullPath(assets.assetRoot) + Path.DirectorySeparatorChar; - string result = Path.GetFullPath(Path.Combine( - assets.assetRoot, - relativePath.Replace('/', Path.DirectorySeparatorChar))); + string result = Path.GetFullPath(Path.Combine(assets.assetRoot, relativePath.Replace('/', Path.DirectorySeparatorChar))); if (!result.StartsWith(root, StringComparison.Ordinal)) throw new InvalidDataException($"Asset history path '{relativePath}' escapes the asset root."); return result; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs b/src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs similarity index 87% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs rename to src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs index 4084f55fe..28e73c575 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetImportSettingsEdits.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/ImportSettings/AssetImportSettingsEdits.cs @@ -7,8 +7,7 @@ using Inno.Editor.Interactions; using Inno.Extensibility.Types; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; /// /// Applies importer configuration through the common sidecar pipeline and stable-identity Editor history. /// @@ -76,8 +75,13 @@ string expectedFingerprint throw new InvalidOperationException("The importer settings type changed. Reload the editor."); if (!m_assets.TryGetPersistentId(path, out Guid id)) throw new InvalidOperationException("The source has no persistent identity."); - var data = new ChangeData { assetId = id, typeId = m_types.GetTypeRef(settings.GetType()).stableId, - before = Capture(before.value), after = Capture(settings) }; + var data = new ChangeData + { + assetId = id, + typeId = m_types.GetTypeRef(settings.GetType()).stableId, + before = Capture(before.value), + after = Capture(settings) + }; if (data.before.AsSpan().SequenceEqual(data.after)) return m_assets.TryGetInfo(id, out AssetInfo? info) && info?.status == AssetImportStatus.Imported; bool imported = m_assets.SaveImportSettings(path, settings, expectedFingerprint); @@ -97,8 +101,10 @@ string expectedFingerprint { throw new AggregateException(failure, rollback); } + throw; } + return imported; } @@ -144,15 +150,20 @@ EditorHistoryDirection direction return (info.assetPath, current); } - private byte[] Capture(ISerializable value) - => m_serialization.Encode(writer => writer.WriteProperties(value), AssetSerializationContext.Create(m_assets)); - + private byte[] Capture(ISerializable value) => m_serialization.Encode(writer => writer.WriteProperties(value), AssetSerializationContext.Create(m_assets)); private sealed class ChangeData : ISerializable { - [SerializableProperty] internal Guid assetId { get; set; } - [SerializableProperty] internal Guid typeId { get; set; } - [SerializableProperty] internal byte[] before { get; set; } = []; - [SerializableProperty] internal byte[] after { get; set; } = []; + [SerializableProperty] + internal Guid assetId { get; set; } + + [SerializableProperty] + internal Guid typeId { get; set; } + + [SerializableProperty] + internal byte[] before { get; set; } = []; + + [SerializableProperty] + internal byte[] after { get; set; } = []; } } @@ -174,21 +185,23 @@ internal sealed class AssetImportSettingsHistory(AssetImportSettingsEdits edits) /// /// The validated editor history availability that represents the completed operation. /// -protected override EditorHistoryAvailability Query( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryAvailability Query( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { edits.Validate(change, direction); return EditorHistoryAvailability.Available(); } - catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { return EditorHistoryAvailability.Unavailable(failure.Message); } } + /// /// Applies a validated change atomically at the caller-controlled commit point. /// @@ -204,17 +217,18 @@ EditorHistoryDirection direction /// /// The validated editor history result that represents the completed operation. /// -protected override EditorHistoryResult Apply( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryResult Apply( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { edits.ApplyHistory(change, direction); return EditorHistoryResult.Success(); } - catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { return EditorHistoryResult.Failure(failure.Message); } diff --git a/src/composition/editor/features/Inno.Editor.Assets/Inno.Editor.Assets.csproj b/src/composition/editor/features/Inno.Editor.Assets/Inno.Editor.Assets.csproj index a77201c8c..0b740982a 100644 --- a/src/composition/editor/features/Inno.Editor.Assets/Inno.Editor.Assets.csproj +++ b/src/composition/editor/features/Inno.Editor.Assets/Inno.Editor.Assets.csproj @@ -10,24 +10,22 @@ + - - - - + + + + + + + + - + - diff --git a/src/composition/editor/features/Inno.Editor.Assets/Properties/ScriptingApi.cs b/src/composition/editor/features/Inno.Editor.Assets/Properties/ScriptingApi.cs index 1aba477cf..b628355d7 100644 --- a/src/composition/editor/features/Inno.Editor.Assets/Properties/ScriptingApi.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Properties/ScriptingApi.cs @@ -1,12 +1,15 @@ using Inno.Editor.Assets; +using Inno.Assets.Pipeline; using Inno.Scripting.Api; -[assembly: ScriptingApiNamespace( - "InnoEditor.Assets", - "Inno.Editor.Assets", - ScriptingApiScope.Editor)] - +[assembly: ScriptingApiNamespace("InnoEditor.Assets", "Inno.Editor.Assets", ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetCreationMenuAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetCreationContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetCreationTemplate), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetCreationTemplate<>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetEditor), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetFileEntry), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetEditorAttribute), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetEditorContext), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetOperationValidation), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetImportSettingsEdits), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModule.cs b/src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs similarity index 68% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModule.cs rename to src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs index 77f34c6b6..71c197a5d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModule.cs +++ b/src/composition/editor/features/Inno.Editor.Assets/Samples/AssetSampleImportModule.cs @@ -1,8 +1,8 @@ +using Inno.Editor.Assets; using System; using System.IO; using System.Linq; using System.Threading.Tasks; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Core.Execution; @@ -12,13 +12,15 @@ using Inno.Editor.Scripting; using Inno.Scripting.Compiler; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets; +/// +/// Owns sample import validation, publication and rollback independently of its progress window. +/// [EditorModule("asset-sample-import", order: 110)] -internal sealed class AssetSampleImportModule : EditorModule, IProgress +public sealed class AssetSampleImportModule : EditorModule, IProgress { private readonly object m_progressLock = new(); - private readonly AssetEditorModule m_assets; + private readonly AssetEditingModule m_assets; private readonly ScriptCompiler m_compiler; private readonly IEditorScriptCompilation m_scripting; private readonly Logger m_log; @@ -30,9 +32,8 @@ internal sealed class AssetSampleImportModule : EditorModule, IProgress public override bool blocksFollowingUpdates => isBusy || m_faulted; + /// + /// Gets whether an import still owns validation or publication work. + /// + public bool isBusy => !m_faulted && m_import is not null; + /// + /// Gets whether the current authoring generation admits a new sample import. + /// + public bool canImport => !m_faulted && !isBusy && m_scripting.state is EditorScriptCompilationState.Ready or EditorScriptCompilationState.Failed; - internal bool isBusy => !m_faulted && m_import is not null; - internal bool canImport => !m_faulted && !isBusy && m_scripting.state is EditorScriptCompilationState.Ready or EditorScriptCompilationState.Failed; - internal string status { get { lock (m_progressLock) return m_status; } } - internal float progress { get { lock (m_progressLock) return m_progress; } } + /// + /// Gets an immutable progress message captured under the progress owner lock. + /// + public string status + { + get + { + lock (m_progressLock) + return m_status; + } + } - internal void Request(AssetPath source) + /// + /// Gets the most recent validation progress fraction. + /// + public float progress + { + get + { + lock (m_progressLock) + return m_progress; + } + } + + /// + /// Begins validating an installed sample before its source copy can be committed. + /// + /// + /// The installed sample root to validate and import. + /// + /// + /// The authoring generation is busy or faulted. + /// + public void Request(AssetPath source) { if (!canImport) throw new InvalidOperationException("Sample import requires an idle authoring generation."); @@ -62,7 +99,10 @@ internal void Request(AssetPath source) SetProgress(0f, "Preparing sample assets..."); } - internal void Cancel() + /// + /// Requests cancellation without releasing work that has not retired. + /// + public void Cancel() { m_canceled = true; m_import?.Cancel(); @@ -75,9 +115,7 @@ internal void Cancel() /// /// The compiler stage and completion fraction. /// - public void Report(ScriptCompilationProgress value) - => SetProgress(0.2f + value.fraction * 0.6f, value.stage); - + public void Report(ScriptCompilationProgress value) => SetProgress(0.2f + value.fraction * 0.6f, value.stage); /// /// Advances completed background phases and publishes the validated import at a frame safe point. /// @@ -95,6 +133,7 @@ protected override void OnUpdate(EditorContext context) RetireImport(); return; } + AssetSampleImportTransaction import = m_import!; if (!m_validating) { @@ -109,34 +148,29 @@ protected override void OnUpdate(EditorContext context) cancellationToken ) => { - ScriptCompilationResult result = await m_compiler.CompileAuthoringGenerationAsync( - this, cancellationToken, sources).ConfigureAwait(false); + ScriptCompilationResult result = await m_compiler.CompileAuthoringGenerationAsync(this, cancellationToken, sources).ConfigureAwait(false); if (!result.success) - throw new InvalidOperationException("Sample script preflight failed:" + Environment.NewLine - + string.Join(Environment.NewLine, - result.diagnostics.Select(static diagnostic => diagnostic.message))); + throw new InvalidOperationException("Sample script preflight failed:" + Environment.NewLine + string.Join(Environment.NewLine, result.diagnostics.Select(static diagnostic => diagnostic.message))); SetProgress(0.85f, "Preparing sample history..."); m_historyPayload = await Task.Run(() => { - byte[] archive = AssetSourceArchive.CapturePhysical( - physicalSource, isDirectory: true, cancellationToken); - var data = new AssetHistoryData( - AssetHistoryOperationKind.CreateAsset, targetPath, string.Empty, - isDirectory: true, archive); + byte[] archive = AssetSourceArchive.CapturePhysical(physicalSource, isDirectory: true, cancellationToken); + var data = new AssetHistoryData(AssetHistoryOperationKind.CreateAsset, targetPath, string.Empty, isDirectory: true, archive); cancellationToken.ThrowIfCancellationRequested(); return EditorHistoryPayload.FromBytes(data.Encode()); }, cancellationToken).ConfigureAwait(false); }); return; } + if (!import.isValidationComplete) return; import.Commit(_ => { m_assets.interactions.history.RecordApplied( - "Import Sample", new EditorHistoryChange( - AssetHistoryKinds.SourceOperation, - m_historyPayload ?? throw new InvalidOperationException("Validated sample history is missing."))); + "Import Sample", + new EditorHistoryChange(AssetHistoryKinds.SourceOperation, m_historyPayload ?? throw new InvalidOperationException("Validated sample history is missing.")) + ); }); m_log.Write(LogLevel.Info, "Imported sample to '{0}'.", [import.target]); m_assets.SelectPath(import.target.ToString()); @@ -146,7 +180,7 @@ protected override void OnUpdate(EditorContext context) { if (m_import?.isFaulted == true) ReportFault(pending); - // The next frame retries retirement while the modal and dependency ownership remain intact. + // The next frame retries retirement while the modal and dependency ownership remain intact. } catch (Exception failure) { @@ -173,7 +207,6 @@ protected override void OnStop(EditorContext context) /// Retries retirement without releasing dependencies still used by background work. /// protected override void OnDispose() => RetireImport(); - private void RetireImport() { m_import?.Rollback(); diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Inno.Editor.Exporting.csproj b/src/composition/editor/features/Inno.Editor.Exporting/Inno.Editor.Exporting.csproj index 20fd4466e..82ee91974 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Inno.Editor.Exporting.csproj +++ b/src/composition/editor/features/Inno.Editor.Exporting/Inno.Editor.Exporting.csproj @@ -4,35 +4,21 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - - - - - - - - + + + + +
diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Runtime/ExportWindowModule.cs b/src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs similarity index 50% rename from src/composition/editor/features/Inno.Editor.Exporting/Runtime/ExportWindowModule.cs rename to src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs index 670e0688b..bcfdede72 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Runtime/ExportWindowModule.cs +++ b/src/composition/editor/features/Inno.Editor.Exporting/Runtime/EditorExportService.cs @@ -13,14 +13,13 @@ using Inno.Editor.Core; namespace Inno.Editor.Exporting; - +/// +/// Owns asynchronous export, progress, cancellation and retirement independently from presentation. +/// [EditorModule("exporting", order: 300)] -internal sealed class ExportWindowModule : EditorModule +public sealed class EditorExportService : EditorModule { - private readonly EditorContext m_editor; private readonly BuildPipeline m_buildPipeline; - private readonly BuildSettingsStore m_buildSettings; - private readonly ProjectSettingsStore m_projectSettings; private readonly Logger m_log; private readonly ConcurrentQueue m_progress = new(); private CancellationTokenSource? m_cancellation; @@ -28,164 +27,103 @@ internal sealed class ExportWindowModule : EditorModule private Task? m_gameExport; private Task? m_pluginExport; private GameBuildRequest? m_pendingGameRequest; - - internal ExportWindowModule( - EditorContext editor, + internal EditorExportService( BuildPipeline buildPipeline, - BuildSettingsStore buildSettings, - ProjectSettingsStore projectSettings, LogRouter logs ) { - m_editor = editor ?? throw new ArgumentNullException(nameof(editor)); m_buildPipeline = buildPipeline ?? throw new ArgumentNullException(nameof(buildPipeline)); - m_buildSettings = buildSettings ?? throw new ArgumentNullException(nameof(buildSettings)); - m_projectSettings = projectSettings ?? throw new ArgumentNullException(nameof(projectSettings)); ArgumentNullException.ThrowIfNull(logs); - m_log = logs.CreateLogger(); - } - - internal bool isPluginVisible { get; private set; } - - internal bool isGameVisible { get; private set; } - - internal bool isGameProgressVisible { get; private set; } - - internal bool isPluginBusy => m_pluginExport is not null; - - internal bool isGameBusy => m_gamePreparation is not null || m_gameExport is not null; - - internal string pluginId => m_projectSettings.projectId.value; - - internal string pluginDisplayName { get; set; } = string.Empty; - - internal string pluginOutputPath { get; set; } = string.Empty; - - internal string gameApplicationId => m_projectSettings.projectId.value; - - internal string gameProductName { get; set; } = string.Empty; - - internal string gamePersistentDataPath { get; set; } = string.Empty; - - internal string gameStartupScene { get; set; } = string.Empty; - - internal string gameOutputDirectory { get; set; } = string.Empty; - - internal int gameWindowWidth { get; set; } = 1280; - - internal int gameWindowHeight { get; set; } = 720; - - internal BuildTargetId gameTarget { get; set; } - - internal ManagedDeploymentId? gameManagedDeployment { get; set; } - - internal IReadOnlyList availableManagedDeployments => m_buildPipeline.GetManagedDeployments(gameTarget); - - internal IReadOnlyList availableGameTargets => m_buildPipeline.availableGameTargets; - - internal string status { get; private set; } = string.Empty; - - internal string error { get; private set; } = string.Empty; - - internal float gameProgress { get; private set; } - - internal bool includePluginDependencies { get; set; } - - internal string GetGameTargetDisplayName(BuildTargetId target) - => m_buildPipeline.TryGetGameTargetDisplayName(target, out string displayName) - ? displayName - : $"Missing ({target})"; - - internal void OpenPlugin() - { - if (isPluginBusy || isGameBusy) - return; - CloseGame(); - status = string.Empty; - error = string.Empty; - BuildSettings defaults = LoadBuildSettings(); - pluginDisplayName = defaults.pluginDisplayName; - pluginOutputPath = defaults.pluginOutputPath; - includePluginDependencies = defaults.includePluginDependencies; - isPluginVisible = true; + m_log = logs.CreateLogger(); } - internal void OpenGame() - { - if (isPluginBusy || isGameBusy) - return; - ClosePlugin(); - isGameProgressVisible = false; - status = string.Empty; - error = string.Empty; - BuildSettings defaults = LoadBuildSettings(); - gameProductName = defaults.gameProductName; - gamePersistentDataPath = defaults.gamePersistentDataPath; - gameStartupScene = defaults.gameStartupScene; - gameOutputDirectory = defaults.gameOutputDirectory; - gameWindowWidth = defaults.gameWindowWidth; - gameWindowHeight = defaults.gameWindowHeight; - gameTarget = defaults.gameTarget; - gameManagedDeployment = defaults.gameManagedDeployment; - isGameVisible = true; - } + /// + /// Gets whether a Plugin source export still owns build resources. + /// + public bool isPluginBusy => m_pluginExport is not null; + /// + /// Gets whether a game export or its Support Pack preparation is active. + /// + public bool isGameBusy => m_gamePreparation is not null || m_gameExport is not null; + /// + /// Gets the latest owner-thread progress message. + /// + public string status { get; private set; } = string.Empty; + /// + /// Gets the last failure or cancellation diagnostic, or an empty string. + /// + public string error { get; private set; } = string.Empty; + /// + /// Gets monotonic progress for the current game build, from zero through one. + /// + public float gameProgress { get; private set; } + /// + /// Gets the targets registered by the active Build distribution. + /// + public IReadOnlyList availableGameTargets => m_buildPipeline.availableGameTargets; - internal void BeginPluginExport() + /// + /// Gets deployment implementations registered for a target. + /// + /// + /// The explicitly chosen build target. + /// + /// + /// The registered deployment choices; no host fallback is introduced. + /// + public IReadOnlyList GetManagedDeployments(BuildTargetId target) => m_buildPipeline.GetManagedDeployments(target); + /// + /// Gets a registered target label or a diagnostic for an unavailable target. + /// + /// + /// The target to describe. + /// + /// + /// A presentation label; never used as a routing identity. + /// + public string GetGameTargetDisplayName(BuildTargetId target) => m_buildPipeline.TryGetGameTargetDisplayName( + target, + out string displayName + ) ? displayName : $"Missing ({target})"; + /// + /// Starts a source-package export using the existing Build pipeline. + /// + /// + /// The complete, explicitly selected package request. + /// + /// + /// Another export still owns the service. + /// + public void StartPlugin(PluginBuildRequest request) { - if (isPluginBusy || isGameBusy) - return; + ArgumentNullException.ThrowIfNull(request); + EnsureIdle(); StartCancellation(); status = "Capturing the current Plugin source and dependency generation..."; error = string.Empty; - m_pluginExport = m_buildPipeline.BuildPluginAsync( - new PluginBuildRequest - { - pluginId = pluginId, - displayName = pluginDisplayName, - outputPath = ResolveOutputPath(pluginOutputPath), - includeDependencies = includePluginDependencies - }, - new BuildProgressSink(m_progress), - m_cancellation!.Token).AsTask(); + m_pluginExport = m_buildPipeline.BuildPluginAsync(request, new BuildProgressSink(m_progress), m_cancellation!.Token).AsTask(); } - internal void CancelPluginExport() - { - if (!isPluginBusy) - return; - status = "Canceling Plugin export..."; - m_cancellation?.Cancel(); - } - - internal void BeginGameExport() + /// + /// Prepares the selected Support Pack before capturing and building the game. + /// + /// + /// The complete game request; no form or window state is retained. + /// + /// + /// Another export still owns the service. + /// + public void StartGame(GameBuildRequest request) { - if (isPluginBusy || isGameBusy) - return; - var profile = new BuildProfile - { - applicationId = gameApplicationId, - productName = gameProductName, - persistentDataPath = gamePersistentDataPath, - startupScene = gameStartupScene, - target = gameTarget, - managedDeployment = gameManagedDeployment, - windowWidth = gameWindowWidth, - windowHeight = gameWindowHeight - }; + ArgumentNullException.ThrowIfNull(request); + EnsureIdle(); StartCancellation(); - isGameVisible = false; - isGameProgressVisible = true; gameProgress = 0f; status = "Checking or preparing the Player Support Pack..."; error = string.Empty; + m_pendingGameRequest = request; try { - m_pendingGameRequest = new GameBuildRequest - { - profile = profile, - outputDirectory = ResolveOutputPath(gameOutputDirectory) - }; - m_gamePreparation = m_buildPipeline.EnsurePlayerSupportPackAsync( - profile.target, profile.managedDeployment, m_cancellation!.Token).AsTask(); + m_gamePreparation = m_buildPipeline.EnsurePlayerSupportPackAsync(request.profile.target, request.profile.managedDeployment, m_cancellation!.Token).AsTask(); } catch (Exception exception) { @@ -193,34 +131,21 @@ internal void BeginGameExport() } } - internal void CancelGameExport() - { - status = "Canceling game build..."; - m_cancellation?.Cancel(); - } - - internal void ClosePlugin() + /// + /// Requests cancellation without releasing resources before their owned work drains. + /// + public void Cancel() { - if (isPluginBusy) - CancelPluginExport(); - isPluginVisible = false; - if (!isPluginBusy) - { - status = string.Empty; - error = string.Empty; - } + if (m_cancellation is null) + return; + status = "Canceling export..."; + m_cancellation.Cancel(); } - internal void CloseGame() + private void EnsureIdle() { - if (isGameBusy) - CancelGameExport(); - isGameVisible = false; - if (!isGameBusy) - { - status = string.Empty; - error = string.Empty; - } + if (isPluginBusy || isGameBusy) + throw new InvalidOperationException("An export still owns the build service."); } /// @@ -234,9 +159,10 @@ protected override void OnUpdate(EditorContext context) while (m_progress.TryDequeue(out BuildProgress progress)) { status = progress.message; - if (isGameProgressVisible) + if (isGameBusy) gameProgress = MathF.Max(gameProgress, (float)progress.fraction); } + CompletePluginExport(); CompleteGamePreparation(); CompleteGameExport(); @@ -250,9 +176,6 @@ protected override void OnUpdate(EditorContext context) /// protected override void OnStop(EditorContext context) { - isPluginVisible = false; - isGameVisible = false; - isGameProgressVisible = false; RetireExports(); } @@ -267,16 +190,14 @@ protected override void OnDispose() private void RetireExports() { m_cancellation?.Cancel(); - if (m_gamePreparation is { IsCompleted: false } - || m_gameExport is { IsCompleted: false } - || m_pluginExport is { IsCompleted: false }) + if (m_gamePreparation is { IsCompleted: false } || m_gameExport is { IsCompleted: false } || m_pluginExport is { IsCompleted: false }) { throw new RetirementPendingException("Canceled export work still owns build services; retry retirement after it drains."); } + RequireRetiredOutcome(m_gamePreparation); RequireRetiredOutcome(m_gameExport); RequireRetiredOutcome(m_pluginExport); - CompletePluginExport(); CompleteGameExport(); Task? preparation = m_gamePreparation; @@ -290,14 +211,18 @@ private void RetireExports() } catch (OperationCanceledException) { - // Cancellation during shutdown is expected; preparation must never start a new build here. + // Cancellation during shutdown is expected; preparation must never start a new build here. } catch (Exception exception) { m_log.Write(LogLevel.Error, "Game Support Pack preparation failed during shutdown: {0}", [exception]); } } - while (m_progress.TryDequeue(out _)) { } + + while (m_progress.TryDequeue(out _)) + { + } + ReleaseCancellation(); } @@ -321,16 +246,16 @@ private void CompletePluginExport() ReportBuildFailure("Plugin export", result); return; } - string outputPath = result.outputPath - ?? throw new InvalidOperationException("A successful Plugin build has no output path."); - string contentHash = result.contentHash - ?? throw new InvalidOperationException("A successful Plugin build has no content identity."); + + string outputPath = result.outputPath ?? throw new InvalidOperationException("A successful Plugin build has no output path."); + string contentHash = result.contentHash ?? throw new InvalidOperationException("A successful Plugin build has no content identity."); status = $"Exported {result.assetCount} source files to {outputPath}"; error = string.Empty; m_log.Write( LogLevel.Info, "Exported Plugin '{0}' to '{1}' ({2} assets, {3} embedded dependencies, {4}).", - [pluginId, outputPath, result.assetCount, result.embeddedPluginCount, contentHash]); + ["Project", outputPath, result.assetCount, result.embeddedPluginCount, contentHash] + ); } catch (OperationCanceledException) { @@ -364,21 +289,16 @@ private void CompleteGameExport() ReportBuildFailure("Game build", result); return; } - string outputPath = result.outputPath - ?? throw new InvalidOperationException("A successful game build has no output path."); + + string outputPath = result.outputPath ?? throw new InvalidOperationException("A successful game build has no output path."); status = $"Exported {result.assetCount} assets and {result.runtimeAssemblyCount} runtime assemblies to {outputPath}"; error = string.Empty; gameProgress = 1f; m_log.Write( LogLevel.Info, "Exported game to '{0}' ({1}, {2} assets, {3} artifact bundles, {4} runtime assemblies).", - [ - outputPath, - result.target?.ToString() ?? "unknown", - result.assetCount, - result.artifactBundleCount, - result.runtimeAssemblyCount - ]); + [outputPath, result.target?.ToString() ?? "unknown", result.assetCount, result.artifactBundleCount, result.runtimeAssemblyCount] + ); } catch (OperationCanceledException) { @@ -394,7 +314,6 @@ private void CompleteGameExport() } finally { - isGameProgressVisible = false; ReleaseCancellation(); } } @@ -409,12 +328,10 @@ private void CompleteGamePreparation() { using PlayerSupportPackLease prepared = preparation.GetAwaiter().GetResult(); m_cancellation!.Token.ThrowIfCancellationRequested(); - GameBuildRequest request = m_pendingGameRequest - ?? throw new InvalidOperationException("The prepared game build has no request."); + GameBuildRequest request = m_pendingGameRequest ?? throw new InvalidOperationException("The prepared game build has no request."); m_pendingGameRequest = null; status = "Capturing the authoring generation and compiling runtime scripts..."; - m_gameExport = m_buildPipeline.BuildGameAsync( - request, new BuildProgressSink(m_progress), m_cancellation.Token).AsTask(); + m_gameExport = m_buildPipeline.BuildGameAsync(request, new BuildProgressSink(m_progress), m_cancellation.Token).AsTask(); } catch (OperationCanceledException) { @@ -422,7 +339,6 @@ private void CompleteGamePreparation() status = string.Empty; error = "Game build was canceled; no partial output was installed."; m_log.Write(LogLevel.Info, "Game build was canceled while preparing its Support Pack."); - isGameProgressVisible = false; ReleaseCancellation(); } catch (Exception exception) @@ -431,36 +347,10 @@ private void CompleteGamePreparation() status = string.Empty; error = exception.Message; m_log.Write(LogLevel.Error, "Game Support Pack preparation failed: {0}", [exception]); - isGameProgressVisible = false; ReleaseCancellation(); } } - private BuildSettings LoadBuildSettings() - { - try - { - return m_buildSettings.Load(); - } - catch (Exception exception) when (exception is IOException - or InvalidDataException - or InvalidOperationException - or UnauthorizedAccessException) - { - error = exception.Message; - return m_buildSettings.defaultSettings; - } - } - - private string ResolveOutputPath(string path) - { - if (string.IsNullOrWhiteSpace(path)) - return path; - return Path.IsPathFullyQualified(path) - ? Path.GetFullPath(path) - : Path.GetFullPath(path, m_editor.projectDirectory); - } - private void StartCancellation() { m_cancellation?.Dispose(); @@ -477,15 +367,10 @@ private void ReportBuildFailure( string operation, BuildResult result ) { - string message = string.Join( - Environment.NewLine, - result.diagnostics.Select(static diagnostic => $"[{diagnostic.code}] {diagnostic.message}")); + string message = string.Join(Environment.NewLine, result.diagnostics.Select(static diagnostic => $"[{diagnostic.code}] {diagnostic.message}")); status = string.Empty; error = message; - m_log.Write( - LogLevel.Error, - "{0} failed:{1}{2}", - [operation, Environment.NewLine, message]); + m_log.Write(LogLevel.Error, "{0} failed:{1}{2}", [operation, Environment.NewLine, message]); } private sealed class BuildProgressSink(ConcurrentQueue progress) : IProgress diff --git a/src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs b/src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs index 8ea232978..28127edb0 100644 --- a/src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs +++ b/src/composition/editor/features/Inno.Editor.PlayMode/IEditorPlayMode.cs @@ -30,9 +30,18 @@ public interface IEditorPlayMode LogSessionId activeSessionId { get; } /// - /// Occurs after changes. + /// Subscribes to committed state transitions through the Core event lifetime. /// - event Action? stateChanged; + /// + /// The observer invoked synchronously after a transition. + /// + /// + /// An independently disposable subscription; dispose it before retiring the observer. + /// + /// + /// The listener is null. + /// + IDisposable ListenStateChanges(Action listener); /// /// Requests entry after the active script generation becomes ready. diff --git a/src/composition/editor/features/Inno.Editor.PlayMode/Inno.Editor.PlayMode.csproj b/src/composition/editor/features/Inno.Editor.PlayMode/Inno.Editor.PlayMode.csproj index 4497d4f6e..b56a0b3ba 100644 --- a/src/composition/editor/features/Inno.Editor.PlayMode/Inno.Editor.PlayMode.csproj +++ b/src/composition/editor/features/Inno.Editor.PlayMode/Inno.Editor.PlayMode.csproj @@ -10,28 +10,15 @@ - - - - - + + + + + + - - + + diff --git a/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs b/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs index 661d0d31d..1d9ad489e 100644 --- a/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs +++ b/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeController.cs @@ -2,6 +2,7 @@ using Inno.Core.Execution; using Inno.Extensibility.Reload; using System; +using Inno.Core.Events; using System.Collections.Generic; using Inno.Core.Logging; @@ -30,6 +31,8 @@ public sealed class EditorPlayModeController : IEditorReloadGuard, IDisposable { + private readonly EventDispatcher m_stateDispatcher = new(); + private readonly EventHub m_stateEvents; private readonly IEditorHistoryIsolation m_history; private readonly IEditorAudioHost? m_audio; private readonly EngineHost m_engineHost; @@ -104,6 +107,7 @@ public EditorPlayModeController( m_audio = audio; ArgumentNullException.ThrowIfNull(logs); m_log = logs.CreateLogger(); + m_stateEvents = m_stateDispatcher.CreateHub(); } /// @@ -138,10 +142,23 @@ public EditorPlayModeController( /// public LogSessionId activeSessionId => m_activeSessionId; - /// - /// Occurs after the controller commits a state transition. - /// - public event Action? stateChanged; + /// + public IDisposable ListenStateChanges(Action listener) + { + ObjectDisposedException.ThrowIf(m_disposed, this); + ArgumentNullException.ThrowIfNull(listener); + return m_stateEvents.Listen(notification => + { + try + { + listener(notification.state); + } + catch (Exception failure) + { + notification.failures.Add(failure); + } + }); + } /// /// Requests a fresh script generation before preparing an isolated runtime session. @@ -262,6 +279,7 @@ public void Dispose() m_engineHost.generations.Fault(failure); throw; } + m_stateEvents.Dispose(); m_disposed = true; } @@ -497,24 +515,16 @@ private void SetState(EditorPlayModeState value) if (m_state == value) return; m_state = value; - Action? handlers = stateChanged; - if (handlers is null) - return; - List? failures = null; - foreach (Action handler in handlers.GetInvocationList()) - { - try - { - handler(value); - } - catch (Exception exception) - { - failures ??= []; - failures.Add(exception); - } - } - if (failures is not null) - throw new AggregateException("One or more Play Mode state observers failed.", failures); + var notification = new StateTransitionEvent(value); + m_stateDispatcher.Emit(notification); + if (notification.failures.Count != 0) + throw new AggregateException("One or more Play Mode state observers failed.", notification.failures); + } + + private sealed class StateTransitionEvent(EditorPlayModeState state) : Event + { + internal EditorPlayModeState state { get; } = state; + internal List failures { get; } = []; } private static void DisposeResource( diff --git a/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeModule.cs b/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeModule.cs index 9825c3d60..500a3fe98 100644 --- a/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeModule.cs +++ b/src/composition/editor/features/Inno.Editor.PlayMode/Runtime/EditorPlayModeModule.cs @@ -67,14 +67,8 @@ public override bool blocksFollowingUpdates /// public LogSessionId activeSessionId => m_controller.activeSessionId; - /// - /// Occurs after the Play Mode transition state changes. - /// - public event Action? stateChanged - { - add => m_controller.stateChanged += value; - remove => m_controller.stateChanged -= value; - } + /// + public IDisposable ListenStateChanges(Action listener) => m_controller.ListenStateChanges(listener); /// /// Requests entry after the active script generation becomes ready. diff --git a/src/composition/editor/features/Inno.Editor.Rendering/Inno.Editor.Rendering.csproj b/src/composition/editor/features/Inno.Editor.Rendering/Inno.Editor.Rendering.csproj index 2d275907a..802541231 100644 --- a/src/composition/editor/features/Inno.Editor.Rendering/Inno.Editor.Rendering.csproj +++ b/src/composition/editor/features/Inno.Editor.Rendering/Inno.Editor.Rendering.csproj @@ -10,44 +10,28 @@ - - - - - - - + + - - - - - - - - + + - - - + + + + + + + + + + - diff --git a/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs b/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs index c99dd9667..55c7eb5e6 100644 --- a/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs +++ b/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineDocuments.cs @@ -11,7 +11,6 @@ using Inno.Rendering.Assets; namespace Inno.Editor.Rendering; - /// /// Edits Pipeline sources and typed extension settings without changing canonical rendering assets before Save. /// @@ -20,9 +19,23 @@ public sealed class PipelineDocuments : EditorModule { internal const string C_HISTORY = "inno.pipeline/draft"; private readonly TypeCatalog m_types; - internal readonly AssetPipeline assets; - internal readonly SerializationRegistry serialization; - internal readonly AssetDraftDocuments drafts; + /// + /// Gets the authoritative asset pipeline used by source inspectors. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetPipeline assets { get; } + + /// + /// Gets the current owner serialization registry. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public SerializationRegistry serialization { get; } + + /// + /// Gets the source draft owner shared by Pipeline inspectors. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetDraftDocuments drafts { get; } internal PipelineDocuments( AssetPipeline assets, @@ -30,7 +43,9 @@ internal PipelineDocuments( TypeCatalog types, EditorInteractions interactions ) { - this.assets = assets; this.serialization = serialization; m_types = types; + this.assets = assets; + this.serialization = serialization; + m_types = types; drafts = new(assets, serialization, interactions, "inno.pipeline", C_HISTORY, ".irenderpipeline", "Pipeline"); } @@ -44,7 +59,6 @@ EditorInteractions interactions /// The persistent source identity used by draft operations. /// public Guid Open(AssetPath path) => drafts.Open(path); - /// /// Reads a detached current-generation Pipeline value; its referenced canonical assets must not be mutated. /// @@ -55,7 +69,6 @@ EditorInteractions interactions /// The unsaved native Pipeline value. /// public RenderPipelineAsset Read(Guid assetId) => drafts.Read(assetId); - /// /// Changes a draft through shared History without publishing it to Scene/Game. /// @@ -65,24 +78,14 @@ EditorInteractions interactions /// /// Detached edited value. /// - /// - /// Whether this sample completes the gesture. + /// + /// The current gesture key, or null for a discrete edit. /// public void Replace( Guid assetId, RenderPipelineAsset candidate, - bool finishGesture = true - ) - => drafts.Replace(assetId, candidate, finishGesture); - - /// - /// Completes one gesture without saving the source. - /// - /// - /// Open Pipeline identity. - /// - public void Commit(Guid assetId) => drafts.Commit(assetId); - + string? mergeKey = null + ) => drafts.Replace(assetId, candidate, mergeKey); /// /// Captures typed Pipeline settings with this source owner's complete reference and dependency context. /// @@ -95,22 +98,32 @@ public void Replace( /// /// Detached edited settings. /// - /// - /// Whether this sample completes the gesture. + /// + /// The current gesture key, or null for a discrete edit. /// public void ReplaceSettings( Guid assetId, TSettings settings, - bool finishGesture = true + string? mergeKey = null ) where TSettings : class, ISerializable { RenderPipelineAsset pipeline = Read(assetId); pipeline.pipelineState = new SerializedRenderExtensionState(assets.CaptureProperties(settings)); - Replace(assetId, pipeline, finishGesture); + Replace(assetId, pipeline, mergeKey); } - internal ISerializable? RestoreSettings(SerializedRenderExtensionState state) + /// + /// Restores detached settings using the current type and serialization generation. + /// + /// + /// Neutral settings retaining their stable type identity and properties. + /// + /// + /// The detached settings, or null when the extension is temporarily unavailable. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public ISerializable? RestoreSettings(SerializedRenderExtensionState state) { if (state.stableTypeId == Guid.Empty) return null; @@ -123,7 +136,6 @@ public void ReplaceSettings( internal static bool Recoverable(Exception error) => error is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException or FormatException && Inno.Core.Execution.RetirementPendingException.Find(error) is null; - /// /// Initializes this feature when its owning runtime becomes active. /// @@ -165,11 +177,11 @@ internal sealed class PipelineDraftHistory(PipelineDocuments documents) : Editor /// /// The validated editor history availability that represents the completed operation. /// -protected override EditorHistoryAvailability Query( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryAvailability Query( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { documents.drafts.ValidateHistory(change, direction); @@ -180,6 +192,7 @@ EditorHistoryDirection direction return EditorHistoryAvailability.Unavailable(error.Message); } } + /// /// Applies a validated change atomically at the caller-controlled commit point. /// @@ -195,11 +208,11 @@ EditorHistoryDirection direction /// /// The validated editor history result that represents the completed operation. /// -protected override EditorHistoryResult Apply( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryResult Apply( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { documents.drafts.ApplyHistory(change, direction); @@ -210,4 +223,11 @@ EditorHistoryDirection direction return EditorHistoryResult.Failure(error.Message); } } + + /// + protected override bool TryMerge( + EditorHistoryChange older, + EditorHistoryChange newer, + out EditorHistoryChange? merged + ) => documents.drafts.TryMergeHistory(older, newer, out merged); } diff --git a/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.PlayMode.cs b/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.PlayMode.cs index cb6512da9..412083e54 100644 --- a/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.PlayMode.cs +++ b/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.PlayMode.cs @@ -57,12 +57,28 @@ IDisposable IEditorScenePlayMode.BeginPlayMode(RuntimeSession runtimeSession) runtimeSession, snapshots, activeSceneId, - selectedSceneObjectId); - MaterializeRuntimeSceneSet(session); - m_playModeSession = session; - if (selectedSceneObjectId is not null) - RestoreSelection(selectedSceneObjectId); - return session; + selectedSceneObjectId, + m_selection?.RegisterIdentityDomain(runtimeSession.identities)); + try + { + MaterializeRuntimeSceneSet(session); + m_playModeSession = session; + if (selectedSceneObjectId is not null) + RestoreSelection(selectedSceneObjectId); + return session; + } + catch (Exception startupFailure) + { + try + { + session.Dispose(); + } + catch (Exception cleanupFailure) + { + throw new AggregateException("Play scene preparation and identity routing retirement failed.", startupFailure, cleanupFailure); + } + throw; + } } finally { @@ -83,8 +99,16 @@ private void ReleasePlayModeLease(PlayModeLease session) m_runtimeSession.scenes.Find(currentId) is not null ? currentId : session.editSelectionId; - if (runtimeSelection is EngineObject || runtimeSelection is null && session.editSelectionId is not null) - RestoreSelection(editSelectionId, selectActiveSceneWhenMissing: false); + try + { + if (runtimeSelection is EngineObject || runtimeSelection is null && session.editSelectionId is not null) + RestoreSelection(editSelectionId, selectActiveSceneWhenMissing: false); + } + catch + { + m_playModeSession = session; + throw; + } } private void MaterializeRuntimeSceneSet(PlayModeLease session) @@ -137,7 +161,8 @@ private sealed class PlayModeLease( RuntimeSession playSession, SceneDocumentSnapshot[] sceneSnapshots, Guid? activeScene, - Guid? selectedSceneObject + Guid? selectedSceneObject, + IDisposable? identityDomain ) : IDisposable { private readonly Dictionary m_snapshotBySceneId = @@ -162,8 +187,9 @@ public void Dispose() { if (m_disposed) return; - m_disposed = true; workspace.ReleasePlayModeLease(this); + identityDomain?.Dispose(); + m_disposed = true; } internal void Capture(EditorState state) diff --git a/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs b/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs index 264d385a9..1806ec590 100644 --- a/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs +++ b/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Elements.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Runtime.ExceptionServices; using System.Linq; - using Inno.Extensibility.Types; using Inno.Core.Serialization; using Inno.Editor.Core; @@ -12,7 +11,6 @@ using Inno.Scene.Layers; namespace Inno.Editor.Scene; - sealed partial class SceneEdits { /// @@ -43,10 +41,7 @@ public GameComponent AddComponent( try { TypeRef typeRef = GetTypeRef(componentType); - byte[] state = SceneElementSerialization.CaptureState( - component, - m_workspace.serialization, - m_workspace.assets); + byte[] state = SceneElementSerialization.CaptureState(component, m_workspace.serialization, m_workspace.assets); RecordElement( historyName ?? $"Add {componentType.Name}", new SceneElementHistoryData( @@ -61,7 +56,9 @@ public GameComponent AddComponent( existsAfter: true, beforeState: [], afterState: state, - incomingReferences: [])); + incomingReferences: [] + ) + ); return component; } catch (Exception exception) @@ -99,14 +96,8 @@ public bool RemoveComponent( GameObject owner = component.gameObject; GameScene scene = owner.scene; TypeRef typeRef = GetElementType(component); - byte[] state = SceneElementSerialization.CaptureState( - component, - m_workspace.serialization, - m_workspace.assets); - SceneIncomingReferenceState[] incoming = SceneReferenceIndex.CaptureIncoming( - component, - scene, - m_workspace); + byte[] state = SceneElementSerialization.CaptureState(component, m_workspace.serialization, m_workspace.assets); + SceneIncomingReferenceState[] incoming = SceneReferenceIndex.CaptureIncoming(component, scene, m_workspace); int index = owner.GetComponentIndex(component); Guid componentId = component.identity.persistentId; Type componentType = component.GetType(); @@ -128,7 +119,9 @@ public bool RemoveComponent( existsAfter: false, beforeState: state, afterState: [], - incoming)); + incoming + ) + ); } catch (Exception exception) { @@ -136,20 +129,15 @@ public bool RemoveComponent( { ExceptionDispatchInfo.Capture(exception).Throw(); } + RollbackAndRethrow(exception, () => { - GameComponent restored = SceneElementSerialization.RestoreComponent( - owner, - typeRef, - componentId, - index, - state, - m_workspace.serialization, - m_workspace.assets); + GameComponent restored = SceneElementSerialization.RestoreComponent(owner, typeRef, componentId, index, state, m_workspace.serialization, m_workspace.assets); RequireElementRestore(restored, state); RequireReferenceRestore(SceneReferenceIndex.RestoreIncoming(incoming, m_workspace)); }); } + return true; } @@ -170,24 +158,19 @@ public void ResetComponent( using IDisposable presentationScope = m_workspace.EnterPresentationScope(); m_workspace.EnsureEditable(component.gameObject.scene); GameObject owner = component.gameObject; - byte[] before = SceneElementSerialization.CaptureState( - component, - m_workspace.serialization, - m_workspace.assets); + byte[] before = SceneElementSerialization.CaptureState(component, m_workspace.serialization, m_workspace.assets); byte[] after; try { owner.ResetComponent(component); - after = SceneElementSerialization.CaptureState( - component, - m_workspace.serialization, - m_workspace.assets); + after = SceneElementSerialization.CaptureState(component, m_workspace.serialization, m_workspace.assets); } catch (Exception exception) { RollbackAndRethrow(exception, () => RequireElementRestore(component, before)); throw; } + if (before.AsSpan().SequenceEqual(after)) return; int index = owner.GetComponentIndex(component); @@ -195,19 +178,22 @@ public void ResetComponent( () => RecordElement( historyName ?? $"Reset {component.GetType().Name}", new SceneElementHistoryData( - SceneElementKind.Component, - owner.scene.identity.persistentId, - owner.identity.persistentId, - component.identity.persistentId, - GetElementType(component), - index, - index, - existsBefore: true, - existsAfter: true, - before, - after, - incomingReferences: [])), - () => RequireElementRestore(component, before)); + SceneElementKind.Component, + owner.scene.identity.persistentId, + owner.identity.persistentId, + component.identity.persistentId, + GetElementType(component), + index, + index, + existsBefore: true, + existsAfter: true, + before, + after, + incomingReferences: [] + ) + ), + () => RequireElementRestore(component, before) + ); } /// @@ -244,25 +230,29 @@ public void SetComponentIndex( RollbackAndRethrow(exception, () => owner.SetComponentIndex(component, beforeIndex)); throw; } + if (beforeIndex == afterIndex) return; RecordWithRollback( () => RecordElement( historyName, new SceneElementHistoryData( - SceneElementKind.Component, - owner.scene.identity.persistentId, - owner.identity.persistentId, - component.identity.persistentId, - GetElementType(component), - beforeIndex, - afterIndex, - existsBefore: true, - existsAfter: true, - beforeState: [], - afterState: [], - incomingReferences: [])), - () => owner.SetComponentIndex(component, beforeIndex)); + SceneElementKind.Component, + owner.scene.identity.persistentId, + owner.identity.persistentId, + component.identity.persistentId, + GetElementType(component), + beforeIndex, + afterIndex, + existsBefore: true, + existsAfter: true, + beforeState: [], + afterState: [], + incomingReferences: [] + ) + ), + () => owner.SetComponentIndex(component, beforeIndex) + ); } /// @@ -305,11 +295,10 @@ public GameSystem AddSystem( existsBefore: false, existsAfter: true, beforeState: [], - afterState: SceneElementSerialization.CaptureState( - system, - m_workspace.serialization, - m_workspace.assets), - incomingReferences: [])); + afterState: SceneElementSerialization.CaptureState(system, m_workspace.serialization, m_workspace.assets), + incomingReferences: [] + ) + ); return system; } catch (Exception exception) @@ -326,9 +315,6 @@ public GameSystem AddSystem( /// /// Removes the system from its scene and records a reversible serialized history change. /// - /// - /// The scene currently owning the system. - /// /// /// The registered system to remove. /// @@ -339,24 +325,19 @@ public GameSystem AddSystem( /// when the system was removed and recorded. /// public bool RemoveSystem( - GameScene scene, GameSystem system, string? historyName = null ) { - ArgumentNullException.ThrowIfNull(scene); ArgumentNullException.ThrowIfNull(system); + if (system.isDestroyed) + return false; + GameScene scene = ResolveOwnerScene(system); using IDisposable presentationScope = m_workspace.EnterPresentationScope(); m_workspace.EnsureEditable(scene); if (system.isDestroyed) return false; - byte[] state = SceneElementSerialization.CaptureState( - system, - m_workspace.serialization, - m_workspace.assets); - SceneIncomingReferenceState[] incoming = SceneReferenceIndex.CaptureIncoming( - system, - scene, - m_workspace); + byte[] state = SceneElementSerialization.CaptureState(system, m_workspace.serialization, m_workspace.assets); + SceneIncomingReferenceState[] incoming = SceneReferenceIndex.CaptureIncoming(system, scene, m_workspace); int index = scene.GetSystemIndex(system); Guid systemId = system.identity.persistentId; TypeRef typeRef = GetElementType(system); @@ -368,18 +349,20 @@ public bool RemoveSystem( RecordElement( historyName ?? $"Remove {systemType.Name}", new SceneElementHistoryData( - SceneElementKind.System, - scene.identity.persistentId, - Guid.Empty, - systemId, - typeRef, - beforeIndex: index, - afterIndex: -1, - existsBefore: true, - existsAfter: false, - beforeState: state, + SceneElementKind.System, + scene.identity.persistentId, + Guid.Empty, + systemId, + typeRef, + beforeIndex: index, + afterIndex: -1, + existsBefore: true, + existsAfter: false, + beforeState: state, afterState: [], - incoming)); + incoming + ) + ); } catch (Exception exception) { @@ -387,29 +370,21 @@ public bool RemoveSystem( { ExceptionDispatchInfo.Capture(exception).Throw(); } + RollbackAndRethrow(exception, () => { - GameSystem restored = SceneElementSerialization.RestoreSystem( - scene, - typeRef, - systemId, - index, - state, - m_workspace.serialization, - m_workspace.assets); + GameSystem restored = SceneElementSerialization.RestoreSystem(scene, typeRef, systemId, index, state, m_workspace.serialization, m_workspace.assets); RequireElementRestore(restored, state); RequireReferenceRestore(SceneReferenceIndex.RestoreIncoming(incoming, m_workspace)); }); } + return true; } /// /// Resets one scene system and records its compact property state before and after Reset. /// - /// - /// The scene currently owning the system. - /// /// /// The registered system to reset. /// @@ -417,32 +392,26 @@ public bool RemoveSystem( /// An optional user-facing history entry name. /// public void ResetSystem( - GameScene scene, GameSystem system, string? historyName = null ) { - ArgumentNullException.ThrowIfNull(scene); ArgumentNullException.ThrowIfNull(system); + GameScene scene = ResolveOwnerScene(system); using IDisposable presentationScope = m_workspace.EnterPresentationScope(); m_workspace.EnsureEditable(scene); - byte[] before = SceneElementSerialization.CaptureState( - system, - m_workspace.serialization, - m_workspace.assets); + byte[] before = SceneElementSerialization.CaptureState(system, m_workspace.serialization, m_workspace.assets); byte[] after; try { scene.ResetSystem(system); - after = SceneElementSerialization.CaptureState( - system, - m_workspace.serialization, - m_workspace.assets); + after = SceneElementSerialization.CaptureState(system, m_workspace.serialization, m_workspace.assets); } catch (Exception exception) { RollbackAndRethrow(exception, () => RequireElementRestore(system, before)); throw; } + if (before.AsSpan().SequenceEqual(after)) return; int index = scene.GetSystemIndex(system); @@ -450,19 +419,22 @@ public void ResetSystem( () => RecordElement( historyName ?? $"Reset {system.GetType().Name}", new SceneElementHistoryData( - SceneElementKind.System, - scene.identity.persistentId, - Guid.Empty, - system.identity.persistentId, - GetElementType(system), - index, - index, - existsBefore: true, - existsAfter: true, - before, - after, - incomingReferences: [])), - () => RequireElementRestore(system, before)); + SceneElementKind.System, + scene.identity.persistentId, + Guid.Empty, + system.identity.persistentId, + GetElementType(system), + index, + index, + existsBefore: true, + existsAfter: true, + before, + after, + incomingReferences: [] + ) + ), + () => RequireElementRestore(system, before) + ); } /// @@ -503,39 +475,38 @@ public void SetSystemIndex( RollbackAndRethrow(exception, () => scene.SetSystemIndex(system, beforeIndex)); throw; } + if (beforeIndex == afterIndex) return; RecordWithRollback( () => RecordElement( historyName, new SceneElementHistoryData( - SceneElementKind.System, - scene.identity.persistentId, - Guid.Empty, - system.identity.persistentId, - GetElementType(system), - beforeIndex, - afterIndex, - existsBefore: true, - existsAfter: true, - beforeState: [], - afterState: [], - incomingReferences: [])), - () => scene.SetSystemIndex(system, beforeIndex)); + SceneElementKind.System, + scene.identity.persistentId, + Guid.Empty, + system.identity.persistentId, + GetElementType(system), + beforeIndex, + afterIndex, + existsBefore: true, + existsAfter: true, + beforeState: [], + afterState: [], + incomingReferences: [] + ) + ), + () => scene.SetSystemIndex(system, beforeIndex) + ); } - private TypeRef GetElementType(EngineObject element) - => element switch - { - MissingGameComponent missing => missing.missingType, - MissingGameSystem missing => missing.missingType, - _ => GetTypeRef(element.GetType()) - }; - + private TypeRef GetElementType(EngineObject element) => element switch + { + MissingGameComponent missing => missing.missingType, + MissingGameSystem missing => missing.missingType, + _ => GetTypeRef(element.GetType())}; private void RequireElementRestore( EngineObject element, ReadOnlySpan data - ) - => SceneElementSerialization.RestoreState(element, data, m_workspace.serialization, m_workspace.assets); - + ) => SceneElementSerialization.RestoreState(element, data, m_workspace.serialization, m_workspace.assets); } diff --git a/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs b/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs new file mode 100644 index 000000000..0443166d4 --- /dev/null +++ b/src/composition/editor/features/Inno.Editor.Scene/Documents/SceneEdits.Selection.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Inno.Core.Mathematics; +using Inno.Core.Serialization; +using Inno.Editor.Interactions; +using Inno.Scene; + +namespace Inno.Editor.Scene; +sealed partial class SceneEdits +{ + /// + /// Deletes selected subtrees atomically, omitting descendants already covered by a selected ancestor. + /// + /// + /// Live objects to delete; every owner is validated before the first mutation. + /// + /// + /// An owner is unavailable, read-only, or rollback fails. + /// + public void DeleteGameObjects(IReadOnlyList targets) + { + ArgumentNullException.ThrowIfNull(targets); + GameObject[] unique = targets.Distinct().ToArray(); + foreach (GameObject target in unique) + { + ArgumentNullException.ThrowIfNull(target); + m_workspace.EnsureEditable(target.scene); + if (!target.isRuntimeValid) + throw new InvalidOperationException("A selected GameObject is no longer available."); + } + + var selected = unique.Select(value => value.identity.persistentId).ToHashSet(); + GameObject[] roots = unique.Where(value => !HasSelectedAncestor(value, selected)).ToArray(); + using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction("Delete GameObjects"); + foreach (GameObject target in roots) + _ = DeleteGameObject(target); + transaction.Commit(); + } + + /// + /// Applies one primary property edit to a selection using owner-resolved serialized values and one atomic History transaction. + /// + /// + /// Ordered compatible scene objects; the first object supplies the edited value. + /// + /// + /// The common root serialized property. + /// + /// + /// Changes the primary value synchronously; no callback is retained. + /// + /// + /// The user-facing batch edit name. + /// + /// + /// An optional active gesture identity; null keeps discrete edits separate. + /// + /// + /// An optional public scalar value component; Euler axes use euler.x, euler.y or euler.z. + /// + /// + /// True when at least one object changed; false when all values already matched. + /// + /// + /// The selection is empty or a required common property is absent. + /// + /// + /// An owner is unavailable, read-only, or rollback fails. + /// + public bool ChangeProperties( + IReadOnlyList targets, + string propertyName, + Action mutation, + string historyName, + string? mergeKey = null, + string? componentName = null + ) { + ArgumentNullException.ThrowIfNull(targets); + ArgumentException.ThrowIfNullOrWhiteSpace(propertyName); + ArgumentNullException.ThrowIfNull(mutation); + ArgumentException.ThrowIfNullOrWhiteSpace(historyName); + if (targets.Count == 0) + throw new ArgumentException("A property edit requires at least one target.", nameof(targets)); + foreach (EngineObject target in targets) + { + ArgumentNullException.ThrowIfNull(target); + m_workspace.EnsureEditable(ResolveOwnerScene(target)); + if (!ScenePropertySerialization.CapturePropertySnapshots(target, m_workspace.serialization, m_workspace.assets).Any(value => value.name == propertyName)) + throw new ArgumentException($"Selected target has no serialized property '{propertyName}'.", nameof(targets)); + } + + if (componentName is not null) + foreach (EngineObject target in targets) + ValidateComponent(GetSelectionProperty(target, propertyName).propertyType, componentName); + using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction(historyName); + bool changed = ChangeProperty(targets[0], propertyName, mutation, historyName, mergeKey); + SerializationPropertySnapshot snapshot = ScenePropertySerialization.CapturePropertySnapshots( + targets[0], + m_workspace.serialization, + m_workspace.assets + ).Single(value => value.name == propertyName); + byte[] value = m_workspace.serialization.EncodePropertySnapshots([snapshot]); + for (int index = 1; index < targets.Count; index++) + { + EngineObject target = targets[index]; + changed |= ChangeProperty(target, propertyName, () => + { + if (componentName is null) + RequirePropertyRestore(target, value); + else + { + SerializedProperty property = GetSelectionProperty(target, propertyName); + object primary = GetSelectionProperty(targets[0], propertyName).GetValue()!; + object current = property.GetValue() ?? Activator.CreateInstance(Nullable.GetUnderlyingType(property.propertyType) ?? property.propertyType)!; + property.SetValue(CopyComponent(primary, current, componentName)); + } + }, historyName, mergeKey); + } + + transaction.Commit(); + return changed; + } + + /// + /// Compares common serialized property values using the complete owner serialization context. + /// + /// + /// Live compatible scene objects to compare. + /// + /// + /// The required common serialized member. + /// + /// + /// True when every encoded value matches, including an empty selection. + /// + /// + /// A selected object does not expose the requested property. + /// + public bool HaveSamePropertyValues( + IReadOnlyList targets, + string propertyName + ) { + ArgumentNullException.ThrowIfNull(targets); + ArgumentException.ThrowIfNullOrWhiteSpace(propertyName); + SerializationPropertySnapshot? first = null; + foreach (EngineObject target in targets) + { + SerializationPropertySnapshot current = ScenePropertySerialization.CapturePropertySnapshots( + target, + m_workspace.serialization, + m_workspace.assets + ).Single(value => value.name == propertyName); + if (first is not null && !first.data.Span.SequenceEqual(current.data.Span)) + return false; + first = current; + } + + return true; + } + + /// + /// Moves minimal selected roots together, preserving descendants and recording only affected placements. + /// + /// + /// Selected live objects in their required relative order. + /// + /// + /// The loaded editable destination scene. + /// + /// + /// The new parent, or null for scene roots. + /// + /// + /// The insertion index after removing the selected roots; the default appends them. + /// + /// + /// An owner is read-only, unavailable, or the move would form a cycle. + /// + /// + /// The parent belongs to a different scene. + /// + public void MoveGameObjects( + IReadOnlyList targets, + GameScene destination, + GameObject? parent = null, + int insertionIndex = int.MaxValue + ) { + ArgumentNullException.ThrowIfNull(targets); + ArgumentNullException.ThrowIfNull(destination); + m_workspace.EnsureEditable(destination); + if (parent is not null && !ReferenceEquals(parent.scene, destination)) + throw new ArgumentException("The parent must belong to the destination scene.", nameof(parent)); + GameObject[] unique = targets.Distinct().ToArray(); + foreach (GameObject target in unique) + { + ArgumentNullException.ThrowIfNull(target); + m_workspace.EnsureEditable(target.scene); + if (!target.isRuntimeValid) + throw new InvalidOperationException("A selected GameObject is no longer available."); + } + + var selected = unique.Select(value => value.identity.persistentId).ToHashSet(); + GameObject[] roots = unique.Where(value => !HasSelectedAncestor(value, selected)).ToArray(); + if (roots.Length == 0) + return; + for (var ancestor = parent?.transform; ancestor is not null; ancestor = ancestor.parent) + if (selected.Contains(ancestor.gameObject.identity.persistentId)) + throw new InvalidOperationException("A selection cannot be moved beneath itself."); + _ = ChangeHierarchy(roots[0], hierarchy => + { + foreach (GameObject target in roots) + { + if (!ReferenceEquals(target.scene, destination)) + hierarchy.MoveToScene(target, destination); + target.transform.SetParent(parent?.transform); + target.transform.SetSiblingIndex(int.MaxValue); + } + + int available = parent is null ? destination.GetObjects().Count(value => value.transform.parent is null) - roots.Length : parent.transform.children.Count - roots.Length; + int start = Math.Clamp(insertionIndex, 0, available); + // Place from the end so intermediate sibling shifts cannot reverse the selection. + for (int index = roots.Length - 1; index >= 0; index--) + roots[index].transform.SetSiblingIndex(start); + }, "Move GameObjects", roots); + } + + private SerializedProperty GetSelectionProperty( + EngineObject target, + string propertyName + ) => m_workspace.serialization.GetProperties((ISerializable)target).Single(value => value.name == propertyName); + + private static void ValidateComponent( + Type type, + string componentName + ) { + type = Nullable.GetUnderlyingType(type) ?? type; + if (type == typeof(Quaternion) && componentName is "euler.x" or "euler.y" or "euler.z") + return; + FieldInfo? field = type.GetField(componentName, BindingFlags.Instance | BindingFlags.Public); + if (!type.IsValueType || field is null || field.IsInitOnly || !field.FieldType.IsPrimitive) + throw new ArgumentException("A batch component must be a writable public scalar value field.", nameof(componentName)); + } + + private static object CopyComponent( + object primary, + object current, + string componentName + ) { + if (primary is Quaternion edited && current is Quaternion existing && componentName.StartsWith("euler.", StringComparison.Ordinal)) + { + Vector3 next = edited.ToEulerAnglesXYZDegrees(); + Vector3 value = existing.ToEulerAnglesXYZDegrees(); + switch (componentName) + { + case "euler.x": value.x = next.x; break; + case "euler.y": value.y = next.y; break; + case "euler.z": value.z = next.z; break; + } + return Quaternion.FromEulerAnglesXYZDegrees(value).normalized; + } + // Only the current public value is boxed and edited; no private state or plugin metadata is retained. + FieldInfo field = primary.GetType().GetField(componentName, BindingFlags.Instance | BindingFlags.Public)!; + field.SetValue(current, field.GetValue(primary)); + return current; + } + + private static bool HasSelectedAncestor( + GameObject target, + ISet selected + ) { + for (var parent = target.transform.parent; parent is not null; parent = parent.parent) + if (selected.Contains(parent.gameObject.identity.persistentId)) + return true; + return false; + } +} diff --git a/src/composition/editor/features/Inno.Editor.Scripting/Inno.Editor.Scripting.csproj b/src/composition/editor/features/Inno.Editor.Scripting/Inno.Editor.Scripting.csproj index c2dd4cd6f..c5a0e1242 100644 --- a/src/composition/editor/features/Inno.Editor.Scripting/Inno.Editor.Scripting.csproj +++ b/src/composition/editor/features/Inno.Editor.Scripting/Inno.Editor.Scripting.csproj @@ -10,36 +10,16 @@ - - + - - - - - - - + + + + + + - + diff --git a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/EditorScripting.cs b/src/composition/editor/features/Inno.Editor.Scripting/Runtime/EditorScripting.cs index 73e042490..c15d4b23d 100644 --- a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/EditorScripting.cs +++ b/src/composition/editor/features/Inno.Editor.Scripting/Runtime/EditorScripting.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Plugins.Authoring; @@ -12,7 +11,6 @@ using Inno.Scripting.Reload; namespace Inno.Editor.Scripting; - /// /// Owns script compilation and activation for one editor project. /// @@ -39,7 +37,6 @@ internal sealed class EditorScripting : EditorModule, IEditorScriptCompilation private bool m_reportedReloadWait; private string? m_activationFailure; private long m_nextRequestId; - internal EditorScripting( AssetPipeline assets, PluginEnvironment plugins, @@ -65,25 +62,20 @@ Func moduleSourceFactory /// Gets whether blocks following updates is enabled for this implementation. /// public override bool blocksFollowingUpdates => m_blockFollowingUpdates; - /// /// Gets whether script compilation is currently active. /// - internal bool isCompiling - => m_manager?.isFaulted != true && - (m_showCompilation || - m_compilation is not null || - m_pendingCompletion is not null || - m_manager?.isCompiling == true || - m_manager?.isUnloadVerificationPending == true || - m_manager?.isCompilationPending == true); - + internal bool isCompiling => m_manager?.isFaulted != true + && (m_showCompilation + || m_compilation is not null + || m_pendingCompletion is not null + || m_manager?.isCompiling == true + || m_manager?.isUnloadVerificationPending == true + || m_manager?.isCompilationPending == true); internal bool isAvailable => m_manager is not null; - - internal bool requiresExclusiveProgress => isCompiling && m_reloads.CanActivateReload(out _); - - internal bool canReload => m_manager is not null && !isCompiling && !m_manager.isFaulted - && m_reloads.CanRequestReload(out _); + /// + public bool requiresExclusiveProgress => isCompiling && m_reloads.CanActivateReload(out _); + internal bool canReload => m_manager is not null && !isCompiling && !m_manager.isFaulted && m_reloads.CanRequestReload(out _); /// /// Gets the current lifecycle state observed by callers. @@ -113,15 +105,11 @@ public EditorScriptCompilationState state /// /// Gets the current compiler progress. /// - internal float progress => m_manager?.compilationProgress ?? 0f; - + public float progress => m_manager?.compilationProgress ?? 0f; /// /// Gets the current compiler stage. /// - public string status => m_activationFailure ?? (!m_reloads.CanActivateReload(out string? reason) - ? reason ?? "Waiting for Play Mode to stop." - : m_manager?.compilationStatus ?? "Initializing project scripting."); - + public string status => m_activationFailure ?? (!m_reloads.CanActivateReload(out string? reason) ? reason ?? "Waiting for Play Mode to stop." : m_manager?.compilationStatus ?? "Initializing project scripting."); /// /// Gets the most recently completed compiler result, which may still await activation and retirement verification. /// @@ -135,22 +123,23 @@ public EditorScriptCompilationState state /// public IScriptCompilationTicket RequestCompilation() { - var ticket = new ScriptCompilationTicket( - ++m_nextRequestId, - "Script compilation is queued."); + var ticket = new ScriptCompilationTicket(++m_nextRequestId, "Script compilation is queued."); ScriptReloadHost? manager = m_manager; if (!m_reloads.CanRequestReload(out string? reason)) { ticket.MarkFailed(result: null, reason ?? "Stop Play Mode before requesting compilation."); return ticket; } + if (manager is null || manager.isFaulted) { - ticket.MarkFailed(result: null, manager?.isFaulted == true - ? "Generation retirement failed. Restart the host before requesting compilation." - : "Project scripting is not available."); + ticket.MarkFailed( + result: null, + manager?.isFaulted == true ? "Generation retirement failed. Restart the host before requesting compilation." : "Project scripting is not available." + ); return ticket; } + m_currentTicket?.MarkSuperseded(); m_currentTicket = ticket; QueueReload(static value => value.RecompileScripting(), supersedeCurrentTicket: false); @@ -174,8 +163,8 @@ internal void ReloadPlugins() QueueReload(static manager => manager.ReloadPlugins(), supersedeCurrentTicket: true); } - internal void CancelCompilation() => m_manager?.CancelCompilation(); - + /// + public void CancelCompilation() => m_manager?.CancelCompilation(); /// /// Initializes this feature when its owning runtime becomes active. /// @@ -184,15 +173,7 @@ internal void ReloadPlugins() /// protected override void OnStart(EditorContext context) { - m_manager = new ScriptReloadHost( - new ScriptReloadOptions(), - m_compiler, - m_assets, - m_plugins, - m_modules, - m_settings, - m_reloads, - m_moduleSourceFactory); + m_manager = new ScriptReloadHost(new ScriptReloadOptions(), m_compiler, m_assets, m_plugins, m_modules, m_settings, m_reloads, m_moduleSourceFactory); m_manager.Start(); if (m_manager.TryCompilePending(out Task? compilation)) { @@ -216,11 +197,13 @@ protected override void OnUpdate(EditorContext context) m_hideCompilationOnNextUpdate = false; m_showCompilation = false; } + if (m_manager?.isFaulted == true) { AdvanceUnloadVerification(); return; } + if (!m_reloads.CanActivateReload(out string? waitingReason)) { if (m_manager?.isCompilationPending == true && !m_reportedReloadWait) @@ -228,8 +211,10 @@ protected override void OnUpdate(EditorContext context) m_log.Write(LogLevel.Info, "{0}", [waitingReason ?? "Script changes are queued until Play Mode stops."]); m_reportedReloadWait = true; } + return; } + m_reportedReloadWait = false; CompleteCompilation(); if (m_manager is null || m_compilation is not null) @@ -239,6 +224,7 @@ protected override void OnUpdate(EditorContext context) AdvanceUnloadVerification(); return; } + if (m_manager.isFaulted) return; if (!context.isFocused && !m_explicitReloadPending) @@ -259,12 +245,10 @@ protected override void OnUpdate(EditorContext context) /// The context that supplies state and services for this operation. /// protected override void OnStop(EditorContext context) => DisposeManager(); - /// /// Releases resources retained by this feature after it has stopped. /// protected override void OnDispose() => DisposeManager(); - private void CompleteCompilation() { Task? compilation = m_compilation; @@ -286,10 +270,10 @@ private void CompleteCompilation() ticket?.MarkSuperseded(); return; } + m_activationFailure = null; ScriptDiagnosticPublisher.ClearReload(); - CompleteAfterRetirement(new PendingCompletion(ticket, result, true, - "Script generation compiled, activated, and retired successfully.")); + CompleteAfterRetirement(new PendingCompletion(ticket, result, true, "Script generation compiled, activated, and retired successfully.")); } else { @@ -299,9 +283,7 @@ private void CompleteCompilation() m_blockFollowingUpdates = true; m_activationFailure = null; ScriptDiagnosticPublisher.ClearReload(); - CompleteAfterRetirement(new PendingCompletion(ticket, result, false, - "Script compilation failed after a Plugin availability change; unavailable types were " + - "preserved as Missing.")); + CompleteAfterRetirement(new PendingCompletion(ticket, result, false, "Script compilation failed after a Plugin availability change; unavailable types were " + "preserved as Missing.")); } else { @@ -315,18 +297,14 @@ private void CompleteCompilation() { m_plugins.RollbackPending(); ticket?.MarkCanceled("Script compilation was canceled; the active generation was retained."); - m_log.Write( - LogLevel.Info, - "Script compilation was canceled; the active generation was retained."); + m_log.Write(LogLevel.Info, "Script compilation was canceled; the active generation was retained."); } catch (Exception exception) { if (!m_manager.isFaulted) m_plugins.RollbackPending(); m_activationFailure = $"Script generation activation failed: {exception.Message}"; - ticket?.MarkFailed( - ticket.result, - $"Script generation activation failed: {exception.Message}"); + ticket?.MarkFailed(ticket.result, $"Script generation activation failed: {exception.Message}"); ScriptDiagnosticPublisher.PublishReloadFailure(exception); m_log.Write(LogLevel.Error, "Script assembly reload failed: {0}", [exception]); } @@ -334,9 +312,7 @@ private void CompleteCompilation() { m_compilation = null; m_compilationTicket = null; - m_hideCompilationOnNextUpdate = m_pendingCompletion is null && - m_manager?.isUnloadVerificationPending != true && - m_manager?.isCompilationPending != true; + m_hideCompilationOnNextUpdate = m_pendingCompletion is null && m_manager?.isUnloadVerificationPending != true && m_manager?.isCompilationPending != true; } } @@ -347,6 +323,7 @@ private void CompleteAfterRetirement(PendingCompletion completion) m_pendingCompletion = completion; return; } + FinishCompletion(completion); } @@ -357,9 +334,7 @@ private void FinishCompletion(PendingCompletion completion) bool projected = GenerateIdeProjection(); if (completion.succeeded) { - completion.ticket?.MarkSucceeded(completion.result, projected - ? completion.status - : "Script generation retired successfully, but IDE project generation failed."); + completion.ticket?.MarkSucceeded(completion.result, projected ? completion.status : "Script generation retired successfully, but IDE project generation failed."); } else { @@ -383,11 +358,13 @@ private void AdvanceUnloadVerification() { ScriptDiagnosticPublisher.PublishUnloadFailure(failure); } + m_activationFailure = status; m_showCompilation = false; m_hideCompilationOnNextUpdate = false; return; } + ScriptDiagnosticPublisher.ClearUnload(); if (completion is not null) FinishCompletion(completion); @@ -398,8 +375,7 @@ private bool GenerateIdeProjection() { try { - ScriptReloadHost manager = m_manager - ?? throw new InvalidOperationException("Project scripting is not available."); + ScriptReloadHost manager = m_manager ?? throw new InvalidOperationException("Project scripting is not available."); manager.GenerateProjectFiles(); ScriptDiagnosticPublisher.ClearIdeProjection(); return true; @@ -407,10 +383,7 @@ private bool GenerateIdeProjection() catch (Exception exception) { ScriptDiagnosticPublisher.PublishIdeProjectionFailure(exception); - m_log.Write( - LogLevel.Warn, - "Script generation activated, but IDE project generation failed: {0}", - [exception]); + m_log.Write(LogLevel.Warn, "Script generation activated, but IDE project generation failed: {0}", [exception]); return false; } } @@ -447,6 +420,7 @@ private void DisposeManager() ScriptDiagnosticPublisher.ClearAll(); return; } + m_manager.Dispose(); m_currentTicket?.MarkCanceled("Project scripting stopped before this request could activate."); m_manager = null; diff --git a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs b/src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs index e1111252d..937d7d159 100644 --- a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs +++ b/src/composition/editor/features/Inno.Editor.Scripting/Runtime/IEditorScriptCompilation.cs @@ -1,7 +1,6 @@ using Inno.Scripting.Compiler; namespace Inno.Editor.Scripting; - /// /// Identifies whether the active project script generation can safely start game simulation. /// @@ -11,17 +10,14 @@ public enum EditorScriptCompilationState /// The script service has not produced its initial active generation. /// Initializing, - /// /// A requested script generation is compiling, activating, or completing unload verification. /// Compiling, - /// /// The most recent compilation succeeded, its generation is active, and retired generations passed GC verification. /// Ready, - /// /// Compilation, activation, or generation retirement failed, so game simulation must not start. /// @@ -33,6 +29,20 @@ public enum EditorScriptCompilationState /// public interface IEditorScriptCompilation { + /// + /// Gets whether compiler or activation work currently requires a blocking progress presentation. + /// + bool requiresExclusiveProgress { get; } + + /// + /// Gets the most recent compiler progress fraction in the inclusive range zero to one. + /// + float progress { get; } + + /// + /// Requests cancellation while retaining ownership until compilation and retirement finish. + /// + void CancelCompilation(); /// /// Queues a fresh cache-aware compilation for a build or simulation workflow. /// @@ -41,7 +51,6 @@ public interface IEditorScriptCompilation /// a faulted host returns a failed ticket and requires restart. /// IScriptCompilationTicket RequestCompilation(); - /// /// Gets the most recently requested compilation ticket, or when no explicit request exists. /// diff --git a/src/composition/editor/features/Inno.Editor.Scripting/ScriptingReloadActions.cs b/src/composition/editor/features/Inno.Editor.Scripting/ScriptingReloadActions.cs index 96ed83d87..b910234d3 100644 --- a/src/composition/editor/features/Inno.Editor.Scripting/ScriptingReloadActions.cs +++ b/src/composition/editor/features/Inno.Editor.Scripting/ScriptingReloadActions.cs @@ -1,9 +1,8 @@ using Inno.Editor.Interactions; namespace Inno.Editor.Scripting; - [EditorAction(ScriptingInteractionIds.C_RELOAD_SCRIPTING, ScriptingInteractionIds.C_MAIN_MENU_AREA)] -[EditorMenu(ScriptingInteractionIds.C_MAIN_MENU_AREA, "Scripting/Reload Scripts", order: 100)] +[EditorMenu(ScriptingInteractionIds.C_MAIN_MENU_AREA, "Tools/Scripting/Reload Scripts", order: 100)] internal sealed class ReloadScriptingAction(EditorScripting scripting) : EditorAction { /// @@ -15,9 +14,7 @@ internal sealed class ReloadScriptingAction(EditorScripting scripting) : EditorA /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => scripting.canReload ? EditorActionState.enabled : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => scripting.canReload ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -28,7 +25,7 @@ protected override EditorActionState Query(EditorActionContext context) } [EditorAction(ScriptingInteractionIds.C_RELOAD_PLUGINS, ScriptingInteractionIds.C_MAIN_MENU_AREA)] -[EditorMenu(ScriptingInteractionIds.C_MAIN_MENU_AREA, "Scripting/Reload Plugins", order: 120)] +[EditorMenu(ScriptingInteractionIds.C_MAIN_MENU_AREA, "Tools/Scripting/Reload Plugins", order: 120)] internal sealed class ReloadPluginsAction(EditorScripting scripting) : EditorAction { /// @@ -40,9 +37,7 @@ internal sealed class ReloadPluginsAction(EditorScripting scripting) : EditorAct /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => scripting.canReload ? EditorActionState.enabled : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => scripting.canReload ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Inno.Editor.Shaders.csproj b/src/composition/editor/features/Inno.Editor.Shaders/Inno.Editor.Shaders.csproj index e806c2c6f..6d3ced8a9 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Inno.Editor.Shaders.csproj +++ b/src/composition/editor/features/Inno.Editor.Shaders/Inno.Editor.Shaders.csproj @@ -10,47 +10,27 @@ - + - - - - - - - - + + + + - - - + - - + - - + + + + + - diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs b/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs index 62ade737f..6829869d1 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs +++ b/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialDocuments.cs @@ -16,7 +16,6 @@ using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Shaders; - /// /// Owns Material authoring sessions; native source editing uses the shared asset draft lifecycle. /// @@ -27,9 +26,18 @@ public sealed class MaterialDocuments : EditorModule private readonly AssetDraftDocuments m_documents; private readonly SerializationRegistry m_serialization; private readonly ConditionalWeakTable m_presentations = new(); - internal readonly AssetPipeline assets; + /// + /// Gets the authoritative asset catalog used by source-aware inspectors. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetPipeline assets { get; } + internal readonly EditorInteractions interactions; - internal readonly IEditorPreviewService previews; + /// + /// Gets the generation-scoped preview service shared by material presentations. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public IEditorPreviewService previews { get; } internal MaterialDocuments( AssetPipeline assets, @@ -37,7 +45,9 @@ internal MaterialDocuments( EditorInteractions interactions, IEditorPreviewService previews ) { - this.assets = assets; this.interactions = interactions; this.previews = previews; + this.assets = assets; + this.interactions = interactions; + this.previews = previews; m_serialization = serialization; m_documents = new(assets, serialization, interactions, "inno.material", C_HISTORY, ".imaterial", "Material"); } @@ -52,7 +62,6 @@ IEditorPreviewService previews /// The persistent identity used for subsequent draft operations. /// public Guid Open(AssetPath path) => m_documents.Open(path); - /// /// Reads a detached current-generation Material; referenced assets remain canonical read-only inputs. /// @@ -63,7 +72,6 @@ IEditorPreviewService previews /// The current unsaved Material value. /// public MaterialAsset Read(Guid assetId) => m_documents.Read(assetId); - /// /// Changes a draft through shared History without modifying its source or Scene/Game. /// @@ -73,56 +81,72 @@ IEditorPreviewService previews /// /// Detached edited Material. /// - /// - /// Whether this sample finishes the active gesture. + /// + /// The current gesture key, or null for a discrete edit. /// public void Replace( Guid assetId, MaterialAsset candidate, - bool finishGesture = true - ) - => m_documents.Replace(assetId, candidate, finishGesture); - - /// - /// Finishes a draft gesture without saving its source. - /// - /// - /// Open Material source identity. - /// - public void Commit(Guid assetId) => m_documents.Commit(assetId); - + string? mergeKey = null + ) => m_documents.Replace(assetId, candidate, mergeKey); /// /// Changes compatible selected drafts within one shared gesture. /// /// /// Detached Material values keyed by persistent source identity. /// - /// - /// Whether this sample finishes the shared gesture. + /// + /// The current gesture key, or null for a discrete edit. /// public void ReplaceMany( IReadOnlyDictionary candidates, - bool finishGesture = true - ) - => m_documents.ReplaceMany(candidates, finishGesture); - + string? mergeKey = null + ) => m_documents.ReplaceMany(candidates, mergeKey); /// - /// Completes a multi-document gesture as one shared History transaction. + /// Opens the current source draft without publishing it. /// - /// - /// Participating persistent source identities. + /// + /// The isolated Material source path. /// - public void CommitMany(IEnumerable assetIds) => m_documents.CommitMany(assetIds); - - internal Draft OpenDraft(AssetPath path) => m_documents.GetDraft(Open(path)); - internal Draft Open(MaterialAsset material) + /// + /// The generation-scoped draft; callers must not retain it across a reload. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public Draft OpenDraft(AssetPath path) => m_documents.GetDraft(Open(path)); + /// + /// Resolves the source draft for a canonical Material identity. + /// + /// + /// The current canonical Material whose source is edited. + /// + /// + /// The generation-scoped editable source draft. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public Draft Open(MaterialAsset material) { if (!assets.TryGetInfo(material.identity.persistentId, out AssetInfo? info) || info is null) throw new IOException("The Material source identity is unavailable."); return OpenDraft(info.assetPath); } - internal void TouchInspection(Draft draft) => m_documents.TouchInspection(draft.id); - internal ShaderParameterPresentation Presentation( + + /// + /// Reads parameter presentation from the current authoring Shader graph. + /// + /// + /// The canonical Shader used by the Material. + /// + /// + /// The stable binding identity. + /// + /// + /// Receives the source diagnostic, or an empty string when presentation is available. + /// + /// + /// Detached presentation values; an unavailable graph uses the ordinary presentation defaults. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public ShaderParameterPresentation Presentation( ShaderAsset shader, ShaderPropertyId propertyId, out string error @@ -142,16 +166,11 @@ out string error cache.error = failure.Message; } } + error = cache.error; - return cache.graph is null ? new() : ShaderParameterPresentation.Read(cache.graph, propertyId, - m_serialization, AssetSerializationContext.Create(assets)); + return cache.graph is null ? new() : ShaderParameterPresentation.Read(cache.graph, propertyId, m_serialization, AssetSerializationContext.Create(assets)); } - internal void Edit( - Draft draft, - MaterialAsset candidate, - bool finishGesture - ) => Replace(draft.id, candidate, finishGesture); - internal void Commit(Draft draft) => Commit(draft.id); + internal void ValidateHistory( EditorHistoryChange change, EditorHistoryDirection direction @@ -160,9 +179,13 @@ internal void ApplyHistory( EditorHistoryChange change, EditorHistoryDirection direction ) => m_documents.ApplyHistory(change, direction); + internal bool TryMergeHistory( + EditorHistoryChange older, + EditorHistoryChange newer, + out EditorHistoryChange? merged + ) => m_documents.TryMergeHistory(older, newer, out merged); internal static bool Recoverable(Exception error) => error is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException or FormatException && Inno.Core.Execution.RetirementPendingException.Find(error) is null; - /// /// Initializes this feature when its owning runtime becomes active. /// @@ -215,11 +238,11 @@ internal sealed class MaterialDraftHistory(MaterialDocuments documents) : Editor /// /// The validated editor history availability that represents the completed operation. /// -protected override EditorHistoryAvailability Query( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryAvailability Query( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { documents.ValidateHistory(change, direction); @@ -230,6 +253,7 @@ EditorHistoryDirection direction return EditorHistoryAvailability.Unavailable(error.Message); } } + /// /// Applies a validated change atomically at the caller-controlled commit point. /// @@ -245,11 +269,11 @@ EditorHistoryDirection direction /// /// The validated editor history result that represents the completed operation. /// -protected override EditorHistoryResult Apply( - EditorHistoryContext context, - EditorHistoryChange change, - EditorHistoryDirection direction -) { + protected override EditorHistoryResult Apply( + EditorHistoryContext context, + EditorHistoryChange change, + EditorHistoryDirection direction + ) { try { documents.ApplyHistory(change, direction); @@ -260,4 +284,11 @@ EditorHistoryDirection direction return EditorHistoryResult.Failure(error.Message); } } + + /// + protected override bool TryMerge( + EditorHistoryChange older, + EditorHistoryChange newer, + out EditorHistoryChange? merged + ) => documents.TryMergeHistory(older, newer, out merged); } diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Properties/ScriptingApi.cs b/src/composition/editor/features/Inno.Editor.Shaders/Properties/ScriptingApi.cs index 5ca180c11..e02b2788e 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Properties/ScriptingApi.cs +++ b/src/composition/editor/features/Inno.Editor.Shaders/Properties/ScriptingApi.cs @@ -2,13 +2,8 @@ using Inno.Scripting.Api; [assembly: ScriptingApiNamespace("InnoEditor.Shaders", "Inno.Editor.Shaders", ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(ShaderNodeDrawerAttribute), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(ShaderNodeDrawer), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(ShaderNodeDrawContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(MaterialDocuments), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(ShaderPropertyInspector), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ShaderParameterPresentation), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ShaderPreviewProviderAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ShaderPreviewProvider), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ShaderPreviewContext), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(ShaderPreviews), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs b/src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs new file mode 100644 index 000000000..9cdc7b9ab --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Core/Extensions/EditorTypeSpecificity.cs @@ -0,0 +1,59 @@ +using System; + +namespace Inno.Editor.Core; +/// +/// Ranks assignable editor extension targets by class ancestry, with interface registrations one step away. +/// +public static class EditorTypeSpecificity +{ + /// + /// Determines whether a registration accepts a concrete target and returns its specificity distance. + /// + /// + /// The target type supplied by the active extension generation. + /// + /// + /// The registered class or interface accepted by the extension. + /// + /// + /// Zero for an exact match, ancestry distance for classes, or one for an interface. + /// + /// + /// True when the registration accepts the target; otherwise false and the maximum distance. + /// + /// + /// Either type is null. + /// + public static bool TryGet( + Type concreteType, + Type candidateType, + out int distance + ) { + ArgumentNullException.ThrowIfNull(concreteType); + ArgumentNullException.ThrowIfNull(candidateType); + if (!candidateType.IsAssignableFrom(concreteType)) + { + distance = int.MaxValue; + return false; + } + + if (concreteType == candidateType) + { + distance = 0; + return true; + } + + distance = 1; + Type? current = concreteType.BaseType; + while (current is not null) + { + if (current == candidateType) + return true; + distance++; + current = current.BaseType; + } + + distance = candidateType.IsInterface ? 1 : distance; + return true; + } +} diff --git a/src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs b/src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs new file mode 100644 index 000000000..dd4fa1331 --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs @@ -0,0 +1,54 @@ +using System; +using System.Text; + +namespace Inno.Editor.Core; + +/// +/// Formats readable Editor labels independently from a native UI context or session. +/// +public static class EditorDisplayNames +{ + /// + /// Converts an identifier into a readable editor label. + /// + /// + /// Source identifier. + /// + /// + /// A label with identifier separators and lower-to-upper transitions spaced; empty when the input is blank. + /// + public static string Nicify(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return string.Empty; + } + + var builder = new StringBuilder(name.Length + 8); + char previous = '\0'; + for (int i = 0; i < name.Length; i++) + { + char current = name[i]; + if (current == '_' || current == '-') + { + if (builder.Length > 0 && builder[builder.Length - 1] != ' ') + { + builder.Append(' '); + } + + previous = current; + continue; + } + + if (i > 0 && char.IsUpper(current) && char.IsLower(previous) && builder[builder.Length - 1] != ' ') + { + builder.Append(' '); + } + + builder.Append(builder.Length == 0 ? char.ToUpperInvariant(current) : current); + previous = current; + } + + return builder.ToString(); + } +} diff --git a/src/composition/editor/framework/Inno.Editor.Core/Properties/ScriptingApi.cs b/src/composition/editor/framework/Inno.Editor.Core/Properties/ScriptingApi.cs index a603a8e26..694a08389 100644 --- a/src/composition/editor/framework/Inno.Editor.Core/Properties/ScriptingApi.cs +++ b/src/composition/editor/framework/Inno.Editor.Core/Properties/ScriptingApi.cs @@ -20,3 +20,5 @@ [assembly: ScriptingApiExport(typeof(EditorModal), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorModalAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(IEditorPanelReloadState), ScriptingApiScope.Editor)] + +[assembly: ScriptingApiExport(typeof(EditorDisplayNames), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs b/src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs index db403e152..d34efea5a 100644 --- a/src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs +++ b/src/composition/editor/framework/Inno.Editor.Diagnostics/EditorConsole.cs @@ -24,6 +24,7 @@ public sealed class EditorConsole : IEditorConsole, ILogSink, IDiagnosticSink, I private int m_capacity = 1024; private long m_nextSequence; private long m_revision; + private IDisposable? m_playModeSubscription; private bool m_started; private bool m_disposed; @@ -93,10 +94,21 @@ public void Start() ObjectDisposedException.ThrowIf(m_disposed, this); if (m_started) return; - m_logRouter.RegisterSink(this); - m_diagnosticHub.RegisterSink(this); - m_playMode.stateChanged += OnPlayModeStateChanged; - m_started = true; + IDisposable subscription = m_playMode.ListenStateChanges(OnPlayModeStateChanged); + try + { + m_logRouter.RegisterSink(this); + m_diagnosticHub.RegisterSink(this); + m_playModeSubscription = subscription; + m_started = true; + } + catch + { + m_diagnosticHub.UnregisterSink(this); + m_logRouter.UnregisterSink(this); + subscription.Dispose(); + throw; + } } /// @@ -195,7 +207,8 @@ public void Dispose() return; if (m_started) { - m_playMode.stateChanged -= OnPlayModeStateChanged; + m_playModeSubscription?.Dispose(); + m_playModeSubscription = null; m_logRouter.UnregisterSink(this); m_diagnosticHub.UnregisterSink(this); m_started = false; diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/BuiltInInspectorAttributeDrawers.cs b/src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/BuiltInInspectorAttributeDrawers.cs index c73c00f6d..26ac1188f 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/BuiltInInspectorAttributeDrawers.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/AttributeDrawing/BuiltInInspectorAttributeDrawers.cs @@ -1,17 +1,15 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections; using System.Globalization; using System.Reflection; -using Inno.Adapter.Presentation.ImGui; using Inno.Core.Serialization; using Inno.Editor.Annotations; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [InspectorAttributeDrawer(typeof(HeaderAttribute))] internal sealed class HeaderInspectorAttributeDrawer : IInspectorAttributeDrawer { @@ -24,7 +22,7 @@ internal sealed class HeaderInspectorAttributeDrawer : IInspectorAttributeDrawer public void DrawBefore(InspectorAttributeDrawContext context) { var header = (HeaderAttribute)context.attribute; - ImGuiWidget.SectionHeader(header.title, header.description); + EditorUI.current.widgets.SectionHeader(header.title, header.description); } } @@ -40,14 +38,14 @@ internal sealed class TextInspectorAttributeDrawer : IInspectorAttributeDrawer public void DrawBefore(InspectorAttributeDrawContext context) { string text = ((TextAttribute)context.attribute).text; - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.textDisabled); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.textDisabled); try { - ImGuiWidget.WrappedText(text); + EditorUI.current.widgets.WrappedText(text); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } } @@ -64,7 +62,7 @@ internal sealed class SpaceInspectorAttributeDrawer : IInspectorAttributeDrawer public void DrawBefore(InspectorAttributeDrawContext context) { float height = ((SpaceAttribute)context.attribute).height; - NativeImGui.Dummy(new System.Numerics.Vector2(0f, height)); + Ui.Dummy(new System.Numerics.Vector2(0f, height)); } } @@ -133,10 +131,7 @@ internal sealed class ShowIfInspectorAttributeDrawer : IInspectorAttributeDrawer public void Update(InspectorAttributeDrawContext context) { var condition = (ShowIfAttribute)context.attribute; - context.isVisible &= InspectorConditionEvaluator.Evaluate( - context.GetSiblingValue(condition.memberName), - condition.condition, - condition.expectedValue); + context.isVisible &= InspectorConditionEvaluator.Evaluate(context.GetSiblingValue(condition.memberName), condition.condition, condition.expectedValue); } } @@ -152,10 +147,7 @@ internal sealed class HideIfInspectorAttributeDrawer : IInspectorAttributeDrawer public void Update(InspectorAttributeDrawContext context) { var condition = (HideIfAttribute)context.attribute; - context.isVisible &= !InspectorConditionEvaluator.Evaluate( - context.GetSiblingValue(condition.memberName), - condition.condition, - condition.expectedValue); + context.isVisible &= !InspectorConditionEvaluator.Evaluate(context.GetSiblingValue(condition.memberName), condition.condition, condition.expectedValue); } } @@ -173,25 +165,26 @@ public void DrawBefore(InspectorAttributeDrawContext context) var help = (HelpBoxAttribute)context.attribute; if (!string.IsNullOrWhiteSpace(help.conditionMember) && !InspectorConditionEvaluator.Evaluate( - context.GetSiblingValue(help.conditionMember), - help.condition, - help.expectedValue)) + context.GetSiblingValue(help.conditionMember), + help.condition, + help.expectedValue + )) { return; } + System.Numerics.Vector4 color = help.messageType switch { InspectorMessageType.Warning => EditorPalette.warning, InspectorMessageType.Error => EditorPalette.error, - _ => new System.Numerics.Vector4(0.42f, 0.66f, 0.88f, EditorPalette.opacityOpaque) - }; + _ => new System.Numerics.Vector4(0.42f, 0.66f, 0.88f, EditorPalette.opacityOpaque)}; string icon = help.messageType switch { InspectorMessageType.Warning => ImGuiIcon.TriangleExclamation, InspectorMessageType.Error => ImGuiIcon.CircleXmark, _ => ImGuiIcon.CircleInfo }; - ImGuiWidget.HelpBox(help.text, icon, color); + EditorUI.current.widgets.HelpBox(help.text, icon, color); } } @@ -212,8 +205,7 @@ internal static bool Evaluate( InspectorCondition.NotNull => value is not null, InspectorCondition.Assigned => IsAssigned(value), InspectorCondition.NotAssigned => !IsAssigned(value), - _ => throw new ArgumentOutOfRangeException(nameof(condition), condition, "Unknown Inspector condition.") - }; + _ => throw new ArgumentOutOfRangeException(nameof(condition), condition, "Unknown Inspector condition.")}; } private static bool IsTruthy(object? value) @@ -232,12 +224,8 @@ private static bool IsAssigned(object? value) { if (value is null) return false; - PropertyInfo? property = value.GetType().GetProperty( - "isAssigned", - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - return property?.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0 - ? (bool)(property.GetValue(value) ?? false) - : true; + PropertyInfo? property = value.GetType().GetProperty("isAssigned", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return property?.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0 ? (bool)(property.GetValue(value) ?? false) : true; } private static bool AreEqual( @@ -251,11 +239,18 @@ private static bool AreEqual( Type valueType = value.GetType(); try { - object converted = valueType.IsEnum - ? expected is string name - ? Enum.Parse(valueType, name, ignoreCase: false) - : Enum.ToObject(valueType, expected) - : Convert.ChangeType(expected, valueType, CultureInfo.InvariantCulture); + object converted = valueType.IsEnum ? expected is string name ? Enum.Parse( + valueType, + name, + ignoreCase: false + ) : Enum.ToObject( + valueType, + expected + ) : Convert.ChangeType( + expected, + valueType, + CultureInfo.InvariantCulture + ); return Equals(value, converted); } catch (Exception exception) when (exception is ArgumentException or InvalidCastException or FormatException or OverflowException) diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/Inno.Editor.Inspection.csproj b/src/composition/editor/framework/Inno.Editor.Inspection/Inno.Editor.Inspection.csproj index f00abc403..5558e7286 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/Inno.Editor.Inspection.csproj +++ b/src/composition/editor/framework/Inno.Editor.Inspection/Inno.Editor.Inspection.csproj @@ -12,21 +12,9 @@ - - - - - + + + diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs b/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs deleted file mode 100644 index 43d3d2cb4..000000000 --- a/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/AssetInspectionSelection.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Inno.Editor.Inspection; - -/// -/// Represents an ordered multi-asset selection without retaining asset or plugin-generation objects. -/// -public sealed class AssetInspectionSelection -{ - /// - /// Captures unique persistent identities in selection order. - /// - /// - /// Nonempty identities; the last identity is the primary selection. - /// - public AssetInspectionSelection(IEnumerable assetIds) - { - ArgumentNullException.ThrowIfNull(assetIds); - Guid[] values = assetIds.Distinct().ToArray(); - if (values.Length == 0 || values.Contains(Guid.Empty)) - throw new ArgumentException("Select at least one persistent asset.", nameof(assetIds)); - this.assetIds = Array.AsReadOnly(values); - } - /// - /// Gets stable asset identities in selection order. - /// - public IReadOnlyList assetIds { get; } - /// - /// Gets the most recently selected asset identity. - /// - public Guid primaryAssetId => assetIds[^1]; -} diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/DefaultInspectionDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/DefaultInspectionDrawer.cs index 37a8d6b92..561a43b2e 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/DefaultInspectionDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/ObjectDrawing/DefaultInspectionDrawer.cs @@ -1,10 +1,8 @@ +using Inno.Editor.ImGui; using System; - -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [InspectionDrawer(typeof(object), useForChildren: true, priority: int.MinValue)] internal sealed class DefaultInspectionDrawer : InspectionDrawer { @@ -28,9 +26,7 @@ internal sealed class DefaultInspectionDrawer : InspectionDrawer protected override (string name, Action? setter) BindName( InspectionDrawContext context, object target - ) - => (target.GetType().Name, null); - + ) => (target.GetType().Name, null); /// /// Renders the header presentation for the current editor frame. /// @@ -43,9 +39,7 @@ object target protected override void DrawHeader( InspectionDrawContext context, object target - ) - => NativeImGui.TextUnformatted(target.GetType().FullName ?? target.GetType().Name); - + ) => Ui.Text(target.GetType().FullName ?? target.GetType().Name); /// /// Renders the value presentation for the current editor frame. /// diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/Properties/ScriptingApi.cs b/src/composition/editor/framework/Inno.Editor.Inspection/Properties/ScriptingApi.cs index 4e433c9bb..a81860133 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/Properties/ScriptingApi.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/Properties/ScriptingApi.cs @@ -1,16 +1,11 @@ using Inno.Scripting.Api; using Inno.Editor.Inspection; -[assembly: ScriptingApiNamespace( - "InnoEditor.Inspection", - "Inno.Editor.Inspection", - ScriptingApiScope.Editor)] - +[assembly: ScriptingApiNamespace("InnoEditor.Inspection", "Inno.Editor.Inspection", ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(InspectionDrawer<>), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(IInspectionIconProvider<>), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(IPropertyDrawer), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(InspectionDrawContext), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetInspectionSelection), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(InspectionDrawerAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(PropertyDrawContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(PropertyDrawerAttribute), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/BooleanPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/BooleanPropertyDrawer.cs index 602e720cc..8040d4c5c 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/BooleanPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/BooleanPropertyDrawer.cs @@ -1,7 +1,6 @@ -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(bool))] internal sealed class BooleanPropertyDrawer : IPropertyDrawer { @@ -14,7 +13,7 @@ internal sealed class BooleanPropertyDrawer : IPropertyDrawer public void Draw(PropertyDrawContext context) { bool value = context.GetValue() is bool current && current; - if (EditorWidget.Checkbox($"##{context.path}", ref value, context.tooltip)) + if (EditorUI.current.widgets.Checkbox($"##{context.path}", ref value, context.tooltip)) { context.SetValue(value); } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/CollectionPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/CollectionPropertyDrawer.cs index fbc04ca41..7f5fca8b7 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/CollectionPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/CollectionPropertyDrawer.cs @@ -1,19 +1,16 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; - using Inno.Core.Serialization; using Inno.Editor.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(IEnumerable<>), useForChildren: true, priority: -20)] -[PropertyDrawer(typeof(IDictionary<,>), useForChildren: true, priority: 20)] -[PropertyDrawer(typeof(IReadOnlyDictionary<,>), useForChildren: true, priority: 20)] +[PropertyDrawer(typeof(IDictionary<, >), useForChildren: true, priority: 20)] +[PropertyDrawer(typeof(IReadOnlyDictionary<, >), useForChildren: true, priority: 20)] internal sealed class CollectionPropertyDrawer : IPropertyDrawer { /// @@ -36,7 +33,7 @@ public void Draw(PropertyDrawContext context) return; } - NativeImGui.TextUnformatted($"Unsupported collection: {context.propertyType.Name}"); + Ui.Text($"Unsupported collection: {context.propertyType.Name}"); } private static void DrawSequence( @@ -44,14 +41,12 @@ private static void DrawSequence( Type elementType ) { List values = EditorCollectionUtility.EnumerateSequence(context.GetValue()); - if (!NativeImGui.TreeNodeEx( - $"Count: {values.Count}##{context.path}_sequence", - (int)(ImGuiTreeNodeFlags.SpanAvailWidth))) + if (!Ui.TreeNode($"Count: {values.Count}##{context.path}_sequence", (ImGuiTreeNodeFlags.SpanAvailWidth))) { return; } - if (!context.isReadOnly && NativeImGui.SmallButton($"+##{context.path}_add")) + if (!context.isReadOnly && Ui.SmallButton($"+##{context.path}_add")) { values.Add(CreateDefault(elementType)); context.SetValue(EditorCollectionUtility.BuildSequence(context.propertyType, elementType, values)); @@ -65,19 +60,15 @@ Type elementType break; } - context.DrawChild( - $"Element {index}", - elementType, - () => EditorCollectionUtility.EnumerateSequence(context.GetValue())[index], - value => - { - List updated = EditorCollectionUtility.EnumerateSequence(context.GetValue()); - updated[index] = value; - context.SetValue(EditorCollectionUtility.BuildSequence(context.propertyType, elementType, updated)); - }); + context.DrawChild($"Element {index}", elementType, () => EditorCollectionUtility.EnumerateSequence(context.GetValue())[index], value => + { + List updated = EditorCollectionUtility.EnumerateSequence(context.GetValue()); + updated[index] = value; + context.SetValue(EditorCollectionUtility.BuildSequence(context.propertyType, elementType, updated)); + }); } - NativeImGui.TreePop(); + Ui.TreePop(); } private static bool DrawSequenceActions( @@ -87,7 +78,7 @@ private static bool DrawSequenceActions( int count ) { bool changed = false; - if (NativeImGui.SmallButton($"Up##{context.path}_{index}_up") && index > 0) + if (Ui.SmallButton($"Up##{context.path}_{index}_up") && index > 0) { List updated = EditorCollectionUtility.EnumerateSequence(context.GetValue()); (updated[index - 1], updated[index]) = (updated[index], updated[index - 1]); @@ -95,8 +86,8 @@ int count changed = true; } - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.SmallButton($"Down##{context.path}_{index}_down") && index < count - 1) + Ui.SameLine(0f, -1f); + if (Ui.SmallButton($"Down##{context.path}_{index}_down") && index < count - 1) { List updated = EditorCollectionUtility.EnumerateSequence(context.GetValue()); (updated[index + 1], updated[index]) = (updated[index], updated[index + 1]); @@ -104,8 +95,8 @@ int count changed = true; } - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.SmallButton($"Remove##{context.path}_{index}_remove")) + Ui.SameLine(0f, -1f); + if (Ui.SmallButton($"Remove##{context.path}_{index}_remove")) { List updated = EditorCollectionUtility.EnumerateSequence(context.GetValue()); updated.RemoveAt(index); @@ -122,24 +113,18 @@ private static void DrawMap( Type valueType ) { List> entries = EnumerateMap(context); - if (!NativeImGui.TreeNodeEx( - $"Count: {entries.Count}##{context.path}_map", - (int)(ImGuiTreeNodeFlags.SpanAvailWidth))) + if (!Ui.TreeNode($"Count: {entries.Count}##{context.path}_map", (ImGuiTreeNodeFlags.SpanAvailWidth))) { return; } - if (!context.isReadOnly && NativeImGui.SmallButton($"+##{context.path}_map_add")) + if (!context.isReadOnly && Ui.SmallButton($"+##{context.path}_map_add")) { object? defaultKey = CreateDefault(keyType); if (defaultKey is not null && !entries.Any(entry => Equals(entry.Key, defaultKey))) { entries.Add(new KeyValuePair(defaultKey, CreateDefault(valueType))); - context.SetValue(EditorCollectionUtility.BuildMap( - context.propertyType, - keyType, - valueType, - entries)); + context.SetValue(EditorCollectionUtility.BuildMap(context.propertyType, keyType, valueType, entries)); } } @@ -147,80 +132,55 @@ Type valueType { int index = i; string errorKey = $"map-error-{index}"; - context.DrawChild( - $"Key {index}", - keyType, - () => EnumerateMap(context)[index].Key, - key => + context.DrawChild($"Key {index}", keyType, () => EnumerateMap(context)[index].Key, key => + { + List> updated = EnumerateMap(context); + bool duplicate = updated.Where(( + _, + candidateIndex + ) => candidateIndex != index).Any(entry => Equals(entry.Key, key)); + if (duplicate) { - List> updated = EnumerateMap(context); - bool duplicate = updated.Where(( - _, - candidateIndex - ) => candidateIndex != index) - .Any(entry => Equals(entry.Key, key)); - if (duplicate) - { - context.SetTextState(errorKey, "Duplicate map keys are not allowed."); - return; - } - - context.ClearTextState(errorKey); - updated[index] = new KeyValuePair(key, updated[index].Value); - context.SetValue(EditorCollectionUtility.BuildMap( - context.propertyType, - keyType, - valueType, - updated)); - }); + context.SetTextState(errorKey, "Duplicate map keys are not allowed."); + return; + } + + context.ClearTextState(errorKey); + updated[index] = new KeyValuePair(key, updated[index].Value); + context.SetValue(EditorCollectionUtility.BuildMap(context.propertyType, keyType, valueType, updated)); + }); if (context.TryGetTextState(errorKey, out string? error)) { - EditorWidget.ColoredText(EditorPalette.error, error!); + EditorUI.current.widgets.ColoredText(EditorPalette.error, error!); } - context.DrawChild( - $"Value {index}", - valueType, - () => EnumerateMap(context)[index].Value, - value => - { - List> updated = EnumerateMap(context); - updated[index] = new KeyValuePair(updated[index].Key, value); - context.SetValue(EditorCollectionUtility.BuildMap( - context.propertyType, - keyType, - valueType, - updated)); - }); - - if (!context.isReadOnly && NativeImGui.SmallButton($"Remove##{context.path}_{index}_map_remove")) + context.DrawChild($"Value {index}", valueType, () => EnumerateMap(context)[index].Value, value => + { + List> updated = EnumerateMap(context); + updated[index] = new KeyValuePair(updated[index].Key, value); + context.SetValue(EditorCollectionUtility.BuildMap(context.propertyType, keyType, valueType, updated)); + }); + if (!context.isReadOnly && Ui.SmallButton($"Remove##{context.path}_{index}_map_remove")) { List> updated = EnumerateMap(context); updated.RemoveAt(index); context.ClearTextState(errorKey); - context.SetValue(EditorCollectionUtility.BuildMap( - context.propertyType, - keyType, - valueType, - updated)); + context.SetValue(EditorCollectionUtility.BuildMap(context.propertyType, keyType, valueType, updated)); break; } } - NativeImGui.TreePop(); + Ui.TreePop(); } private static List> EnumerateMap(PropertyDrawContext context) { - if (EditorCollectionUtility.TryEnumerateMap( - context.GetValue(), - context.propertyType, - out List> entries)) + if (EditorCollectionUtility.TryEnumerateMap(context.GetValue(), context.propertyType, out List> entries)) { return entries; } - return []; + return[]; } private static object? CreateDefault(Type type) diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/EnumPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/EnumPropertyDrawer.cs index 238a34b5b..e6da674b5 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/EnumPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/EnumPropertyDrawer.cs @@ -1,12 +1,10 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(Enum), useForChildren: true, priority: 100)] internal sealed class EnumPropertyDrawer : IPropertyDrawer { @@ -27,7 +25,7 @@ public void Draw(PropertyDrawContext context) } string preview = Enum.GetName(enumType, value) ?? value.ToString() ?? "Unknown"; - if (!EditorWidget.BeginBoundedCombo($"##{context.path}", preview)) + if (!EditorUI.current.widgets.BeginBoundedCombo($"##{context.path}", preview)) { return; } @@ -37,13 +35,13 @@ public void Draw(PropertyDrawContext context) { object candidate = values.GetValue(i)!; string name = Enum.GetName(enumType, candidate) ?? candidate.ToString()!; - if (NativeImGui.Selectable(name, Equals(candidate, value))) + if (Ui.Selectable(name, Equals(candidate, value))) { context.SetValue(candidate); } } - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } private static void DrawFlags( @@ -53,7 +51,7 @@ object value ) { ulong currentBits = ToBits(enumType, value); string preview = value.ToString() ?? currentBits.ToString(); - if (!EditorWidget.BeginBoundedCombo($"##{context.path}", preview)) + if (!EditorUI.current.widgets.BeginBoundedCombo($"##{context.path}", preview)) { return; } @@ -63,24 +61,18 @@ object value { object candidate = values.GetValue(i)!; ulong candidateBits = ToBits(enumType, candidate); - bool selected = candidateBits == 0 - ? currentBits == 0 - : (currentBits & candidateBits) == candidateBits; + bool selected = candidateBits == 0 ? currentBits == 0 : (currentBits & candidateBits) == candidateBits; string name = Enum.GetName(enumType, candidate) ?? candidate.ToString()!; - if (!NativeImGui.Selectable(name, selected, (int)(ImGuiSelectableFlags.NoAutoClosePopups))) + if (!Ui.Selectable(name, selected, (ImGuiSelectableFlags.NoAutoClosePopups))) { continue; } - currentBits = candidateBits == 0 - ? 0 - : selected - ? currentBits & ~candidateBits - : currentBits | candidateBits; + currentBits = candidateBits == 0 ? 0 : selected ? currentBits & ~candidateBits : currentBits | candidateBits; context.SetValue(Enum.ToObject(enumType, currentBits)); } - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } private static ulong ToBits( @@ -98,7 +90,6 @@ object value TypeCode.UInt16 => Convert.ToUInt16(value), TypeCode.UInt32 => Convert.ToUInt32(value), TypeCode.UInt64 => Convert.ToUInt64(value), - _ => throw new InvalidOperationException($"Unsupported enum underlying type '{underlyingType.FullName}'.") - }; + _ => throw new InvalidOperationException($"Unsupported enum underlying type '{underlyingType.FullName}'.")}; } } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/GuidPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/GuidPropertyDrawer.cs index c5b172f44..3ffb93f87 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/GuidPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/GuidPropertyDrawer.cs @@ -1,15 +1,13 @@ +using Inno.Editor.ImGui; using System; using EditorImGui = Inno.Editor.ImGui.ImGui; -using Inno.Native.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(Guid))] internal sealed class GuidPropertyDrawer : IPropertyDrawer { private const int C_BUFFER_SIZE = 64; private const string C_TEXT_STATE = "guid"; - /// /// Renders the value presentation for the current editor frame. /// @@ -19,15 +17,8 @@ internal sealed class GuidPropertyDrawer : IPropertyDrawer public void Draw(PropertyDrawContext context) { Guid value = context.GetValue() is Guid current ? current : Guid.Empty; - string text = context.TryGetTextState(C_TEXT_STATE, out string? editing) - ? editing! - : value.ToString("D"); - if (EditorImGui.InputText( - $"##{context.path}", - ref text, - C_BUFFER_SIZE, - ImGuiInputTextFlags.EnterReturnsTrue) && - Guid.TryParse(text, out Guid parsed)) + string text = context.TryGetTextState(C_TEXT_STATE, out string? editing) ? editing! : value.ToString("D"); + if (EditorImGui.InputText($"##{context.path}", ref text, C_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue) && Guid.TryParse(text, out Guid parsed)) { context.SetValue(parsed); context.ClearTextState(C_TEXT_STATE); diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs index f307cbba5..052087445 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/IInspectionPropertyEditService.cs @@ -1,7 +1,6 @@ using System; namespace Inno.Editor.Inspection; - /// /// Applies one root serialized-property mutation through the history mechanism owned by a feature. /// @@ -22,21 +21,29 @@ public interface IInspectionPropertyEditService /// /// The user-facing name for the resulting history entry. /// + /// + /// The current shared gesture key, or null for a discrete edit. + /// + /// + /// An optional public value component; null replaces the complete root value. + /// /// /// when the value changed and the owning feature recorded the edit; /// otherwise, . /// /// - /// Thrown when or is + /// Thrown when or is /// . /// /// - /// Thrown when or is empty. + /// Thrown when or is empty. /// bool ChangeProperty( object owner, string propertyName, Action mutation, - string historyName + string historyName, + string? mergeKey, + string? componentName ); } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs index 049b9792a..4944f06d2 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs @@ -1,23 +1,20 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - using Inno.Core.Mathematics; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using EditorImGui = Inno.Editor.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; using NumericsVector4 = System.Numerics.Vector4; namespace Inno.Editor.Inspection; - internal static unsafe class ComponentFieldDrawer { internal static float GetWidth(int count) { - float spacing = NativeImGui.Raw.GetStyle()->ItemSpacing.X; - return MathF.Max( - 1f, - (NativeImGui.GetContentRegionAvail().X - spacing * (count - 1)) / count); + float spacing = UiDrawing.GetLayout().itemSpacing.X; + return MathF.Max(1f, (Ui.GetContentRegionAvailable().X - spacing * (count - 1)) / count); } internal static bool Float( @@ -26,7 +23,7 @@ internal static bool Float( ref float value, float width ) { - return EditorWidget.AxisDragFloat(path, label, ref value, width); + return EditorUI.current.widgets.AxisDragFloat(path, label, ref value, width); } internal static bool Int( @@ -35,10 +32,10 @@ internal static bool Int( ref int value, float width ) { - return EditorWidget.AxisDragInt(path, label, ref value, width); + return EditorUI.current.widgets.AxisDragInt(path, label, ref value, width); } - internal static void Next() => NativeImGui.SameLine(0f, -1f); + internal static void Next() => Ui.SameLine(0f, -1f); } [PropertyDrawer(typeof(Vector2))] @@ -54,13 +51,11 @@ public void Draw(PropertyDrawContext context) { Vector2 value = context.GetValue() is Vector2 current ? current : default; float width = ComponentFieldDrawer.GetWidth(2); - bool changed = ComponentFieldDrawer.Float(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Float(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); } } @@ -77,15 +72,14 @@ public void Draw(PropertyDrawContext context) { Vector3 value = context.GetValue() is Vector3 current ? current : default; float width = ComponentFieldDrawer.GetWidth(3); - bool changed = ComponentFieldDrawer.Float(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Float(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Z", ref value.z, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Float(context.path, "Z", ref value.z, width)) + context.SetValue(value, "z"); } } @@ -102,17 +96,17 @@ public void Draw(PropertyDrawContext context) { Vector4 value = context.GetValue() is Vector4 current ? current : default; float width = ComponentFieldDrawer.GetWidth(4); - bool changed = ComponentFieldDrawer.Float(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Float(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Z", ref value.z, width); + if (ComponentFieldDrawer.Float(context.path, "Z", ref value.z, width)) + context.SetValue(value, "z"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "W", ref value.w, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Float(context.path, "W", ref value.w, width)) + context.SetValue(value, "w"); } } @@ -129,13 +123,11 @@ public void Draw(PropertyDrawContext context) { Vector2Int value = context.GetValue() is Vector2Int current ? current : default; float width = ComponentFieldDrawer.GetWidth(2); - bool changed = ComponentFieldDrawer.Int(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Int(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); } } @@ -152,15 +144,14 @@ public void Draw(PropertyDrawContext context) { Vector3Int value = context.GetValue() is Vector3Int current ? current : default; float width = ComponentFieldDrawer.GetWidth(3); - bool changed = ComponentFieldDrawer.Int(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Int(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Z", ref value.z, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Int(context.path, "Z", ref value.z, width)) + context.SetValue(value, "z"); } } @@ -177,17 +168,17 @@ public void Draw(PropertyDrawContext context) { Vector4Int value = context.GetValue() is Vector4Int current ? current : default; float width = ComponentFieldDrawer.GetWidth(4); - bool changed = ComponentFieldDrawer.Int(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Int(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Z", ref value.z, width); + if (ComponentFieldDrawer.Int(context.path, "Z", ref value.z, width)) + context.SetValue(value, "z"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "W", ref value.w, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Int(context.path, "W", ref value.w, width)) + context.SetValue(value, "w"); } } @@ -204,17 +195,17 @@ public void Draw(PropertyDrawContext context) { Rect value = context.GetValue() is Rect current ? current : default; float width = ComponentFieldDrawer.GetWidth(4); - bool changed = ComponentFieldDrawer.Float(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Float(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Float(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "W", ref value.width, width); + if (ComponentFieldDrawer.Float(context.path, "W", ref value.width, width)) + context.SetValue(value, "width"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "H", ref value.height, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Float(context.path, "H", ref value.height, width)) + context.SetValue(value, "height"); } } @@ -231,17 +222,17 @@ public void Draw(PropertyDrawContext context) { RectInt value = context.GetValue() is RectInt current ? current : default; float width = ComponentFieldDrawer.GetWidth(4); - bool changed = ComponentFieldDrawer.Int(context.path, "X", ref value.x, width); + if (ComponentFieldDrawer.Int(context.path, "X", ref value.x, width)) + context.SetValue(value, "x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width); + if (ComponentFieldDrawer.Int(context.path, "Y", ref value.y, width)) + context.SetValue(value, "y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "W", ref value.width, width); + if (ComponentFieldDrawer.Int(context.path, "W", ref value.width, width)) + context.SetValue(value, "width"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Int(context.path, "H", ref value.height, width); - if (changed) - { - context.SetValue(value); - } + if (ComponentFieldDrawer.Int(context.path, "H", ref value.height, width)) + context.SetValue(value, "height"); } } @@ -258,10 +249,7 @@ public void Draw(PropertyDrawContext context) { Color value = context.GetValue() is Color current ? current : default; var nativeValue = new NumericsVector4(value.r, value.g, value.b, value.a); - var flags = context.hdrColor - ? Inno.Native.ImGui.ImGuiColorEditFlags.Hdr | Inno.Native.ImGui.ImGuiColorEditFlags.Float - | Inno.Native.ImGui.ImGuiColorEditFlags.InputRgb | Inno.Native.ImGui.ImGuiColorEditFlags.DisplayRgb - : Inno.Native.ImGui.ImGuiColorEditFlags.None; + var flags = context.hdrColor ? Inno.Editor.ImGui.ImGuiColorEditFlags.Hdr | Inno.Editor.ImGui.ImGuiColorEditFlags.Float | Inno.Editor.ImGui.ImGuiColorEditFlags.InputRgb | Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayRgb : Inno.Editor.ImGui.ImGuiColorEditFlags.None; if (EditorImGui.ColorEditLinear4($"##{context.path}", ref nativeValue, flags)) { context.SetValue(new Color(nativeValue.X, nativeValue.Y, nativeValue.Z, nativeValue.W)); @@ -283,14 +271,13 @@ public void Draw(PropertyDrawContext context) Quaternion value = context.GetValue() is Quaternion current ? current : Quaternion.identity; Vector3 eulerDegrees = value.ToEulerAnglesXYZDegrees(); float width = ComponentFieldDrawer.GetWidth(3); - bool changed = ComponentFieldDrawer.Float(context.path, "X", ref eulerDegrees.x, width); + if (ComponentFieldDrawer.Float(context.path, "X", ref eulerDegrees.x, width)) + context.SetValue(Quaternion.FromEulerAnglesXYZDegrees(eulerDegrees).normalized, "euler.x"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Y", ref eulerDegrees.y, width); + if (ComponentFieldDrawer.Float(context.path, "Y", ref eulerDegrees.y, width)) + context.SetValue(Quaternion.FromEulerAnglesXYZDegrees(eulerDegrees).normalized, "euler.y"); ComponentFieldDrawer.Next(); - changed |= ComponentFieldDrawer.Float(context.path, "Z", ref eulerDegrees.z, width); - if (changed) - { - context.SetValue(Quaternion.FromEulerAnglesXYZDegrees(eulerDegrees).normalized); - } + if (ComponentFieldDrawer.Float(context.path, "Z", ref eulerDegrees.z, width)) + context.SetValue(Quaternion.FromEulerAnglesXYZDegrees(eulerDegrees).normalized, "euler.z"); } } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NullablePropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NullablePropertyDrawer.cs index f4cea9c51..950b3f7c9 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NullablePropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NullablePropertyDrawer.cs @@ -1,10 +1,8 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(Nullable<>))] internal sealed class NullablePropertyDrawer : IPropertyDrawer { @@ -16,15 +14,11 @@ internal sealed class NullablePropertyDrawer : IPropertyDrawer /// public void Draw(PropertyDrawContext context) { - Type underlyingType = Nullable.GetUnderlyingType(context.propertyType) - ?? throw new InvalidOperationException("Nullable drawer requires a nullable property type."); + Type underlyingType = Nullable.GetUnderlyingType(context.propertyType) ?? throw new InvalidOperationException("Nullable drawer requires a nullable property type."); object? value = context.GetValue(); bool hasValue = value is not null; bool previousHasValue = hasValue; - _ = EditorWidget.Checkbox( - $"##{context.path}_has_value", - ref hasValue, - context.tooltip); + _ = EditorUI.current.widgets.Checkbox($"##{context.path}_has_value", ref hasValue, context.tooltip); if (hasValue != previousHasValue) { value = hasValue ? Activator.CreateInstance(underlyingType) : null; @@ -33,16 +27,12 @@ public void Draw(PropertyDrawContext context) if (!hasValue) { - NativeImGui.SameLine(0f, -1f); - NativeImGui.TextUnformatted("Null"); + Ui.SameLine(0f, -1f); + Ui.Text("Null"); return; } - NativeImGui.SameLine(0f, -1f); - context.DrawInlineChild( - "Value", - underlyingType, - context.GetValue, - context.SetValue); + Ui.SameLine(0f, -1f); + context.DrawInlineChild("Value", underlyingType, context.GetValue, value => context.SetValue(value)); } } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs index 3c0b08ff7..7232b5132 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs @@ -1,13 +1,11 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Globalization; using EditorImGui = Inno.Editor.ImGui.ImGui; - -using Inno.Native.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(byte))] [PropertyDrawer(typeof(sbyte))] [PropertyDrawer(typeof(short))] @@ -23,7 +21,6 @@ internal sealed class NumericPropertyDrawer : IPropertyDrawer { private const int C_BUFFER_SIZE = 128; private const string C_TEXT_STATE = "numeric"; - /// /// Renders the value presentation for the current editor frame. /// @@ -37,13 +34,21 @@ public void Draw(PropertyDrawContext context) if (type == typeof(int)) { int value = rawValue is int current ? current : 0; - bool changed = context.minimum is double minimum && context.maximum is double maximum - ? NativeImGui.SliderInt( - $"##{context.path}", - ref value, - (int)Math.Clamp(Math.Ceiling(minimum), int.MinValue, int.MaxValue), - (int)Math.Clamp(Math.Floor(maximum), int.MinValue, int.MaxValue)) - : NativeImGui.DragInt($"##{context.path}", ref value, 1f, 0, 0, "%d", 0); + bool changed = context.minimum is double minimum + && context.maximum is double maximum ? Ui.SliderInt( + $"##{context.path}", + ref value, + (int)Math.Clamp(Math.Ceiling(minimum), int.MinValue, int.MaxValue), + (int)Math.Clamp(Math.Floor(maximum), int.MinValue, int.MaxValue) + ) : Ui.DragInt( + $"##{context.path}", + ref value, + 1f, + 0, + 0, + "%d", + 0 + ); if (changed) { context.SetValue(value); @@ -55,13 +60,17 @@ public void Draw(PropertyDrawContext context) if (type == typeof(float)) { float value = rawValue is float current ? current : 0f; - bool changed = context.minimum is double minimum && context.maximum is double maximum - ? EditorWidget.CompactSliderFloat( - $"##{context.path}", - ref value, - (float)minimum, - (float)maximum) - : EditorWidget.CompactDragFloat($"##{context.path}", ref value, 0.1f); + bool changed = context.minimum is double minimum + && context.maximum is double maximum ? EditorUI.current.widgets.CompactSliderFloat( + $"##{context.path}", + ref value, + (float)minimum, + (float)maximum + ) : EditorUI.current.widgets.CompactDragFloat( + $"##{context.path}", + ref value, + 0.1f + ); if (changed) { context.SetValue(value); @@ -72,12 +81,7 @@ public void Draw(PropertyDrawContext context) _ = context.TryGetTextState(C_TEXT_STATE, out string? text); text ??= Convert.ToString(rawValue, CultureInfo.InvariantCulture) ?? "0"; - - if (EditorImGui.InputText( - $"##{context.path}", - ref text, - C_BUFFER_SIZE, - ImGuiInputTextFlags.EnterReturnsTrue)) + if (EditorImGui.InputText($"##{context.path}", ref text, C_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue)) { if (TryConvert(text, type, out object? converted)) { diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs index 65249476a..7e9f742dd 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/PropertyDrawContext.cs @@ -3,9 +3,9 @@ using Inno.Core.Serialization; using Inno.Editor.Core; using Inno.Editor.Interactions; +using Inno.Editor.ImGui; namespace Inno.Editor.Inspection; - /// /// Encapsulates one editable property path and its drawing services. /// @@ -18,62 +18,50 @@ public sealed class PropertyDrawContext private readonly object m_owner; private readonly string m_rootPropertyName; private readonly bool m_isReadOnly; - /// /// Gets the shared editor context. /// public EditorContext editorContext { get; } - /// /// Gets the active editor interaction entry point. /// public EditorInteractions interactions { get; } - /// /// Gets the stable control path. /// public string path { get; } - /// /// Gets the display label. /// public string label { get; } - /// /// Gets the declared property type. /// public Type propertyType { get; } - /// /// Gets the serialization visibility flags. /// public PropertyVisibility visibility { get; } - /// /// Gets whether assignments are disabled. /// public bool isReadOnly => m_isReadOnly; - /// /// Gets the optional inclusive numeric lower bound supplied by Inspector metadata. /// public double? minimum { get; } - /// /// Gets the optional inclusive numeric upper bound supplied by Inspector metadata. /// public double? maximum { get; } - /// /// Gets optional hover help supplied by Inspector metadata. /// public string? tooltip { get; } - /// /// Gets whether Color values are unclamped linear floating-point channels. /// public bool hdrColor { get; } - internal object owner => m_owner; internal PropertyDrawContext( @@ -99,9 +87,10 @@ internal PropertyDrawContext( this.interactions = interactions ?? throw new ArgumentNullException(nameof(interactions)); m_edits = edits ?? throw new ArgumentNullException(nameof(edits)); m_owner = owner ?? throw new ArgumentNullException(nameof(owner)); - m_rootPropertyName = string.IsNullOrWhiteSpace(rootPropertyName) - ? throw new ArgumentException("The root property name is required.", nameof(rootPropertyName)) - : rootPropertyName; + m_rootPropertyName = string.IsNullOrWhiteSpace(rootPropertyName) ? throw new ArgumentException( + "The root property name is required.", + nameof(rootPropertyName) + ) : rootPropertyName; this.path = path; this.label = label; this.propertyType = propertyType; @@ -123,7 +112,6 @@ internal PropertyDrawContext( /// The latest value returned by the serialized property's getter. /// public object? GetValue() => m_getter(); - /// /// Tries to read text-edit state scoped to this renderer, inspected owner, and property path. /// @@ -140,7 +128,6 @@ public bool TryGetTextState( string key, out string? value ) => m_renderer.TryGetTextState(m_owner, path, key, out value); - /// /// Stores text-edit state scoped to this renderer, inspected owner, and property path. /// @@ -154,7 +141,6 @@ public void SetTextState( string key, string value ) => m_renderer.SetTextState(m_owner, path, key, value); - /// /// Removes text-edit state for this renderer, inspected owner, and property path. /// @@ -162,24 +148,34 @@ string value /// Drawer-local state identity within the current property path. /// public void ClearTextState(string key) => m_renderer.ClearTextState(m_owner, path, key); - /// /// Assigns a value when the property is writable. /// /// /// New value. /// - public void SetValue(object? value) - { + /// + /// An optional public scalar component, or an Euler axis such as euler.x, for precise batch value edits. + /// + public void SetValue( + object? value, + string? componentName = null + ) { if (isReadOnly) return; - if (Equals(m_getter(), value)) + EditorUiSession ui = EditorUiSession.current; + if (Equals(m_getter(), value) && !ui.widgets.isMixedValue) return; - _ = m_edits.ChangeProperty( - m_owner, - m_rootPropertyName, - () => m_setter(value), - $"Change {label}"); + ui.widgets.BeginPropertyEdit(interactions.history, path, $"Change {label}"); + try + { + _ = m_edits.ChangeProperty(m_owner, m_rootPropertyName, () => m_setter(value), $"Change {label}", ui.widgets.editMergeKey, componentName); + } + catch + { + ui.widgets.CancelEdits(); + throw; + } } /// @@ -207,23 +203,8 @@ public void DrawChild( Action setter, bool readOnly = false ) { - PropertyVisibility childVisibility = readOnly || isReadOnly - ? PropertyVisibility.Readonly - : PropertyVisibility.Show; - m_renderer.Draw( - editorContext, - m_owner, - m_owner, - null, - null, - m_rootPropertyName, - $"{path}.{childName}", - childName, - childType, - childVisibility, - getter, - setter, - m_edits); + PropertyVisibility childVisibility = readOnly || isReadOnly ? PropertyVisibility.Readonly : PropertyVisibility.Show; + m_renderer.Draw(editorContext, m_owner, m_owner, null, null, m_rootPropertyName, $"{path}.{childName}", childName, childType, childVisibility, getter, setter, m_edits); } /// @@ -233,7 +214,6 @@ public void DrawChild( /// Nested property. /// public void DrawChild(SerializedProperty property) => DrawChild(GetValue() ?? m_owner, property); - /// /// Draws a nested serialized property with presentation metadata resolved from its immediate owner. /// @@ -262,7 +242,8 @@ SerializedProperty property isReadOnly || !property.canWrite ? PropertyVisibility.Readonly : property.visibility, property.GetValue, property.SetValue, - m_edits); + m_edits + ); } internal void DrawChildMember( @@ -275,9 +256,7 @@ bool readOnly ) { ArgumentNullException.ThrowIfNull(metadataOwner); ArgumentNullException.ThrowIfNull(member); - PropertyVisibility childVisibility = readOnly || isReadOnly - ? PropertyVisibility.Readonly - : PropertyVisibility.Show; + PropertyVisibility childVisibility = readOnly || isReadOnly ? PropertyVisibility.Readonly : PropertyVisibility.Show; m_renderer.Draw( editorContext, m_owner, @@ -291,7 +270,8 @@ bool readOnly childVisibility, getter, setter, - m_edits); + m_edits + ); } /// @@ -319,21 +299,7 @@ public void DrawInlineChild( Action setter, bool readOnly = false ) { - PropertyVisibility childVisibility = readOnly || isReadOnly - ? PropertyVisibility.Readonly - : PropertyVisibility.Show; - m_renderer.DrawInline( - editorContext, - m_owner, - m_owner, - null, - m_rootPropertyName, - $"{path}.{childName}", - childName, - childType, - childVisibility, - getter, - setter, - m_edits); + PropertyVisibility childVisibility = readOnly || isReadOnly ? PropertyVisibility.Readonly : PropertyVisibility.Show; + m_renderer.DrawInline(editorContext, m_owner, m_owner, null, m_rootPropertyName, $"{path}.{childName}", childName, childType, childVisibility, getter, setter, m_edits); } } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializableObjectPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializableObjectPropertyDrawer.cs index d2868a39d..c5d365de8 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializableObjectPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializableObjectPropertyDrawer.cs @@ -1,21 +1,18 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Extensibility.Types; using Inno.Core.Serialization; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(ISerializable), useForChildren: true, priority: 20)] internal sealed class SerializableObjectPropertyDrawer : IPropertyDrawer { private readonly SerializationRegistry m_serialization; private readonly TypeCacheSnapshot m_types; - internal SerializableObjectPropertyDrawer( TypeCacheSnapshot types, SerializationRegistry serialization @@ -38,26 +35,25 @@ public void Draw(PropertyDrawContext context) value = context.GetValue(); if (value is not ISerializable serializable) { - NativeImGui.TextUnformatted("Null"); + Ui.Text("Null"); return; } string header = $"{value.GetType().Name}##{context.path}_foldout"; - if (!NativeImGui.TreeNodeEx(header, (int)(ImGuiTreeNodeFlags.SpanAvailWidth))) + if (!Ui.TreeNode(header, (ImGuiTreeNodeFlags.SpanAvailWidth))) { return; } IReadOnlyList properties = m_serialization.GetProperties(serializable); - EditorWidget.SectionLayout(() => + EditorUI.current.widgets.SectionLayout(() => { for (int i = 0; i < properties.Count; i++) { context.DrawChild(serializable, properties[i]); } }); - - NativeImGui.TreePop(); + Ui.TreePop(); } private void DrawTypeSelector( @@ -65,14 +61,12 @@ private void DrawTypeSelector( Type runtimeType, object? value ) { - if (!EditorWidget.BeginBoundedCombo( - $"##{context.path}_runtime_type", - value is null ? "Null" : runtimeType.Name)) + if (!EditorUI.current.widgets.BeginBoundedCombo($"##{context.path}_runtime_type", value is null ? "Null" : runtimeType.Name)) { return; } - if (NativeImGui.Selectable("Null", value is null)) + if (Ui.Selectable("Null", value is null)) { context.SetValue(null); } @@ -86,12 +80,12 @@ private void DrawTypeSelector( continue; } - if (NativeImGui.Selectable(candidate.Name, candidate == runtimeType && value is not null)) + if (Ui.Selectable(candidate.Name, candidate == runtimeType && value is not null)) { context.SetValue(Activator.CreateInstance(candidate, nonPublic: true)); } } - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs index cf324c94b..1300a96e0 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/SerializedPropertyRenderer.cs @@ -1,3 +1,4 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Reflection; @@ -9,12 +10,9 @@ using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Interactions; -using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.Inspection; - /// /// Resolves drawers and renders serialized property paths with isolated error handling. /// @@ -27,7 +25,6 @@ public sealed class SerializedPropertyRenderer private readonly EditorInteractions m_interactions; private readonly IInspectionPropertyEditService m_edits; private readonly Logger m_logger; - /// /// Creates a serialized property renderer over one drawer registry and feature-owned edit service. /// @@ -47,7 +44,7 @@ public sealed class SerializedPropertyRenderer /// The host-owned log router used to report isolated drawer failures. /// /// - /// Thrown when , , or + /// Thrown when , , or /// is . /// [ScriptingApiIgnore] @@ -102,7 +99,8 @@ SerializedProperty property property.propertyType, property.visibility, property.GetValue, - property.SetValue); + property.SetValue + ); } /// @@ -163,8 +161,25 @@ public void DrawValue( double? maximum = null ) { ArgumentNullException.ThrowIfNull(edits); - Draw(editorContext, owner, owner, null, null, path, path, label, propertyType, - readOnly ? PropertyVisibility.Readonly : PropertyVisibility.Show, getter, setter, edits, hdrColor, tooltip, minimum, maximum); + Draw( + editorContext, + owner, + owner, + null, + null, + path, + path, + label, + propertyType, + readOnly ? PropertyVisibility.Readonly : PropertyVisibility.Show, + getter, + setter, + edits, + hdrColor, + tooltip, + minimum, + maximum + ); } /// @@ -205,10 +220,49 @@ public void DrawDraftProperty( ArgumentNullException.ThrowIfNull(stateOwner); ArgumentNullException.ThrowIfNull(property); ArgumentNullException.ThrowIfNull(edits); - Draw(editorContext, stateOwner, valueOwner, InspectorMemberMetadata.Resolve(valueOwner.GetType(), property.name), - property, property.name, ownerPath + "." + property.name, property.name, property.propertyType, + Draw( + editorContext, + stateOwner, + valueOwner, + InspectorMemberMetadata.Resolve(valueOwner.GetType(), property.name), + property, + property.name, + ownerPath + "." + property.name, + property.name, + property.propertyType, readOnly ? property.visibility & ~PropertyVisibility.RuntimeSet : property.visibility, - property.GetValue, property.SetValue, edits); + property.GetValue, + property.SetValue, + edits + ); + } + + /// + /// Evaluates the same current-generation presentation attributes used by drawing without creating any UI state. + /// + /// + /// The current object declaring the property's conditional metadata. + /// + /// + /// The root serialized property to inspect. + /// + /// + /// Visibility and editability for this exact owner; callers must combine all selected owners before a batch edit. + /// + [ScriptingApiIgnore] + public (bool isVisible, bool isReadOnly) GetPropertyAvailability( + object owner, + SerializedProperty property + ) { + ArgumentNullException.ThrowIfNull(owner); + ArgumentNullException.ThrowIfNull(property); + bool readOnly = (property.visibility & PropertyVisibility.RuntimeSet) == 0; + MemberInfo? member = InspectorMemberMetadata.Resolve(owner.GetType(), property.name); + if (member is null) + return (true, readOnly); + var context = new InspectorAttributeDrawContext(owner, member, property, property.name, property.name, readOnly); + m_attributes.Update(context, InspectorMemberMetadata.GetAttributes(member)); + return (context.isVisible, context.isReadOnly); } internal void Draw( @@ -230,39 +284,32 @@ internal void Draw( double? minimum = null, double? maximum = null ) { - string displayLabel = EditorWidget.NicifyName(label); + string displayLabel = EditorDisplayNames.Nicify(label); bool isReadOnly = (visibility & PropertyVisibility.RuntimeSet) == 0; Attribute[] attributes = member is null ? [] : InspectorMemberMetadata.GetAttributes(member); bool beginsSection = Array.Exists(attributes, static attribute => attribute is HeaderAttribute); InspectorAttributeDrawContext? attributeContext = null; if (member is not null && attributes.Length > 0) { - attributeContext = new InspectorAttributeDrawContext( - metadataOwner, - member, - property, - path, - displayLabel, - isReadOnly); + attributeContext = new InspectorAttributeDrawContext(metadataOwner, member, property, path, displayLabel, isReadOnly); m_attributes.Update(attributeContext, attributes); if (!attributeContext.isVisible) return; displayLabel = attributeContext.label; isReadOnly = attributeContext.isReadOnly; - if (!beginsSection && !EditorWidget.isSectionContentVisible) + if (!beginsSection && !EditorUI.current.widgets.isSectionContentVisible) return; m_attributes.DrawBefore(attributeContext, attributes); - if (!EditorWidget.isSectionContentVisible) + if (!EditorUI.current.widgets.isSectionContentVisible) return; } - else if (!EditorWidget.isSectionContentVisible) + else if (!EditorUI.current.widgets.isSectionContentVisible) { return; } - if (!beginsSection && !EditorWidget.EnsureSection()) + if (!beginsSection && !EditorUI.current.widgets.EnsureSection()) return; - var context = new PropertyDrawContext( editorContext, m_interactions, @@ -279,13 +326,10 @@ internal void Draw( attributeContext?.tooltip ?? tooltip, getter, setter, - this, hdrColor); - - EditorWidget.PropertyRow( - path, - context.label, - () => DrawContent(context), - tooltip: attributeContext?.tooltip ?? tooltip); + this, + hdrColor + ); + EditorUI.current.widgets.PropertyRow(path, context.label, () => DrawContent(context), tooltip: attributeContext?.tooltip ?? tooltip); if (attributeContext is not null) m_attributes.DrawAfter(attributeContext, attributes); } @@ -311,7 +355,7 @@ internal void DrawInline( owner, rootPropertyName, path, - EditorWidget.NicifyName(label), + EditorDisplayNames.Nicify(label), propertyType, visibility, (visibility & PropertyVisibility.RuntimeSet) == 0, @@ -320,7 +364,8 @@ internal void DrawInline( null, getter, setter, - this); + this + ); DrawContent(context); } @@ -330,20 +375,17 @@ private void DrawContent(PropertyDrawContext context) try { IPropertyDrawer drawer = m_drawers.Resolve(context.propertyType); - EditorWidget.Disabled(context.isReadOnly, () => drawer.Draw(context)); + EditorUI.current.widgets.Disabled(context.isReadOnly, () => drawer.Draw(context)); + EditorUI.current.widgets.CompletePropertyEdit(context.path); failureStates.Remove(context.path); } catch (Exception exception) { - EditorWidget.ColoredText(EditorPalette.error, $"Error: {exception.Message}"); + EditorUI.current.widgets.ColoredText(EditorPalette.error, $"Error: {exception.Message}"); string failureState = $"{exception.GetType().FullName}|{exception.Message}"; - if (!failureStates.TryGetValue(context.path, out string? previous) || - !string.Equals(previous, failureState, StringComparison.Ordinal)) + if (!failureStates.TryGetValue(context.path, out string? previous) || !string.Equals(previous, failureState, StringComparison.Ordinal)) { - m_logger.Write( - LogLevel.Error, - "Inspector failed to draw property '{0}': {1}", - [context.path, exception]); + m_logger.Write(LogLevel.Error, "Inspector failed to draw property '{0}': {1}", [context.path, exception]); failureStates[context.path] = failureState; } } @@ -357,8 +399,7 @@ out string? value ) { ArgumentNullException.ThrowIfNull(owner); value = null; - return m_textStates.TryGetValue(owner, out Dictionary? states) - && states.TryGetValue(CreateStateKey(path, key), out value); + return m_textStates.TryGetValue(owner, out Dictionary? states) && states.TryGetValue(CreateStateKey(path, key), out value); } internal void SetTextState( diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StringPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StringPropertyDrawer.cs index 5d0e86dcc..83697be97 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StringPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StringPropertyDrawer.cs @@ -1,13 +1,11 @@ +using Inno.Editor.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using Inno.Native.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(string))] internal sealed class StringPropertyDrawer : IPropertyDrawer { private const int C_BUFFER_SIZE = 1024; - /// /// Renders the value presentation for the current editor frame. /// diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StructPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StructPropertyDrawer.cs index 800b1c58b..43b0f0361 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StructPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/StructPropertyDrawer.cs @@ -1,21 +1,18 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; - using Inno.Core.Serialization; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; - [PropertyDrawer(typeof(ValueType), useForChildren: true, priority: -100)] internal sealed class StructPropertyDrawer : IPropertyDrawer { private static readonly ConditionalWeakTable S_MEMBER_CACHE = new(); - /// /// Renders the value presentation for the current editor frame. /// @@ -28,77 +25,61 @@ public void Draw(PropertyDrawContext context) MemberInfo[] members = GetMembers(context.propertyType); if (members.Length == 0) { - NativeImGui.TextUnformatted(boxedValue.ToString() ?? context.propertyType.Name); + Ui.Text(boxedValue.ToString() ?? context.propertyType.Name); return; } - if (!NativeImGui.TreeNodeEx($"{context.propertyType.Name}##{context.path}", (int)(ImGuiTreeNodeFlags.SpanAvailWidth))) + if (!Ui.TreeNode($"{context.propertyType.Name}##{context.path}", (ImGuiTreeNodeFlags.SpanAvailWidth))) { return; } - EditorWidget.SectionLayout(() => + EditorUI.current.widgets.SectionLayout(() => { for (int i = 0; i < members.Length; i++) { MemberInfo member = members[i]; Type memberType = GetMemberType(member); - bool memberReadOnly = !CanWrite(member) || - (GetVisibility(member) & PropertyVisibility.RuntimeSet) == 0; - context.DrawChildMember( - context.GetValue() ?? boxedValue, - member, - memberType, - () => GetMemberValue(member, context.GetValue() ?? boxedValue), - value => - { - object updated = context.GetValue() ?? Activator.CreateInstance(context.propertyType)!; - SetMemberValue(member, updated, value); - context.SetValue(updated); - }, - memberReadOnly); + bool memberReadOnly = !CanWrite(member) || (GetVisibility(member) & PropertyVisibility.RuntimeSet) == 0; + context.DrawChildMember(context.GetValue() ?? boxedValue, member, memberType, () => GetMemberValue(member, context.GetValue() ?? boxedValue), value => + { + object updated = context.GetValue() ?? Activator.CreateInstance(context.propertyType)!; + SetMemberValue(member, updated, value); + context.SetValue(updated); + }, memberReadOnly); } }); - - NativeImGui.TreePop(); + Ui.TreePop(); } private static MemberInfo[] GetMembers(Type type) { - return S_MEMBER_CACHE.GetValue(type, static value => new MembersBox( - value - .GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(IsRuntimeVisibleMember) - .OrderBy(static member => member.MetadataToken) - .ToArray())).members; + return S_MEMBER_CACHE.GetValue( + type, + static value => new MembersBox(value.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsRuntimeVisibleMember).OrderBy(static member => member.MetadataToken).ToArray()) + ).members; } private static bool IsRuntimeVisibleMember(MemberInfo member) { - SerializablePropertyAttribute? attribute = - member.GetCustomAttribute(inherit: true); + SerializablePropertyAttribute? attribute = member.GetCustomAttribute(inherit: true); PropertyVisibility visibility = attribute?.propertyVisibility ?? PropertyVisibility.Show; if ((visibility & PropertyVisibility.RuntimeGet) == 0) return false; - return member switch { - FieldInfo field => - !field.IsStatic && - (attribute is not null || field.IsPublic && !field.IsInitOnly), - PropertyInfo property => - property.GetIndexParameters().Length == 0 && - (attribute is not null || - property.GetGetMethod(nonPublic: true)?.IsPublic == true && - property.GetSetMethod(nonPublic: true)?.IsPublic == true), + FieldInfo field => !field.IsStatic && (attribute is not null || field.IsPublic && !field.IsInitOnly), + PropertyInfo property => property.GetIndexParameters().Length == 0 + && (attribute is not null + || property.GetGetMethod(nonPublic: true)?.IsPublic == true + && property.GetSetMethod(nonPublic: true)?.IsPublic == true), _ => false }; } private static PropertyVisibility GetVisibility(MemberInfo member) { - return member.GetCustomAttribute(true)?.propertyVisibility - ?? PropertyVisibility.Show; + return member.GetCustomAttribute(true)?.propertyVisibility ?? PropertyVisibility.Show; } private static Type GetMemberType(MemberInfo member) @@ -107,8 +88,7 @@ private static Type GetMemberType(MemberInfo member) { FieldInfo field => field.FieldType, PropertyInfo property => property.PropertyType, - _ => throw new InvalidOperationException($"Unsupported struct member '{member.MemberType}'.") - }; + _ => throw new InvalidOperationException($"Unsupported struct member '{member.MemberType}'.")}; } private static object? GetMemberValue( diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/UnsupportedPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/UnsupportedPropertyDrawer.cs index cafc344ac..ad5daf22f 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/UnsupportedPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/UnsupportedPropertyDrawer.cs @@ -1,8 +1,7 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; namespace Inno.Editor.Inspection; - internal sealed class UnsupportedPropertyDrawer : IPropertyDrawer { internal static UnsupportedPropertyDrawer instance { get; } = new(); @@ -16,10 +15,9 @@ internal sealed class UnsupportedPropertyDrawer : IPropertyDrawer public void Draw(PropertyDrawContext context) { object? value = context.GetValue(); - EditorWidget.ColoredText( + EditorUI.current.widgets.ColoredText( EditorPalette.warning, - value is null - ? $"Unsupported {context.propertyType.Name} (null)" - : $"Unsupported {context.propertyType.Name}: {value}"); + value is null ? $"Unsupported {context.propertyType.Name} (null)" : $"Unsupported {context.propertyType.Name}: {value}" + ); } } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs index 9e5beef28..d758c28d6 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorAction.cs @@ -1,26 +1,24 @@ +using Inno.Editor.Core; using System; namespace Inno.Editor.Interactions; - /// /// Defines one automatically discovered editor operation and owns its complete multi-frame lifecycle. /// public abstract class EditorAction : IDisposable { - private object? m_activeTarget; + private EditorTargetReference? m_activeTarget; + private EditorInteractions? m_activeInteractions; private bool m_isActive; private bool m_isDisposed; - /// /// Gets whether the action currently owns an active multi-frame operation. /// public bool isActive => m_isActive; - /// /// Gets the required target type, or for a targetless action. /// public virtual Type? targetType => null; - internal virtual Type? argumentType => null; internal EditorActionState QueryInternal(EditorActionContext context) @@ -38,11 +36,10 @@ internal void ExecuteInternal(EditorActionContext context) internal bool PresentInternal(EditorActionContext context) { ObjectDisposedException.ThrowIf(m_isDisposed, this); - return m_isActive && Equals(m_activeTarget, context.target) && Present(context); + return m_isActive && m_activeTarget is not null && m_activeTarget.Matches(context.interactions, context.target) && Present(context); } internal void CancelInternal() => Cancel(); - internal void LosePresentationInternal() { if (!m_isActive) @@ -58,7 +55,24 @@ internal void LosePresentationInternal() } } - internal bool IsActiveFor(object? target) => m_isActive && Equals(m_activeTarget, target); + internal bool IsActiveFor(object? target) => m_isActive + && m_activeTarget is not null + && m_activeInteractions is not null + && m_activeTarget.Matches( + m_activeInteractions, + target + ); + /// + /// Resolves the active target afresh for this callback; null means the identity is unavailable. + /// + /// + /// Do not retain the result across callbacks. Activation stores a domain-qualified reference rather than the live object. + /// + protected object? activeTarget => m_activeInteractions is null ? null : m_activeTarget?.Resolve(m_activeInteractions); + /// + /// Gets the host-owned passive context of the active operation, or null after completion. + /// + protected EditorContext? activeEditorContext { get; private set; } /// /// Activates this action for a target and cancels any operation it previously owned. @@ -73,7 +87,9 @@ protected void Activate(EditorActionContext context) { ArgumentNullException.ThrowIfNull(context); Cancel(); - m_activeTarget = context.target; + m_activeTarget = EditorTargetReference.Capture(context.target); + m_activeInteractions = context.interactions; + activeEditorContext = context.editor; m_isActive = true; } @@ -85,6 +101,8 @@ protected void Complete() if (!m_isActive) return; m_activeTarget = null; + m_activeInteractions = null; + activeEditorContext = null; m_isActive = false; OnCompleted(); } @@ -97,6 +115,8 @@ protected void Cancel() if (!m_isActive) return; m_activeTarget = null; + m_activeInteractions = null; + activeEditorContext = null; m_isActive = false; OnCancelled(); } @@ -111,7 +131,6 @@ protected void Cancel() /// The current presentation and availability state. /// protected virtual EditorActionState Query(EditorActionContext context) => EditorActionState.enabled; - /// /// Executes the action for the supplied context. /// @@ -119,7 +138,6 @@ protected void Cancel() /// The editor, area, target, and optional argument for the operation. /// protected abstract void Execute(EditorActionContext context); - /// /// Presents an active action at the current target location. /// @@ -130,7 +148,6 @@ protected void Cancel() /// when the action replaced the target's normal content; otherwise, . /// protected virtual bool Present(EditorActionContext context) => false; - /// /// Runs after an active operation completes successfully. /// @@ -151,10 +168,9 @@ protected virtual void OnCancelled() /// /// The default behavior cancels the active operation. An action that owns an editable value may override this /// method to validate and commit that value. The operation is cancelled automatically when an override returns - /// without calling or . + /// without calling or . /// protected virtual void OnPresentationLost() => Cancel(); - /// /// Cancels active work and releases this action instance. /// @@ -178,13 +194,11 @@ public void Dispose() /// The command argument type accepted by the action. /// public abstract class EditorAction : EditorAction - where TTarget : class { /// /// Gets the concrete type handled by this extension implementation. /// public sealed override Type targetType => typeof(TTarget); - internal sealed override Type argumentType => typeof(TArgument); /// @@ -196,11 +210,10 @@ public abstract class EditorAction : EditorAction /// /// The context that supplies state and services for this operation. /// - protected sealed override EditorActionState Query(EditorActionContext context) - => TryCreate(context, out EditorActionContext typed) - ? Query(typed) - : EditorActionState.hidden; - + protected sealed override EditorActionState Query(EditorActionContext context) => TryCreate( + context, + out EditorActionContext typed + ) ? Query(typed) : EditorActionState.hidden; /// /// Applies the editor action to the supplied interaction context. /// @@ -211,10 +224,9 @@ protected sealed override void Execute(EditorActionContext context) { if (!TryCreate(context, out EditorActionContext typed)) { - throw new InvalidOperationException( - $"Editor action '{GetType().FullName}' requires target '{typeof(TTarget).FullName}' " + - $"and argument '{typeof(TArgument).FullName}'."); + throw new InvalidOperationException($"Editor action '{GetType().FullName}' requires target '{typeof(TTarget).FullName}' " + $"and argument '{typeof(TArgument).FullName}'."); } + Execute(typed); } @@ -227,9 +239,7 @@ protected sealed override void Execute(EditorActionContext context) /// /// The context that supplies state and services for this operation. /// - protected sealed override bool Present(EditorActionContext context) - => TryCreate(context, out EditorActionContext typed) && Present(typed); - + protected sealed override bool Present(EditorActionContext context) => TryCreate(context, out EditorActionContext typed) && Present(typed); /// /// Evaluates the action for a typed target and argument. /// @@ -240,7 +250,6 @@ protected sealed override bool Present(EditorActionContext context) /// The current presentation and availability state. /// protected virtual EditorActionState Query(EditorActionContext context) => EditorActionState.enabled; - /// /// Executes the action for a typed target and argument. /// @@ -248,7 +257,6 @@ protected sealed override bool Present(EditorActionContext context) /// The typed contextual request. /// protected abstract void Execute(EditorActionContext context); - /// /// Presents an active action for a typed target and argument. /// @@ -259,7 +267,6 @@ protected sealed override bool Present(EditorActionContext context) /// when content was presented. /// protected virtual bool Present(EditorActionContext context) => false; - private static bool TryCreate( EditorActionContext context, out EditorActionContext typed @@ -269,6 +276,7 @@ out EditorActionContext typed typed = new EditorActionContext(context, target, argument); return true; } + typed = null!; return false; } @@ -293,11 +301,8 @@ public abstract class EditorArgumentAction : EditorAction /// /// The validated editor action state that represents the completed operation. /// - protected sealed override EditorActionState Query(EditorActionContext context) - => context.target is null && context.argument is TArgument argument - ? Query(new EditorActionArgumentContext(context, argument)) - : EditorActionState.hidden; - + protected sealed override EditorActionState Query(EditorActionContext context) => context.target is null + && context.argument is TArgument argument ? Query(new EditorActionArgumentContext(context, argument)) : EditorActionState.hidden; /// /// Applies the editor action to the supplied interaction context. /// @@ -308,9 +313,9 @@ protected sealed override void Execute(EditorActionContext context) { if (context.target is not null || context.argument is not TArgument argument) { - throw new InvalidOperationException( - $"Editor action '{GetType().FullName}' requires argument '{typeof(TArgument).FullName}'."); + throw new InvalidOperationException($"Editor action '{GetType().FullName}' requires argument '{typeof(TArgument).FullName}'."); } + Execute(new EditorActionArgumentContext(context, argument)); } @@ -324,7 +329,6 @@ protected sealed override void Execute(EditorActionContext context) /// The current presentation and availability state. /// protected virtual EditorActionState Query(EditorActionArgumentContext context) => EditorActionState.enabled; - /// /// Executes the action for a typed argument. /// @@ -344,7 +348,6 @@ protected sealed override void Execute(EditorActionContext context) /// The presentation data type. /// public abstract class EditorPresentationAction : EditorAction - where TTarget : class { /// /// Presents this action through the current editor interaction surface. @@ -355,10 +358,8 @@ public abstract class EditorPresentationAction : EditorA /// /// when the operation succeeds or its condition is satisfied; otherwise, . /// - protected sealed override bool Present(EditorActionContext context) - => context.argument is TPresentation presentation && Present( - new EditorActionContext(context, context.target, presentation)); - + protected sealed override bool Present(EditorActionContext context) => context.argument is TPresentation presentation + && Present(new EditorActionContext(context, context.target, presentation)); /// /// Presents active content using strongly typed presentation data. /// @@ -378,7 +379,6 @@ protected sealed override bool Present(EditorActionContext context) /// The target type accepted by the action. /// public abstract class EditorAction : EditorAction - where TTarget : class { /// /// Gets the concrete type handled by this extension implementation. @@ -397,9 +397,7 @@ public abstract class EditorAction : EditorAction protected sealed override EditorActionState Query(EditorActionContext context) { ArgumentNullException.ThrowIfNull(context); - return context.target is TTarget target - ? Query(new EditorActionContext(context, target)) - : EditorActionState.hidden; + return context.target is TTarget target ? Query(new EditorActionContext(context, target)) : EditorActionState.hidden; } /// @@ -413,9 +411,9 @@ protected sealed override void Execute(EditorActionContext context) ArgumentNullException.ThrowIfNull(context); if (context.target is not TTarget target) { - throw new InvalidOperationException( - $"Editor action '{GetType().FullName}' requires target '{typeof(TTarget).FullName}'."); + throw new InvalidOperationException($"Editor action '{GetType().FullName}' requires target '{typeof(TTarget).FullName}'."); } + Execute(new EditorActionContext(context, target)); } @@ -431,8 +429,7 @@ protected sealed override void Execute(EditorActionContext context) protected sealed override bool Present(EditorActionContext context) { ArgumentNullException.ThrowIfNull(context); - return context.target is TTarget target && - Present(new EditorActionContext(context, target)); + return context.target is TTarget target && Present(new EditorActionContext(context, target)); } /// @@ -445,7 +442,6 @@ protected sealed override bool Present(EditorActionContext context) /// The current presentation and availability state. /// protected virtual EditorActionState Query(EditorActionContext context) => EditorActionState.enabled; - /// /// Executes the action for a strongly typed target. /// @@ -453,7 +449,6 @@ protected sealed override bool Present(EditorActionContext context) /// The contextual request containing the typed target. /// protected abstract void Execute(EditorActionContext context); - /// /// Presents an active action for a strongly typed target. /// diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs index 665bf0cca..505563fa6 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionContext.cs @@ -1,9 +1,7 @@ using System; - using Inno.Editor.Core; namespace Inno.Editor.Interactions; - /// /// Provides contextual state to an editor action. /// @@ -28,7 +26,7 @@ public class EditorActionContext /// An optional placement-specific argument. /// /// - /// Thrown when or is . + /// Thrown when or is . /// /// /// Thrown when is empty. @@ -52,27 +50,22 @@ internal EditorActionContext( /// Gets the shared passive editor context. /// public EditorContext editor { get; } - /// /// Gets the active interaction entry point. /// public EditorInteractions interactions { get; } - /// /// Gets the transactional history used to record reversible mutations performed by this action. /// public IEditorHistory history => interactions.history; - /// /// Gets the stable interaction area. /// public string area { get; } - /// /// Gets the contextual action target. /// public object? target { get; } - /// /// Gets the optional action argument. /// @@ -86,13 +79,11 @@ internal EditorActionContext( /// The target type required by the action. /// public sealed class EditorActionContext : EditorActionContext - where TTarget : class { internal EditorActionContext( EditorActionContext context, TTarget target - ) - : base(context.editor, context.interactions, context.area, target, context.argument) + ) : base(context.editor, context.interactions, context.area, target, context.argument) { this.target = target; } @@ -113,14 +104,12 @@ TTarget target /// The action argument type required by the action. /// public sealed class EditorActionContext : EditorActionContext - where TTarget : class { internal EditorActionContext( EditorActionContext context, TTarget target, TArgument argument - ) - : base(context.editor, context.interactions, context.area, target, argument) + ) : base(context.editor, context.interactions, context.area, target, argument) { this.target = target; this.argument = argument; @@ -130,7 +119,6 @@ TArgument argument /// Gets the strongly typed action target. /// public new TTarget target { get; } - /// /// Gets the strongly typed action argument. /// @@ -148,8 +136,7 @@ public sealed class EditorActionArgumentContext : EditorActionContext internal EditorActionArgumentContext( EditorActionContext context, TArgument argument - ) - : base(context.editor, context.interactions, context.area, target: null, argument: argument) + ) : base(context.editor, context.interactions, context.area, target: null, argument: argument) { this.argument = argument; } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs index fd8a465c2..fbd7ab904 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs @@ -1,23 +1,22 @@ using System; using System.Collections.Generic; using System.Linq; - using Inno.Core.Events; using Inno.Core.Logging; using Inno.Editor.Core; namespace Inno.Editor.Interactions; - internal sealed class EditorActionRouter( EditorExtensionCatalog catalog, EditorContext editor, EditorInteractions interactions, Logger log -) { - private readonly Queue<(string Action, EditorActionContext Context)> m_pending = []; +) +{ + private readonly Queue m_pending = []; private readonly HashSet m_presentationFailures = new(StringComparer.Ordinal); private readonly HashSet m_queryFailures = new(StringComparer.Ordinal); - + private IReadOnlyDictionary? m_byId; internal EditorActionState Query( string action, EditorActionContext context @@ -55,6 +54,7 @@ EditorActionContext context EditorActionState state = registration.action.QueryInternal(context); if (!state.isVisible || !state.isEnabled) return false; + interactions.PrepareOperation(); registration.action.ExecuteInternal(context); return true; } @@ -85,10 +85,7 @@ EditorActionContext context { TryCancel(registration.action, action, "presentation cleanup"); if (m_presentationFailures.Add(action)) - log.Write( - LogLevel.Error, - "Editor action '{0}' presentation failed: {1}", - [action, exception]); + log.Write(LogLevel.Error, "Editor action '{0}' presentation failed: {1}", [action, exception]); return false; } } @@ -109,13 +106,19 @@ EditorActionContext context ) { ArgumentException.ThrowIfNullOrWhiteSpace(action); ArgumentNullException.ThrowIfNull(context); - m_pending.Enqueue((action, context)); + m_pending.Enqueue(new PendingAction(action, context.area, EditorTargetReference.Capture(context.target), context.target is not null, EditorTargetReference.Capture(context.argument), context.argument is not null)); } internal void Flush() { - while (m_pending.TryDequeue(out (string Action, EditorActionContext Context) request)) - _ = Execute(request.Action, request.Context); + while (m_pending.TryDequeue(out PendingAction? request)) + { + object? target = request.target.Resolve(interactions); + object? argument = request.argument.Resolve(interactions); + if (request.hasTarget && target is null || request.hasArgument && argument is null) + continue; + _ = Execute(request.action, new EditorActionContext(editor, interactions, request.area, target, argument)); + } } internal void Clear(IReadOnlyList registrations) @@ -124,6 +127,7 @@ internal void Clear(IReadOnlyList reg m_pending.Clear(); m_presentationFailures.Clear(); m_queryFailures.Clear(); + InvalidateDiscovery(); for (int i = 0; i < registrations.Count; i++) { EditorExtensionCatalog.ActionRegistration registration = registrations[i]; @@ -136,8 +140,10 @@ internal void ResetTransientState() m_pending.Clear(); m_presentationFailures.Clear(); m_queryFailures.Clear(); + InvalidateDiscovery(); } + internal void InvalidateDiscovery() => m_byId = null; internal void LosePresentationExcept(object? target) { var visited = new HashSet(ReferenceEqualityComparer.Instance); @@ -152,10 +158,7 @@ internal void LosePresentationExcept(object? target) } catch (Exception exception) { - log.Write( - LogLevel.Error, - "Editor action '{0}' failed while losing presentation focus: {1}", - [registration.attribute.action, exception]); + log.Write(LogLevel.Error, "Editor action '{0}' failed while losing presentation focus: {1}", [registration.attribute.action, exception]); } } } @@ -190,9 +193,8 @@ internal bool DispatchShortcut( { continue; } - foreach (EditorShortcutAttribute shortcut in GetApplicableShortcuts( - resolved.Value.registration, - area)) + + foreach (EditorShortcutAttribute shortcut in GetApplicableShortcuts(resolved.Value.registration, area)) { HotKeyGesture gesture = CreateGesture(shortcut); if (!gesture.Matches(keyEvent)) @@ -201,6 +203,7 @@ internal bool DispatchShortcut( break; } } + candidates.Sort(static ( left, right @@ -212,8 +215,7 @@ internal bool DispatchShortcut( int distanceComparison = left.resolved.targetDistance.CompareTo(right.resolved.targetDistance); if (distanceComparison != 0) return distanceComparison; - int priorityComparison = right.resolved.registration.attribute.priority.CompareTo( - left.resolved.registration.attribute.priority); + int priorityComparison = right.resolved.registration.attribute.priority.CompareTo(left.resolved.registration.attribute.priority); if (priorityComparison != 0) return priorityComparison; return string.Compare(left.action, right.action, StringComparison.Ordinal); @@ -224,6 +226,7 @@ internal bool DispatchShortcut( if (Execute(candidate.action, context)) return true; } + return false; } @@ -232,14 +235,13 @@ private bool TryResolveShortcut( string area, out HotKeyGesture gesture ) { - EditorShortcutAttribute? best = registration is null - ? null - : GetApplicableShortcuts(registration, area).FirstOrDefault(); + EditorShortcutAttribute? best = registration is null ? null : GetApplicableShortcuts(registration, area).FirstOrDefault(); if (best is null) { gesture = default; return false; } + gesture = CreateGesture(best); return true; } @@ -247,22 +249,19 @@ out HotKeyGesture gesture private EditorExtensionCatalog.ActionRegistration? Resolve( string action, EditorActionContext context - ) - => ResolveWithSpecificity(action, context)?.registration; - + ) => ResolveWithSpecificity(action, context)?.registration; private ResolvedAction? ResolveWithSpecificity( string action, EditorActionContext context ) { EditorExtensionCatalog.ActionRegistration? best = null; int bestDistance = int.MaxValue; - foreach (EditorExtensionCatalog.ActionRegistration registration in catalog.extensions.actions) + if (!Index.TryGetValue(action, out EditorExtensionCatalog.ActionRegistration[]? registrations)) + return null; + foreach (EditorExtensionCatalog.ActionRegistration registration in registrations) { - if (!string.Equals(registration.id, action, StringComparison.Ordinal)) - continue; bool exactArea = registration.area is not null; - if (exactArea && - !string.Equals(registration.area, context.area, StringComparison.Ordinal)) + if (exactArea && !string.Equals(registration.area, context.area, StringComparison.Ordinal)) { continue; } @@ -271,52 +270,89 @@ EditorActionContext context Type? targetType = registration.targetType; if (targetType is not null) { - if (context.target is null || - !EditorTypeDistance.TryGet(context.target.GetType(), targetType, out distance)) + if (context.target is null || !EditorTypeDistance.TryGet(context.target.GetType(), targetType, out distance)) { continue; } } + Type? argumentType = registration.argumentType; - if (argumentType is not null && - (context.argument is null || !argumentType.IsInstanceOfType(context.argument))) + if (argumentType is not null && (context.argument is null || !argumentType.IsInstanceOfType(context.argument))) { continue; } bool bestExactArea = best?.area is not null; - if (best is null || - exactArea && !bestExactArea || - exactArea == bestExactArea && - (distance < bestDistance || - distance == bestDistance && registration.attribute.priority > best.attribute.priority || - distance == bestDistance && registration.attribute.priority == best.attribute.priority && - string.Compare(registration.type.FullName, best.type.FullName, StringComparison.Ordinal) < 0)) + if (best is null + || exactArea + && !bestExactArea + || exactArea == bestExactArea + && (distance < bestDistance + || distance == bestDistance + && registration.attribute.priority > best.attribute.priority + || distance == bestDistance + && registration.attribute.priority == best.attribute.priority + && string.Compare( + registration.type.FullName, + best.type.FullName, + StringComparison.Ordinal + ) < 0)) { best = registration; bestDistance = distance; } } - return best is null - ? null - : new ResolvedAction(best, bestDistance, best.area is not null); + + return best is null ? null : new ResolvedAction(best, bestDistance, best.area is not null); } - private HotKeyGesture CreateGesture(EditorShortcutAttribute shortcut) - => shortcut.primary - ? HotKeyGesture.Primary(shortcut.key, editor.keyboard, shortcut.modifiers) - : new HotKeyGesture(shortcut.key, shortcut.modifiers, editor.keyboard.superModifierLabel); + internal IReadOnlyList BuildUnplacedCommands( + EditorActionContext context, + IReadOnlySet placed + ) { + var result = new List(); + foreach (string id in Index.Keys) + { + if (placed.Contains(id)) + continue; + EditorActionState state = Query(id, context); + if (!state.isVisible) + continue; + string label = state.displayName ?? id; + result.Add(new EditorMenuItem(label, id, 0, false, state)); + } + + result.Sort(static ( + left, + right + ) => StringComparer.OrdinalIgnoreCase.Compare(left.label, right.label)); + return result; + } + private IReadOnlyDictionary Index => m_byId ??= catalog.extensions.actions.GroupBy( + static registration => registration.id, + StringComparer.Ordinal + ).ToDictionary( + static group => group.Key, + static group => group.ToArray(), + StringComparer.Ordinal + ); + + private HotKeyGesture CreateGesture(EditorShortcutAttribute shortcut) => shortcut.primary ? HotKeyGesture.Primary( + shortcut.key, + editor.keyboard, + shortcut.modifiers + ) : new HotKeyGesture( + shortcut.key, + shortcut.modifiers, + editor.keyboard.superModifierLabel + ); private static IEnumerable GetApplicableShortcuts( EditorExtensionCatalog.ActionRegistration registration, string area ) { - bool hasExact = registration.shortcuts.Any(shortcut => - !string.IsNullOrEmpty(shortcut.area) && - string.Equals(shortcut.area, area, StringComparison.Ordinal)); - return registration.shortcuts.Where(shortcut => hasExact - ? string.Equals(shortcut.area, area, StringComparison.Ordinal) - : string.IsNullOrEmpty(shortcut.area)); + bool hasExact = registration.shortcuts.Any(shortcut => !string.IsNullOrEmpty(shortcut.area) && string.Equals(shortcut.area, area, StringComparison.Ordinal)); + return registration.shortcuts.Where(shortcut => hasExact ? string.Equals(shortcut.area, area, StringComparison.Ordinal) : string.IsNullOrEmpty(shortcut.area)); } private void TryCancel( @@ -330,19 +366,23 @@ string phase } catch (Exception exception) { - log.Write( - LogLevel.Error, - "Editor action '{0}' failed during {1}: {2}", - [actionId, phase, exception]); + log.Write(LogLevel.Error, "Editor action '{0}' failed during {1}: {2}", [actionId, phase, exception]); } } + private sealed record PendingAction( + string action, + string area, + EditorTargetReference target, + bool hasTarget, + EditorTargetReference argument, + bool hasArgument + ); private readonly record struct ResolvedAction( EditorExtensionCatalog.ActionRegistration registration, int targetDistance, bool exactArea ); - private readonly record struct ShortcutCandidate( string action, ResolvedAction resolved, diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs index 5d3fd141c..a0e834a59 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionState.cs @@ -1,5 +1,4 @@ namespace Inno.Editor.Interactions; - /// /// Describes the current presentation and availability of an editor action. /// @@ -20,48 +19,51 @@ public readonly record struct EditorActionState /// /// An optional contextual label that replaces the static placement label. /// + /// + /// A concise explanation shown when an otherwise visible action cannot execute. + /// public EditorActionState( bool isVisible, bool isEnabled, bool isChecked = false, - string? displayName = null + string? displayName = null, + string? unavailableReason = null ) { this.isVisible = isVisible; this.isEnabled = isEnabled; this.isChecked = isChecked; this.displayName = displayName; + this.unavailableReason = isEnabled ? null : unavailableReason; } /// /// Gets whether the action should be displayed. /// public bool isVisible { get; } - /// /// Gets whether the action can execute. /// public bool isEnabled { get; } - /// /// Gets whether the action is currently checked. /// public bool isChecked { get; } - /// /// Gets an optional contextual display name. /// public string? displayName { get; } - + /// + /// Gets the current execution barrier, or null when no specific explanation is available. + /// + public string? unavailableReason { get; } /// /// Gets a visible and enabled action state. /// public static EditorActionState enabled => new(true, true); - /// /// Gets a visible but disabled action state. /// public static EditorActionState disabled => new(true, false); - /// /// Gets a hidden action state. /// diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Discovery/EditorExtensionCatalog.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Discovery/EditorExtensionCatalog.cs index 93247e844..b6c9fd6e5 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Discovery/EditorExtensionCatalog.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Discovery/EditorExtensionCatalog.cs @@ -2,14 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; - using Inno.Core.Execution; using Inno.Core.Logging; using Inno.Extensibility.Types; using Inno.Editor.Core; namespace Inno.Editor.Interactions; - internal sealed class EditorExtensionCatalog : TypeRegistry { private readonly EditorContext m_context; @@ -24,7 +22,6 @@ internal sealed class EditorExtensionCatalog : TypeRegistry hostServices, Action extensionsChanged - ) - : base(types) + ) : base(types) { m_context = context ?? throw new ArgumentNullException(nameof(context)); m_interactions = interactions ?? throw new ArgumentNullException(nameof(interactions)); m_log = log ?? throw new ArgumentNullException(nameof(log)); ArgumentNullException.ThrowIfNull(hostServices); - m_hostServices = hostServices.Select(static service => - service ?? throw new ArgumentException("Host services cannot contain null.", nameof(hostServices))) - .ToArray(); + m_hostServices = hostServices.Select(static service => service ?? throw new ArgumentException("Host services cannot contain null.", nameof(hostServices))).ToArray(); m_extensionsChanged = extensionsChanged ?? throw new ArgumentNullException(nameof(extensionsChanged)); m_state = new EditorExtensionStateStore(context, log); } @@ -57,18 +51,18 @@ internal Snapshot extensions } } - internal bool TryGetModule(out TModule? module) where TModule : EditorModule + internal bool TryGetModule(out TModule? module) + where TModule : EditorModule { Snapshot? snapshot = m_active; if (snapshot is not null) foreach (ModuleRegistration registration in snapshot.modules) - if (registration.module is TModule candidate - && snapshot.startedModules.Contains(candidate) - && !snapshot.quarantinedModules.Contains(candidate)) + if (registration.module is TModule candidate && snapshot.startedModules.Contains(candidate) && !snapshot.quarantinedModules.Contains(candidate)) { module = candidate; return true; } + module = null; return false; } @@ -90,12 +84,11 @@ internal void UpdateModules() catch (Exception exception) { snapshot.quarantinedModules.Add(registration.module); - LogError("Editor module '{0}' failed to update and was quarantined: {1}", - registration.attribute.id, - exception); + LogError("Editor module '{0}' failed to update and was quarantined: {1}", registration.attribute.id, exception); continue; } } + m_state.Update(m_context.frame.totalTime, GetAvailableState(snapshot), GetAvailablePanels(snapshot)); } @@ -112,11 +105,11 @@ internal bool TryTogglePanel(string panelId) for (int i = 0; i < snapshot.panels.Length; i++) { PanelRegistration registration = snapshot.panels[i]; - if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || - snapshot.quarantinedPanels.Contains(registration.panel)) + if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || snapshot.quarantinedPanels.Contains(registration.panel)) { continue; } + registration.panel.isOpen = !registration.panel.isOpen; if (registration.panel.isOpen) m_pendingPanelFocus.Add(panelId); @@ -124,6 +117,25 @@ internal bool TryTogglePanel(string panelId) m_pendingPanelFocus.Remove(panelId); return true; } + + return false; + } + + internal IReadOnlyList GetPanelDeclarations() => extensions.panelDeclarations; + internal bool TryGetPanelState( + string panelId, + out bool isOpen + ) { + Snapshot snapshot = extensions; + foreach (PanelRegistration registration in snapshot.panels) + { + if (registration.attribute.id != panelId || snapshot.quarantinedPanels.Contains(registration.panel)) + continue; + isOpen = registration.panel.isOpen; + return true; + } + + isOpen = false; return false; } @@ -134,15 +146,16 @@ internal bool TryOpenPanel(string panelId) for (int i = 0; i < snapshot.panels.Length; i++) { PanelRegistration registration = snapshot.panels[i]; - if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || - snapshot.quarantinedPanels.Contains(registration.panel)) + if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || snapshot.quarantinedPanels.Contains(registration.panel)) { continue; } + registration.panel.isOpen = true; m_pendingPanelFocus.Add(panelId); return true; } + return false; } @@ -153,20 +166,20 @@ internal bool TryClosePanel(string panelId) for (int i = 0; i < snapshot.panels.Length; i++) { PanelRegistration registration = snapshot.panels[i]; - if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || - snapshot.quarantinedPanels.Contains(registration.panel)) + if (!string.Equals(registration.attribute.id, panelId, StringComparison.Ordinal) || snapshot.quarantinedPanels.Contains(registration.panel)) { continue; } + registration.panel.isOpen = false; m_pendingPanelFocus.Remove(panelId); return true; } + return false; } internal bool TakePanelFocusRequest(string panelId) => m_pendingPanelFocus.Remove(panelId); - internal void PrepareShutdown() { if (m_active is not null) @@ -174,7 +187,6 @@ internal void PrepareShutdown() } internal ActionRegistration[] GetActionsForShutdown() => m_active?.actions ?? []; - internal void QuarantinePanel( Snapshot snapshot, PanelRegistration registration, @@ -188,9 +200,7 @@ Exception exception registration.panel.isOpen = false; m_diagnostics.ReportPanelFailure(registration.attribute.id, exception); m_diagnostics.Commit(); - LogError("Editor panel '{0}' failed to draw and was quarantined: {1}", - registration.attribute.id, - exception); + LogError("Editor panel '{0}' failed to draw and was quarantined: {1}", registration.attribute.id, exception); } internal void QuarantineModal( @@ -216,6 +226,7 @@ internal void Shutdown(bool saveState = true) AttemptCleanup(() => m_state.PrepareShutdown(GetAvailableState(active), GetAvailablePanels(active)), failures); AttemptCleanup(() => Deactivate(active), failures); } + m_active = null; NotifyExtensionsChanged(); m_diagnostics.Dispose(); @@ -235,99 +246,71 @@ internal void Shutdown(bool saveState = true) protected override Snapshot Build(TypeCacheSnapshot types) { CapturePanelStates(); - Type[] moduleTypes = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .ToArray(); + Type[] moduleTypes = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).ToArray(); var activator = new EditorExtensionActivator( m_context, m_interactions, moduleTypes, types.types.Select(typeRef => typeRef.Resolve(types)).ToArray(), m_hostServices, - m_active?.instances); - - ModuleRegistration[] modules = moduleTypes - .Where(activator.CanCreate) - .Select(type => new ModuleRegistration( - type.GetCustomAttribute(false)!, - type, - activator.CreateModule(type))) - .OrderBy(static value => value.attribute.order) - .ThenBy(static value => value.type.FullName, StringComparer.Ordinal) - .ToArray(); - - ActionRegistration[] actions = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .Select(type => CreateActionRegistration(type, activator)) - .ToArray(); + m_active?.instances + ); + ModuleRegistration[] modules = moduleTypes.Where(activator.CanCreate).Select(type => new ModuleRegistration(type.GetCustomAttribute(false)!, type, activator.CreateModule(type))).OrderBy(static value => value.attribute.order).ThenBy( + static value => value.type.FullName, + StringComparer.Ordinal + ).ToArray(); + ActionRegistration[] actions = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).Select(type => CreateActionRegistration(type, activator)).ToArray(); ValidateActions(actions); ValidateShortcuts(actions); ValidateToolbars(actions); - - MenuSourceRegistration[] menuSources = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .SelectMany(type => CreateMenuSourceRegistrations(type, activator)) - .OrderByDescending(static value => value.priority) - .ThenBy(static value => value.type.FullName, StringComparer.Ordinal) - .ToArray(); - - DropRegistration[] drops = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .SelectMany(type => CreateDropRegistrations(type, activator)) - .ToArray(); + MenuSourceRegistration[] menuSources = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).SelectMany(type => CreateMenuSourceRegistrations(type, activator)).OrderByDescending(static value => value.priority).ThenBy( + static value => value.type.FullName, + StringComparer.Ordinal + ).ToArray(); + DropRegistration[] drops = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).SelectMany(type => CreateDropRegistrations(type, activator)).ToArray(); ValidateDrops(drops); - - PanelRegistration[] panels = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .Select(type => CreatePanelRegistration(type, activator)) - .OrderBy(static value => value.attribute.order) - .ThenBy(static value => value.type.FullName, StringComparer.Ordinal) - .ToArray(); + PanelRegistration[] panels = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).Select(type => CreatePanelRegistration(type, activator)).OrderBy(static value => value.attribute.order).ThenBy( + static value => value.type.FullName, + StringComparer.Ordinal + ).ToArray(); ValidatePanels(panels); ValidateExtensionIds(modules, panels); - - ModalRegistration[] modals = types.GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .Select(type => CreateModalRegistration(type, activator)) - .OrderBy(static value => value.attribute.order) - .ThenBy(static value => value.type.FullName, StringComparer.Ordinal) - .ToArray(); + ModalRegistration[] modals = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).Select(type => CreateModalRegistration(type, activator)).OrderBy(static value => value.attribute.order).ThenBy( + static value => value.type.FullName, + StringComparer.Ordinal + ).ToArray(); ValidateModals(modals); - - HistoryHandlerRegistration[] historyHandlers = types - .GetTypesWithAttribute() - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.FullName, StringComparer.Ordinal) - .Where(activator.CanCreate) - .Select(type => CreateHistoryHandlerRegistration(type, activator)) - .OrderBy(static value => value.attribute.kind, StringComparer.Ordinal) - .ThenBy(static value => value.type.FullName, StringComparer.Ordinal) - .ToArray(); + HistoryHandlerRegistration[] historyHandlers = types.GetTypesWithAttribute().Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.FullName, + StringComparer.Ordinal + ).Where(activator.CanCreate).Select(type => CreateHistoryHandlerRegistration(type, activator)).OrderBy( + static value => value.attribute.kind, + StringComparer.Ordinal + ).ThenBy( + static value => value.type.FullName, + StringComparer.Ordinal + ).ToArray(); ValidateHistoryHandlers(historyHandlers); - StateRegistration[] state = CreateStateRegistrations(modules, panels); - - return new Snapshot( - modules, - actions, - menuSources, - drops, - panels, - modals, - historyHandlers, - state, - activator.instances.ToArray()); + return new Snapshot(modules, actions, menuSources, drops, panels, modals, historyHandlers, state, activator.instances.ToArray()); } /// @@ -345,23 +328,18 @@ Snapshot candidate ) { if (m_activation is not null) throw new InvalidOperationException("An extension generation transition is already active."); - + m_interactions.PrepareOperation(); if (previous is not null) CapturePanelStates(previous); if (previous is not null) m_state.Save(GetAvailableState(previous), GetAvailablePanels(previous)); - - var existing = previous is null - ? new HashSet(ReferenceEqualityComparer.Instance) - : new HashSet(previous.instances, ReferenceEqualityComparer.Instance); - EditorHistory.HandlerUpdate handlers = m_interactions.historyHost.PrepareHandlerUpdate( - CreateHistoryHandlerMap(candidate.historyHandlers)); + var existing = previous is null ? new HashSet(ReferenceEqualityComparer.Instance) : new HashSet(previous.instances, ReferenceEqualityComparer.Instance); + EditorHistory.HandlerUpdate handlers = m_interactions.historyHost.PrepareHandlerUpdate(CreateHistoryHandlerMap(candidate.historyHandlers)); var activation = new ActivationState(previous, candidate, existing, handlers); m_activation = activation; m_staging = candidate; m_interactions.PrepareGenerationTransition(); handlers.Activate(); - if (previous is not null) { foreach (ModuleRegistration registration in candidate.modules) @@ -369,6 +347,7 @@ Snapshot candidate if (existing.Contains(registration.module) && previous.startedModules.Contains(registration.module)) candidate.startedModules.Add(registration.module); } + foreach (PanelRegistration registration in candidate.panels) { if (existing.Contains(registration.panel) && previous.attachedPanels.Contains(registration.panel)) @@ -378,7 +357,6 @@ Snapshot candidate if (previous is not null) RetirePrevious(previous, candidate, activation); - for (int i = 0; i < candidate.modules.Length; i++) { ModuleRegistration registration = candidate.modules[i]; @@ -390,10 +368,9 @@ Snapshot candidate } var retainedPanelIds = new HashSet( - candidate.panels - .Where(registration => existing.Contains(registration.panel)) - .Select(static registration => registration.attribute.id), - StringComparer.Ordinal); + candidate.panels.Where(registration => existing.Contains(registration.panel)).Select(static registration => registration.attribute.id), + StringComparer.Ordinal + ); m_diagnostics.RetainPanels(retainedPanelIds); for (int i = 0; i < candidate.panels.Length; i++) { @@ -418,6 +395,7 @@ Snapshot candidate LogError("Editor panel '{0}' failed to attach: {1}", registration.attribute.id, exception); } } + m_diagnostics.Commit(); m_state.Restore(GetAvailableState(candidate)); m_active = candidate; @@ -441,7 +419,6 @@ Snapshot candidate m_staging = null; if (activation is null || !ReferenceEquals(activation.candidate, candidate)) return; - List failures = []; AttemptCleanup(activation.handlers.Rollback, failures); AttemptCleanup(m_interactions.RollbackGenerationTransition, failures); @@ -451,20 +428,20 @@ Snapshot candidate AttemptCleanup(() => DetachPanel(registration, "activation rollback"), failures); candidate.attachedPanels.Remove(registration.panel); } + for (int i = activation.startedModules.Count - 1; i >= 0; i--) { ModuleRegistration registration = activation.startedModules[i]; AttemptCleanup(() => StopModule(registration, "activation rollback"), failures); candidate.startedModules.Remove(registration.module); } + AttemptCleanup(() => RestorePrevious(activation), failures); m_activation = null; - - var previousPanelIds = previous is null - ? new HashSet(StringComparer.Ordinal) - : new HashSet( - GetAvailablePanels(previous).Select(static registration => registration.attribute.id), - StringComparer.Ordinal); + var previousPanelIds = previous is null ? new HashSet(StringComparer.Ordinal) : new HashSet( + GetAvailablePanels(previous).Select(static registration => registration.attribute.id), + StringComparer.Ordinal + ); m_diagnostics.RetainPanels(previousPanelIds); m_diagnostics.Commit(); ThrowCleanupFailures(failures); @@ -486,7 +463,6 @@ Snapshot currentSnapshot ActivationState? activation = m_activation; if (activation is null || !ReferenceEquals(activation.candidate, currentSnapshot)) return; - activation.handlers.Complete(); m_interactions.CompleteGenerationTransition(); NotifyExtensionsChanged(); @@ -502,9 +478,7 @@ Snapshot currentSnapshot /// protected override void DisposeSnapshot(Snapshot snapshot) { - HashSet? retained = m_active is null - ? null - : new HashSet(m_active.instances, ReferenceEqualityComparer.Instance); + HashSet? retained = m_active is null ? null : new HashSet(m_active.instances, ReferenceEqualityComparer.Instance); DisposeExtensions(snapshot.instances.Where(instance => retained?.Contains(instance) != true)); } @@ -519,6 +493,7 @@ private void Deactivate(Snapshot snapshot) AttemptCleanup(() => DetachPanel(registration, "shutdown"), failures); snapshot.attachedPanels.Remove(registration.panel); } + for (int i = snapshot.modules.Length - 1; i >= 0; i--) { ModuleRegistration registration = snapshot.modules[i]; @@ -527,6 +502,7 @@ private void Deactivate(Snapshot snapshot) AttemptCleanup(() => StopModule(registration, "shutdown"), failures); snapshot.startedModules.Remove(registration.module); } + if (ReferenceEquals(m_active, snapshot)) m_active = null; m_diagnostics.RetainPanels(new HashSet(StringComparer.Ordinal)); @@ -549,10 +525,10 @@ ActivationState activation DetachPanel(registration, "generation retirement"); previous.attachedPanels.Remove(registration.panel); } + for (int i = previous.modules.Length - 1; i >= 0; i--) { - if (!retained.Contains(previous.modules[i].module) && - previous.startedModules.Contains(previous.modules[i].module)) + if (!retained.Contains(previous.modules[i].module) && previous.startedModules.Contains(previous.modules[i].module)) { activation.stoppedPreviousModules.Add(previous.modules[i]); StopModule(previous.modules[i], "generation retirement"); @@ -579,12 +555,10 @@ private void RestorePrevious(ActivationState activation) { previous.quarantinedModules.Add(registration.module); failures.Add(exception); - LogError( - "Editor module '{0}' failed while restoring the previous generation: {1}", - registration.attribute.id, - exception); + LogError("Editor module '{0}' failed while restoring the previous generation: {1}", registration.attribute.id, exception); } } + for (int i = activation.detachedPreviousPanels.Count - 1; i >= 0; i--) { PanelRegistration registration = activation.detachedPreviousPanels[i]; @@ -599,12 +573,10 @@ private void RestorePrevious(ActivationState activation) failures.Add(exception); registration.panel.isOpen = false; m_diagnostics.ReportPanelFailure(registration.attribute.id, exception); - LogError( - "Editor panel '{0}' failed while restoring the previous generation: {1}", - registration.attribute.id, - exception); + LogError("Editor panel '{0}' failed while restoring the previous generation: {1}", registration.attribute.id, exception); } } + ThrowCleanupFailures(failures); } @@ -618,28 +590,13 @@ private void CapturePanelStates(Snapshot snapshot) { foreach (PanelRegistration registration in snapshot.panels) { - ReadOnlyMemory payload = registration.panel is IEditorPanelReloadState reloadable - ? reloadable.CaptureReloadState().ToArray() - : ReadOnlyMemory.Empty; - m_panelStates[registration.attribute.id] = new PanelState( - registration.panel.isOpen, - payload); + ReadOnlyMemory payload = registration.panel is IEditorPanelReloadState reloadable ? reloadable.CaptureReloadState().ToArray() : ReadOnlyMemory.Empty; + m_panelStates[registration.attribute.id] = new PanelState(registration.panel.isOpen, payload); } } - private static StateRegistration[] GetAvailableState(Snapshot snapshot) - => snapshot.state - .Where(registration => registration.kind != StateOwnerKind.Panel || - !snapshot.quarantinedPanels.Contains((EditorPanel)registration.owner)) - .Where(registration => registration.kind != StateOwnerKind.Module || - !snapshot.quarantinedModules.Contains((EditorModule)registration.owner)) - .ToArray(); - - private static PanelRegistration[] GetAvailablePanels(Snapshot snapshot) - => snapshot.panels - .Where(registration => !snapshot.quarantinedPanels.Contains(registration.panel)) - .ToArray(); - + private static StateRegistration[] GetAvailableState(Snapshot snapshot) => snapshot.state.Where(registration => registration.kind != StateOwnerKind.Panel || !snapshot.quarantinedPanels.Contains((EditorPanel)registration.owner)).Where(registration => registration.kind != StateOwnerKind.Module || !snapshot.quarantinedModules.Contains((EditorModule)registration.owner)).ToArray(); + private static PanelRegistration[] GetAvailablePanels(Snapshot snapshot) => snapshot.panels.Where(registration => !snapshot.quarantinedPanels.Contains(registration.panel)).ToArray(); private void StopModule( ModuleRegistration registration, string phase @@ -720,33 +677,19 @@ EditorExtensionActivator activator EditorMenuAttribute[] menus = type.GetCustomAttributes(false).ToArray(); EditorToolbarItemAttribute[] toolbars = type.GetCustomAttributes(false).ToArray(); EditorShortcutAttribute[] shortcuts = type.GetCustomAttributes(false).ToArray(); - return new ActionRegistration( - attribute, - type, - action, - action.targetType, - action.argumentType, - menus, - toolbars, - shortcuts); + return new ActionRegistration(attribute, type, action, action.targetType, action.argumentType, menus, toolbars, shortcuts); } private void LogError( string message, - params object?[] arguments + params object? [] arguments ) => m_log.Write(LogLevel.Error, message, arguments); - private static IEnumerable CreateMenuSourceRegistrations( Type type, EditorExtensionActivator activator ) { EditorMenuSource source = activator.CreateExtension(type); - return type.GetCustomAttributes(false) - .Select(attribute => new MenuSourceRegistration( - attribute.area, - attribute.priority, - type, - source)); + return type.GetCustomAttributes(false).Select(attribute => new MenuSourceRegistration(attribute.area, attribute.priority, type, source)); } private static IEnumerable CreateDropRegistrations( @@ -756,14 +699,7 @@ EditorExtensionActivator activator EditorDrop drop = activator.CreateExtension(type); Type sourceType = drop.sourceType; Type targetType = drop.targetType; - return type.GetCustomAttributes(false) - .Select(attribute => new DropRegistration( - sourceType, - targetType, - attribute.area, - attribute.priority, - type, - drop)); + return type.GetCustomAttributes(false).Select(attribute => new DropRegistration(sourceType, targetType, attribute.area, attribute.priority, type, drop)); } private PanelRegistration CreatePanelRegistration( @@ -780,46 +716,27 @@ EditorExtensionActivator activator } else { - panel.isOpen = m_state.TryGetPanelOpen(attribute.id, out bool isOpen) - ? isOpen - : attribute.defaultOpen; + panel.isOpen = m_state.TryGetPanelOpen(attribute.id, out bool isOpen) ? isOpen : attribute.defaultOpen; } + return new PanelRegistration(attribute, type, panel); } private static ModalRegistration CreateModalRegistration( Type type, EditorExtensionActivator activator - ) - => new( - type.GetCustomAttribute(false)!, - type, - activator.CreateExtension(type)); - + ) => new(type.GetCustomAttribute(false)!, type, activator.CreateExtension(type)); private static HistoryHandlerRegistration CreateHistoryHandlerRegistration( Type type, EditorExtensionActivator activator - ) - => new( - type.GetCustomAttribute(false)!, - type, - activator.CreateExtension(type)); - + ) => new(type.GetCustomAttribute(false)!, type, activator.CreateExtension(type)); private static void ValidateActions(ActionRegistration[] actions) { - foreach (IGrouping<(string Action, string Area, Type? Target, int Priority), ActionRegistration> group in - actions.GroupBy(static value => ( - value.attribute.action, - value.attribute.area, - value.targetType, - value.attribute.priority))) + foreach (IGrouping<(string Action, string Area, Type? Target, int Priority), ActionRegistration> group in actions.GroupBy(static value => (value.attribute.action, value.attribute.area, value.targetType, value.attribute.priority))) { if (group.Count() > 1) { - throw new InvalidOperationException( - $"Editor action '{group.Key.Action}' has conflicting registrations for area " + - $"'{(string.IsNullOrEmpty(group.Key.Area) ? "*" : group.Key.Area)}' and target " + - $"'{group.Key.Target?.FullName ?? "*"}'."); + throw new InvalidOperationException($"Editor action '{group.Key.Action}' has conflicting registrations for area " + $"'{(string.IsNullOrEmpty(group.Key.Area) ? "*" : group.Key.Area)}' and target " + $"'{group.Key.Target?.FullName ?? "*"}'."); } } } @@ -833,21 +750,19 @@ private void ValidateShortcuts(ActionRegistration[] actions) for (int shortcutIndex = 0; shortcutIndex < registration.shortcuts.Length; shortcutIndex++) { EditorShortcutAttribute shortcut = registration.shortcuts[shortcutIndex]; - if (!string.IsNullOrEmpty(registration.attribute.area) && - !string.IsNullOrEmpty(shortcut.area) && - !string.Equals(registration.attribute.area, shortcut.area, StringComparison.Ordinal)) + if (!string.IsNullOrEmpty(registration.attribute.area) + && !string.IsNullOrEmpty(shortcut.area) + && !string.Equals( + registration.attribute.area, + shortcut.area, + StringComparison.Ordinal + )) { - throw new InvalidOperationException( - $"Editor shortcut on '{registration.id}' targets area '{shortcut.area}', " + - $"outside its action area '{registration.attribute.area}'."); + throw new InvalidOperationException($"Editor shortcut on '{registration.id}' targets area '{shortcut.area}', " + $"outside its action area '{registration.attribute.area}'."); } - string effectiveArea = string.IsNullOrEmpty(shortcut.area) - ? registration.attribute.area - : shortcut.area; - shortcuts.Add(new ShortcutValidationEntry( - registration, - effectiveArea, - CreateShortcutGesture(shortcut))); + + string effectiveArea = string.IsNullOrEmpty(shortcut.area) ? registration.attribute.area : shortcut.area; + shortcuts.Add(new ShortcutValidationEntry(registration, effectiveArea, CreateShortcutGesture(shortcut))); } } @@ -857,31 +772,28 @@ private void ValidateShortcuts(ActionRegistration[] actions) for (int rightIndex = leftIndex + 1; rightIndex < shortcuts.Count; rightIndex++) { ShortcutValidationEntry right = shortcuts[rightIndex]; - bool sameAction = string.Equals( - left.registration.id, - right.registration.id, - StringComparison.Ordinal); - if (left.gesture != right.gesture || - left.registration.attribute.priority != right.registration.attribute.priority || - !string.Equals(left.area, right.area, StringComparison.Ordinal) || - !MayHaveEqualTargetSpecificity( - left.registration.targetType, - right.registration.targetType)) + bool sameAction = string.Equals(left.registration.id, right.registration.id, StringComparison.Ordinal); + if (left.gesture != right.gesture + || left.registration.attribute.priority != right.registration.attribute.priority + || !string.Equals( + left.area, + right.area, + StringComparison.Ordinal + ) + || !MayHaveEqualTargetSpecificity( + left.registration.targetType, + right.registration.targetType + )) { continue; } if (sameAction) { - throw new InvalidOperationException( - $"Editor action '{left.registration.id}' declares duplicate shortcut " + - $"'{left.gesture}' for area '{(string.IsNullOrEmpty(left.area) ? "*" : left.area)}'."); + throw new InvalidOperationException($"Editor action '{left.registration.id}' declares duplicate shortcut " + $"'{left.gesture}' for area '{(string.IsNullOrEmpty(left.area) ? "*" : left.area)}'."); } - throw new InvalidOperationException( - $"Editor shortcut '{left.gesture}' is ambiguous between actions " + - $"'{left.registration.id}' and '{right.registration.id}' for area " + - $"'{(string.IsNullOrEmpty(left.area) ? "*" : left.area)}'."); + throw new InvalidOperationException($"Editor shortcut '{left.gesture}' is ambiguous between actions " + $"'{left.registration.id}' and '{right.registration.id}' for area " + $"'{(string.IsNullOrEmpty(left.area) ? "*" : left.area)}'."); } } } @@ -906,46 +818,39 @@ private static void ValidateToolbars(IReadOnlyList actions) continue; if (registration.targetType is not null || registration.argumentType is not null) { - throw new InvalidOperationException( - $"Editor toolbar action '{registration.id}' must not require a target or argument."); + throw new InvalidOperationException($"Editor toolbar action '{registration.id}' must not require a target or argument."); } + for (int toolbarIndex = 0; toolbarIndex < registration.toolbars.Length; toolbarIndex++) { EditorToolbarItemAttribute toolbar = registration.toolbars[toolbarIndex]; - if (!string.IsNullOrEmpty(registration.attribute.area) && - !string.Equals(registration.attribute.area, toolbar.area, StringComparison.Ordinal)) + if (!string.IsNullOrEmpty(registration.attribute.area) && !string.Equals(registration.attribute.area, toolbar.area, StringComparison.Ordinal)) { - throw new InvalidOperationException( - $"Editor toolbar placement on '{registration.id}' targets area '{toolbar.area}', " + - $"outside its action area '{registration.attribute.area}'."); + throw new InvalidOperationException($"Editor toolbar placement on '{registration.id}' targets area '{toolbar.area}', " + $"outside its action area '{registration.attribute.area}'."); } } } - IGrouping<(string id, string area), (string id, string area)>? duplicate = actions - .SelectMany(static registration => registration.toolbars.Select(toolbar => ( - registration.id, - toolbar.area))) - .GroupBy(static placement => placement, EqualityComparer<(string id, string area)>.Default) - .FirstOrDefault(static group => group.Count() > 1); + IGrouping<(string id, string area), (string id, string area)>? duplicate = actions.SelectMany(static registration => registration.toolbars.Select(toolbar => (registration.id, toolbar.area))).GroupBy( + static placement => placement, + EqualityComparer<(string id, string area)>.Default + ).FirstOrDefault(static group => group.Count() > 1); if (duplicate is not null) - throw new InvalidOperationException( - $"Editor action '{duplicate.Key.id}' has duplicate toolbar placements."); + throw new InvalidOperationException($"Editor action '{duplicate.Key.id}' has duplicate toolbar placements."); } - private HotKeyGesture CreateShortcutGesture(EditorShortcutAttribute shortcut) - => shortcut.primary - ? HotKeyGesture.Primary(shortcut.key, m_context.keyboard, shortcut.modifiers) - : new HotKeyGesture(shortcut.key, shortcut.modifiers, m_context.keyboard.superModifierLabel); - + private HotKeyGesture CreateShortcutGesture(EditorShortcutAttribute shortcut) => shortcut.primary ? HotKeyGesture.Primary( + shortcut.key, + m_context.keyboard, + shortcut.modifiers + ) : new HotKeyGesture( + shortcut.key, + shortcut.modifiers, + m_context.keyboard.superModifierLabel + ); private static void ValidateDrops(DropRegistration[] drops) { - foreach (IGrouping<(Type Source, Type Target, string Area, int Priority), DropRegistration> group in - drops.GroupBy(static value => ( - value.sourceType, - value.targetType, - value.area, - value.priority))) + foreach (IGrouping<(Type Source, Type Target, string Area, int Priority), DropRegistration> group in drops.GroupBy(static value => (value.sourceType, value.targetType, value.area, value.priority))) { if (group.Count() > 1) throw new InvalidOperationException("Editor drop registrations contain an ambiguous match."); @@ -954,36 +859,28 @@ private static void ValidateDrops(DropRegistration[] drops) private static void ValidatePanels(PanelRegistration[] panels) { - string? duplicate = panels - .GroupBy(static value => value.attribute.id, StringComparer.Ordinal) - .FirstOrDefault(static group => group.Count() > 1)?.Key; + string? duplicate = panels.GroupBy(static value => value.attribute.id, StringComparer.Ordinal).FirstOrDefault(static group => group.Count() > 1)?.Key; if (duplicate is not null) throw new InvalidOperationException($"Editor panel id '{duplicate}' is registered more than once."); } private static void ValidateModals(ModalRegistration[] modals) { - string? duplicate = modals - .GroupBy(static value => value.attribute.id, StringComparer.Ordinal) - .FirstOrDefault(static group => group.Count() > 1)?.Key; + string? duplicate = modals.GroupBy(static value => value.attribute.id, StringComparer.Ordinal).FirstOrDefault(static group => group.Count() > 1)?.Key; if (duplicate is not null) throw new InvalidOperationException($"Editor modal id '{duplicate}' is registered more than once."); } private static void ValidateHistoryHandlers(HistoryHandlerRegistration[] handlers) { - string? duplicate = handlers - .GroupBy(static value => value.attribute.kind, StringComparer.Ordinal) - .FirstOrDefault(static group => group.Count() > 1)?.Key; + string? duplicate = handlers.GroupBy(static value => value.attribute.kind, StringComparer.Ordinal).FirstOrDefault(static group => group.Count() > 1)?.Key; if (duplicate is not null) { - throw new InvalidOperationException( - $"Editor history handler kind '{duplicate}' is registered more than once."); + throw new InvalidOperationException($"Editor history handler kind '{duplicate}' is registered more than once."); } } - private static IReadOnlyDictionary CreateHistoryHandlerMap( - IReadOnlyList handlers) + private static IReadOnlyDictionary CreateHistoryHandlerMap(IReadOnlyList handlers) { var result = new Dictionary(handlers.Count, StringComparer.Ordinal); for (int i = 0; i < handlers.Count; i++) @@ -995,14 +892,12 @@ private static void ValidateExtensionIds( IReadOnlyList modules, IReadOnlyList panels ) { - string? duplicate = modules - .Select(static value => value.attribute.id) - .Concat(panels.Select(static value => value.attribute.id)) - .GroupBy(static id => id, StringComparer.Ordinal) - .FirstOrDefault(static group => group.Count() > 1)?.Key; + string? duplicate = modules.Select(static value => value.attribute.id).Concat(panels.Select(static value => value.attribute.id)).GroupBy( + static id => id, + StringComparer.Ordinal + ).FirstOrDefault(static group => group.Count() > 1)?.Key; if (duplicate is not null) - throw new InvalidOperationException( - $"Editor extension id '{duplicate}' is registered more than once."); + throw new InvalidOperationException($"Editor extension id '{duplicate}' is registered more than once."); } private static StateRegistration[] CreateStateRegistrations( @@ -1013,30 +908,20 @@ IReadOnlyList panels for (int i = 0; i < modules.Count; i++) { ModuleRegistration module = modules[i]; - StateRegistration? registration = CreateStateRegistration( - module.attribute.id, - StateOwnerKind.Module, - module.type, - module.module, - typeof(EditorModule)); + StateRegistration? registration = CreateStateRegistration(module.attribute.id, StateOwnerKind.Module, module.type, module.module, typeof(EditorModule)); if (registration is not null) result.Add(registration); } + for (int i = 0; i < panels.Count; i++) { PanelRegistration panel = panels[i]; - StateRegistration? registration = CreateStateRegistration( - panel.attribute.id, - StateOwnerKind.Panel, - panel.type, - panel.panel, - typeof(EditorPanel)); + StateRegistration? registration = CreateStateRegistration(panel.attribute.id, StateOwnerKind.Panel, panel.type, panel.panel, typeof(EditorPanel)); if (registration is not null) result.Add(registration); } - return result - .OrderBy(static value => value.id, StringComparer.Ordinal) - .ToArray(); + + return result.OrderBy(static value => value.id, StringComparer.Ordinal).ToArray(); } private static StateRegistration? CreateStateRegistration( @@ -1052,8 +937,8 @@ Type baseType flags, binder: null, [typeof(EditorState)], - modifiers: null) ?? throw new InvalidOperationException( - $"Editor extension '{type.FullName}' has no state capture hook."); + modifiers: null + ) ?? throw new InvalidOperationException($"Editor extension '{type.FullName}' has no state capture hook."); if (capture.DeclaringType == baseType) return null; MethodInfo restore = type.GetMethod( @@ -1061,14 +946,9 @@ Type baseType flags, binder: null, [typeof(EditorState)], - modifiers: null) ?? throw new InvalidOperationException( - $"Editor extension '{type.FullName}' has no state restore hook."); - return new StateRegistration( - id, - kind, - owner, - capture.CreateDelegate>(owner), - restore.CreateDelegate>(owner)); + modifiers: null + ) ?? throw new InvalidOperationException($"Editor extension '{type.FullName}' has no state restore hook."); + return new StateRegistration(id, kind, owner, capture.CreateDelegate>(owner), restore.CreateDelegate>(owner)); } internal sealed record Snapshot( @@ -1081,21 +961,14 @@ internal sealed record Snapshot( HistoryHandlerRegistration[] historyHandlers, StateRegistration[] state, object[] instances - ) { - internal HashSet quarantinedModules { get; } = - new(ReferenceEqualityComparer.Instance); - - internal HashSet quarantinedPanels { get; } = - new(ReferenceEqualityComparer.Instance); - - internal HashSet quarantinedModals { get; } = - new(ReferenceEqualityComparer.Instance); - - internal HashSet startedModules { get; } = - new(ReferenceEqualityComparer.Instance); - - internal HashSet attachedPanels { get; } = - new(ReferenceEqualityComparer.Instance); + ) + { + internal IReadOnlyList panelDeclarations { get; } = Array.AsReadOnly(panels.Select(static registration => registration.attribute).ToArray()); + internal HashSet quarantinedModules { get; } = new(ReferenceEqualityComparer.Instance); + internal HashSet quarantinedPanels { get; } = new(ReferenceEqualityComparer.Instance); + internal HashSet quarantinedModals { get; } = new(ReferenceEqualityComparer.Instance); + internal HashSet startedModules { get; } = new(ReferenceEqualityComparer.Instance); + internal HashSet attachedPanels { get; } = new(ReferenceEqualityComparer.Instance); } internal sealed record ModuleRegistration( @@ -1103,7 +976,6 @@ internal sealed record ModuleRegistration( Type type, EditorModule module ); - internal sealed record ActionRegistration( EditorActionAttribute attribute, Type type, @@ -1113,12 +985,10 @@ internal sealed record ActionRegistration( EditorMenuAttribute[] menus, EditorToolbarItemAttribute[] toolbars, EditorShortcutAttribute[] shortcuts - ) { + ) + { internal string id { get; } = attribute.action; - - internal string? area { get; } = string.IsNullOrEmpty(attribute.area) - ? null - : attribute.area; + internal string? area { get; } = string.IsNullOrEmpty(attribute.area) ? null : attribute.area; } internal sealed record MenuSourceRegistration( @@ -1127,7 +997,6 @@ internal sealed record MenuSourceRegistration( Type type, EditorMenuSource source ); - internal sealed record DropRegistration( Type sourceType, Type targetType, @@ -1136,25 +1005,21 @@ internal sealed record DropRegistration( Type type, EditorDrop drop ); - internal sealed record PanelRegistration( EditorPanelAttribute attribute, Type type, EditorPanel panel ); - internal sealed record ModalRegistration( EditorModalAttribute attribute, Type type, EditorModal modal ); - internal sealed record HistoryHandlerRegistration( EditorHistoryHandlerAttribute attribute, Type type, EditorHistoryHandler handler ); - internal sealed record StateRegistration( string id, StateOwnerKind kind, @@ -1162,7 +1027,6 @@ internal sealed record StateRegistration( Action capture, Action restore ); - internal enum StateOwnerKind { Module, @@ -1173,19 +1037,18 @@ private readonly record struct PanelState( bool isOpen, ReadOnlyMemory payload ); - private readonly record struct ShortcutValidationEntry( ActionRegistration registration, string area, HotKeyGesture gesture ); - private sealed class ActivationState( Snapshot? previous, Snapshot candidate, HashSet existing, EditorHistory.HandlerUpdate handlers - ) { + ) + { internal Snapshot? previous { get; } = previous; internal Snapshot candidate { get; } = candidate; internal HashSet existing { get; } = existing; diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentService.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentService.cs index 7bf862880..c42f94cb9 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentService.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Documents/EditorDocumentService.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; +using Inno.Core.Events; namespace Inno.Editor.Interactions; - internal sealed class EditorDocumentService : IEditorDocumentService { private readonly List m_documents = []; private readonly Dictionary m_providers = new(StringComparer.Ordinal); - - internal EditorDocumentService() { } - + private readonly EventDispatcher m_events; + internal EditorDocumentService(EventDispatcher events) => m_events = events; /// /// Gets an immutable snapshot of currently owned source documents. /// @@ -46,6 +45,7 @@ public IDisposable RegisterProvider(EditorDocumentProvider provider) Unregister(provider); throw; } + return new ProviderLease(this, provider); } @@ -66,28 +66,27 @@ public EditorDocumentContext Open( Guid assetId = default ) { string normalizedPath = NormalizePath(assetPath); - EditorDocumentContext? identityOwner = assetId == Guid.Empty - ? null - : m_documents.FirstOrDefault(document => document.assetId == assetId); + EditorDocumentContext? identityOwner = assetId == Guid.Empty ? null : m_documents.FirstOrDefault(document => document.assetId == assetId); EditorDocumentContext? pathOwner = FindPathOwner(normalizedPath); if (identityOwner is not null && pathOwner is not null && !ReferenceEquals(identityOwner, pathOwner)) RetireStalePathOwner(pathOwner, normalizedPath); - EditorDocumentContext? existing = identityOwner ?? pathOwner; if (existing is not null && assetId != Guid.Empty && existing.assetId != assetId) { RetireStalePathOwner(existing, normalizedPath); existing = null; } + if (existing is not null) { _ = UpdateAssetPath(existing.documentId, normalizedPath); return existing; } - EditorDocumentProvider provider = m_providers.Values - .OrderBy(static candidate => candidate.id, StringComparer.Ordinal) - .FirstOrDefault(candidate => candidate.CanOpen(normalizedPath)) - ?? throw new InvalidOperationException($"No editor document provider can open '{normalizedPath}'."); + + EditorDocumentProvider provider = m_providers.Values.OrderBy( + static candidate => candidate.id, + StringComparer.Ordinal + ).FirstOrDefault(candidate => candidate.CanOpen(normalizedPath)) ?? throw new InvalidOperationException($"No editor document provider can open '{normalizedPath}'."); var context = new EditorDocumentContext(Guid.NewGuid(), assetId, normalizedPath, provider.id) { isProviderAvailable = true @@ -102,6 +101,7 @@ public EditorDocumentContext Open( m_documents.Remove(context); throw; } + return context; } @@ -150,7 +150,6 @@ public void SetDirty( Guid documentId, bool isDirty = true ) => Get(documentId).isDirty = isDirty; - /// /// Persists the supplied value through the configured storage contract. /// @@ -164,7 +163,6 @@ public bool Save(Guid documentId) => Invoke(documentId, static ( provider, context ) => provider.Save(context), clearDirty: true); - /// /// Saves every dirty document through its owning provider. /// @@ -179,6 +177,7 @@ public bool SaveAll() if (document.isDirty && !Save(document.documentId)) succeeded = false; } + return succeeded; } @@ -195,7 +194,6 @@ public bool Apply(Guid documentId) => Invoke(documentId, static ( provider, context ) => provider.Apply(context), clearDirty: true); - /// /// Reverts one open document through its owning provider. /// @@ -209,7 +207,6 @@ public bool Revert(Guid documentId) => Invoke(documentId, static ( provider, context ) => provider.Revert(context), clearDirty: true); - /// /// Closes the requested resource and releases its operation-scoped state. /// @@ -226,6 +223,7 @@ public bool Close( Guid documentId, EditorDocumentCloseMode mode ) { + m_events.Emit(new EditorOperationPreparingEvent()); EditorDocumentContext? document = Find(documentId); if (document is null || mode == EditorDocumentCloseMode.Cancel) return false; @@ -246,6 +244,7 @@ internal void Shutdown() if (m_providers.TryGetValue(document.providerId, out EditorDocumentProvider? provider)) provider.Close(document); } + m_documents.Clear(); m_providers.Clear(); } @@ -255,6 +254,7 @@ private bool Invoke( Func operation, bool clearDirty ) { + m_events.Emit(new EditorOperationPreparingEvent()); EditorDocumentContext document = Get(documentId); if (!m_providers.TryGetValue(document.providerId, out EditorDocumentProvider? provider)) return false; @@ -264,29 +264,21 @@ bool clearDirty return succeeded; } - private EditorDocumentContext Get(Guid documentId) - => Find(documentId) - ?? throw new ArgumentException($"Editor document '{documentId}' is not open.", nameof(documentId)); - + private EditorDocumentContext Get(Guid documentId) => Find(documentId) ?? throw new ArgumentException($"Editor document '{documentId}' is not open.", nameof(documentId)); private EditorDocumentContext? Find(Guid documentId) => m_documents.FirstOrDefault(document => document.documentId == documentId); - private EditorDocumentContext? FindPathOwner( string assetPath, Guid excludedDocumentId = default - ) - => m_documents.FirstOrDefault(document => document.documentId != excludedDocumentId - && string.Equals(document.assetPath, assetPath, StringComparison.Ordinal)); - + ) => m_documents.FirstOrDefault(document => document.documentId != excludedDocumentId && string.Equals(document.assetPath, assetPath, StringComparison.Ordinal)); private void RetireStalePathOwner( EditorDocumentContext document, string destinationPath ) { if (document.isDirty) { - throw new InvalidOperationException( - $"Open document '{document.title}' has unsaved changes for '{destinationPath}'. " + - "Save, revert, or close it before assigning that source path to another asset identity."); + throw new InvalidOperationException($"Open document '{document.title}' has unsaved changes for '{destinationPath}'. " + "Save, revert, or close it before assigning that source path to another asset identity."); } + if (m_providers.TryGetValue(document.providerId, out EditorDocumentProvider? provider)) provider.Close(document); if (!m_documents.Remove(document)) @@ -304,11 +296,11 @@ private static string NormalizePath(string assetPath) private void Unregister(EditorDocumentProvider provider) { - if (!m_providers.TryGetValue(provider.id, out EditorDocumentProvider? registered) - || !ReferenceEquals(provider, registered)) + if (!m_providers.TryGetValue(provider.id, out EditorDocumentProvider? registered) || !ReferenceEquals(provider, registered)) { return; } + m_providers.Remove(provider.id); foreach (EditorDocumentContext document in m_documents.Where(document => document.providerId == provider.id)) document.isProviderAvailable = false; @@ -320,11 +312,10 @@ EditorDocumentProvider provider ) : IDisposable { private EditorDocumentService? m_owner = owner; - /// /// Releases the resources owned by this instance. /// -public void Dispose() + public void Dispose() { EditorDocumentService? current = m_owner; m_owner = null; diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs new file mode 100644 index 000000000..5268bad6c --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorEditGesture.cs @@ -0,0 +1,96 @@ +using System; + +namespace Inno.Editor.Interactions; +/// +/// Owns one continuous edit transaction without retaining edited objects, setters or extension instances. +/// +public sealed class EditorEditGesture : IDisposable +{ + private EditorHistoryTransaction? m_transaction; + /// + /// Begins an edit whose updates use the existing domain mutation and History protocols. + /// + /// + /// The authoritative History for the current editor session. + /// + /// + /// The operation name displayed by Undo and Redo after a successful commit. + /// + /// + /// The History owner is null. + /// + /// + /// History cannot begin a transaction in its current state. + /// + public EditorEditGesture( + IEditorHistory history, + string name + ) { + ArgumentNullException.ThrowIfNull(history); + m_transaction = history.BeginTransaction(name); + mergeKey = "gesture:" + m_transaction.id.ToString("N"); + } + + /// + /// Gets the transient merge boundary shared only by updates within this gesture. + /// + public string mergeKey { get; } + /// + /// Gets whether this gesture still owns an active transaction, including a rejected completion. + /// + public bool isActive => m_transaction is not null; + + /// + /// Commits the edit exactly once; rejected completion leaves the transaction available for retry. + /// + /// + /// The gesture is finished or a nested transaction remains active. + /// + public void Commit() + { + EditorHistoryTransaction transaction = RequireTransaction(); + transaction.Commit(); + m_transaction = null; + transaction.Dispose(); + } + + /// + /// Attempts to restore the state before the gesture without discarding a failed rollback. + /// + /// + /// The rollback outcome; a state-preserving failure keeps this gesture active for retry. + /// + /// + /// The gesture is finished or a nested transaction remains active. + /// + public EditorHistoryResult Cancel() + { + EditorHistoryTransaction transaction = RequireTransaction(); + EditorHistoryResult result = transaction.Rollback(); + if (result.succeeded) + { + m_transaction = null; + transaction.Dispose(); + } + + return result; + } + + /// + /// Cancels an unfinished gesture and releases its ownership only after rollback succeeds. + /// + /// + /// Rollback is blocked or a nested transaction remains active. + /// + public void Dispose() + { + if (m_transaction is null) + return; + EditorHistoryResult result = Cancel(); + if (!result.succeeded) + throw new InvalidOperationException($"The edit gesture could not roll back: {result.message}"); + GC.SuppressFinalize(this); + } + + private EditorHistoryTransaction RequireTransaction() => m_transaction ?? throw new InvalidOperationException("The edit gesture has already finished."); +} diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs new file mode 100644 index 000000000..a6472e6f0 --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Editing/EditorOperationPreparingEvent.cs @@ -0,0 +1,10 @@ +using Inno.Core.Events; + +namespace Inno.Editor.Interactions; +/// +/// Synchronously closes transient edits before command execution or an extension-handler generation switch. +/// +/// +/// A handler failure aborts the boundary. Subscribers must use owned Core Event hubs and release them during retirement. +/// +public sealed class EditorOperationPreparingEvent : Event; diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistory.cs b/src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistory.cs index c2fdc38c3..f46671a4f 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistory.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/History/EditorHistory.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; - using Inno.Core.Logging; using Inno.Editor.Core; namespace Inno.Editor.Interactions; - /// /// Owns the bounded, transactional Undo and Redo history for one editor runtime. /// @@ -18,16 +16,13 @@ internal sealed class EditorHistory : IEditorHistory, IDisposable private readonly EditorHistoryOptions m_options; private readonly EditorHistoryBlobStore m_blobStore; private readonly Logger m_log; - - private IReadOnlyDictionary m_handlers = - new Dictionary(StringComparer.Ordinal); + private IReadOnlyDictionary m_handlers = new Dictionary(StringComparer.Ordinal); private EditorHistoryContext? m_context; private IsolationState? m_isolation; private bool m_isTransitioning; private bool m_isFaulted; private string? m_faultReason; private bool m_isDisposed; - /// /// Creates an empty history with explicit entry, resident-memory, and temporary-disk budgets. /// @@ -59,68 +54,70 @@ Logger log /// Gets the maximum number of committed top-level operations retained by this history. /// public int capacity { get; } - /// /// Gets whether a previous operation is currently available and permits Undo. /// - public bool canUndo => !m_isFaulted && !m_isTransitioning && m_transactions.Count == 0 && - m_undo.Count > 0 && m_undo[^1].canUndo; - + public bool canUndo => !m_isFaulted && !m_isTransitioning && m_transactions.Count == 0 && m_undo.Count > 0 && m_undo[^1].canUndo; /// /// Gets whether a reverted operation is currently available and permits Redo. /// - public bool canRedo => !m_isFaulted && !m_isTransitioning && m_transactions.Count == 0 && - m_redo.Count > 0 && m_redo[^1].canRedo; - + public bool canRedo => !m_isFaulted && !m_isTransitioning && m_transactions.Count == 0 && m_redo.Count > 0 && m_redo[^1].canRedo; /// /// Gets whether a failed compensation left the domain state indeterminate. /// public bool isFaulted => m_isFaulted; - /// /// Gets the diagnostic that faulted this history, or while the history is healthy. /// public string? faultReason => m_faultReason; - /// /// Gets the next operation name displayed by the Undo command, or when unavailable. /// public string? undoName => m_undo.Count == 0 ? null : m_undo[^1].name; - /// /// Gets the next operation name displayed by the Redo command, or when unavailable. /// public string? redoName => m_redo.Count == 0 ? null : m_redo[^1].name; - /// /// Gets the diagnostic explaining why the newest Undo entry is a barrier, or when available. /// public string? undoUnavailableReason => GetUnavailableReason(m_undo, EditorHistoryDirection.Undo); - /// /// Gets the diagnostic explaining why the newest Redo entry is a barrier, or when available. /// public string? redoUnavailableReason => GetUnavailableReason(m_redo, EditorHistoryDirection.Redo); - /// /// Gets the estimated resident payload bytes retained by committed Undo and Redo entries. /// - public long residentBytes => Sum(m_undo, static operation => operation.estimatedMemorySize) + - Sum(m_redo, static operation => operation.estimatedMemorySize) + - (m_isolation is IsolationState isolation - ? Sum(isolation.undo, static operation => operation.estimatedMemorySize) + - Sum(isolation.redo, static operation => operation.estimatedMemorySize) - : 0L); - + public long residentBytes => Sum( + m_undo, + static operation => operation.estimatedMemorySize + ) + Sum( + m_redo, + static operation => operation.estimatedMemorySize + ) + (m_isolation is IsolationState isolation ? Sum( + isolation.undo, + static operation => operation.estimatedMemorySize + ) + Sum( + isolation.redo, + static operation => operation.estimatedMemorySize + ) : 0L); /// /// Gets the estimated temporary disk payload bytes retained by committed Undo and Redo entries. /// - public long diskBytes => Sum(m_undo, static operation => operation.estimatedDiskSize) + - Sum(m_redo, static operation => operation.estimatedDiskSize) + - (m_isolation is IsolationState isolation - ? Sum(isolation.undo, static operation => operation.estimatedDiskSize) + - Sum(isolation.redo, static operation => operation.estimatedDiskSize) - : 0L); + public long diskBytes => Sum( + m_undo, + static operation => operation.estimatedDiskSize + ) + Sum( + m_redo, + static operation => operation.estimatedDiskSize + ) + (m_isolation is IsolationState isolation ? Sum( + isolation.undo, + static operation => operation.estimatedDiskSize + ) + Sum( + isolation.redo, + static operation => operation.estimatedDiskSize + ) : 0L); /// /// Begins an atomic group whose child operations appear as one Undo entry. @@ -184,6 +181,7 @@ internal EditorHistoryResult Execute( ObserveFailure(result); operation.Dispose(); } + return result; } @@ -221,6 +219,7 @@ EditorHistoryChange change { change.Dispose(); } + EditorHistoryResult result = Invoke(operation.RedoInternal, name, "execute"); if (result.succeeded) Record(operation); @@ -229,6 +228,7 @@ EditorHistoryChange change ObserveFailure(result); operation.Dispose(); } + return result; } @@ -322,7 +322,6 @@ object mergeKey /// The transition result. A failed operation remains available for retry. /// public EditorHistoryResult Undo() => Transition(m_undo, m_redo, undo: true); - /// /// Attempts to reapply the newest reverted operation. /// @@ -330,7 +329,6 @@ object mergeKey /// The transition result. A failed operation remains available for retry. /// public EditorHistoryResult Redo() => Transition(m_redo, m_undo, undo: false); - /// /// Starts a temporary empty history branch while retaining the current branch for restoration. /// @@ -347,12 +345,7 @@ internal IDisposable BeginIsolation() throw new InvalidOperationException("Editor history cannot be isolated during a transaction."); if (m_isolation is not null) throw new InvalidOperationException("Editor history already owns an isolated branch."); - - var isolation = new IsolationState( - m_undo, - m_redo, - m_isFaulted, - m_faultReason); + var isolation = new IsolationState(m_undo, m_redo, m_isFaulted, m_faultReason); m_isolation = isolation; m_undo = []; m_redo = []; @@ -378,6 +371,7 @@ internal void Clear() DisposeAll(isolation.redo); m_isolation = null; } + while (m_transactions.TryPop(out TransactionOperation? transaction)) transaction.Dispose(); m_isFaulted = false; @@ -407,6 +401,7 @@ internal void CommitTransaction(EditorHistoryTransaction transaction) operation.Dispose(); return; } + Record(operation); } @@ -425,6 +420,7 @@ internal EditorHistoryResult RollbackTransaction(EditorHistoryTransaction transa { ObserveFailure(result); } + return result; } @@ -442,6 +438,7 @@ private void Record(EditorHistoryOperation operation) operation.Dispose(); return; } + m_undo.Add(operation); int retainedUndoCount = m_isolation?.undo.Count ?? 0; while (m_undo.Count > 0 && m_undo.Count + retainedUndoCount > capacity) @@ -449,6 +446,7 @@ private void Record(EditorHistoryOperation operation) m_undo[0].Dispose(); m_undo.RemoveAt(0); } + EnforceBudgets(); } @@ -462,20 +460,17 @@ bool undo return EditorHistoryResult.Failure("Undo and Redo are unavailable while a transaction is active."); if (source.Count == 0) return EditorHistoryResult.Failure(undo ? "Nothing to undo." : "Nothing to redo."); - EditorHistoryOperation operation = source[^1]; m_isTransitioning = true; try { - EditorHistoryResult result = Invoke( - undo ? operation.UndoInternal : operation.RedoInternal, - operation.name, - undo ? "undo" : "redo"); + EditorHistoryResult result = Invoke(undo ? operation.UndoInternal : operation.RedoInternal, operation.name, undo ? "undo" : "redo"); if (!result.succeeded) { ObserveFailure(result); return result; } + source.RemoveAt(source.Count - 1); destination.Add(operation); return result; @@ -488,11 +483,8 @@ bool undo private TransactionOperation PopTransaction(Guid id) { - if (!m_transactions.TryPop(out TransactionOperation? operation) || - operation.id != id) - { - throw new InvalidOperationException("Editor history transactions must complete in stack order."); - } + TransactionOperation operation = PeekTransaction(id); + _ = m_transactions.Pop(); return operation; } @@ -508,9 +500,9 @@ private void EnsureMutable() ObjectDisposedException.ThrowIf(m_isDisposed, this); if (m_isFaulted) { - throw new InvalidOperationException( - $"Editor history is faulted and must be cleared before further use. {m_faultReason}"); + throw new InvalidOperationException($"Editor history is faulted and must be cleared before further use. {m_faultReason}"); } + if (m_isTransitioning) throw new InvalidOperationException("History cannot be modified while Undo or Redo is executing."); } @@ -521,10 +513,7 @@ private void ObserveFailure(EditorHistoryResult result) return; m_isFaulted = true; m_faultReason = result.message; - m_log.Write( - LogLevel.Error, - "Editor history was faulted because state integrity was lost: {0}", - [result.message]); + m_log.Write(LogLevel.Error, "Editor history was faulted because state integrity was lost: {0}", [result.message]); } internal void Attach( @@ -542,17 +531,15 @@ internal HandlerUpdate PrepareHandlerUpdate(IReadOnlyDictionary change.Retain( - m_blobStore, - m_options.inlinePayloadThreshold, - !string.IsNullOrWhiteSpace(m_options.cacheDirectory)); - + private EditorHistoryChange RetainChange(EditorHistoryChange change) => change.Retain( + m_blobStore, + m_options.inlinePayloadThreshold, + !string.IsNullOrWhiteSpace(m_options.cacheDirectory) + ); private bool TryGetHandler( string kind, out EditorHistoryHandler? handler ) => m_handlers.TryGetValue(kind, out handler); - private EditorHistoryAvailability Query( EditorHistoryChange change, EditorHistoryDirection direction @@ -561,9 +548,9 @@ EditorHistoryDirection direction return EditorHistoryAvailability.Unavailable("The editor history is not attached to an interaction runtime."); if (!TryGetHandler(change.kind, out EditorHistoryHandler? handler) || handler is null) { - return EditorHistoryAvailability.Unavailable( - $"Required history handler '{change.kind}' is not loaded."); + return EditorHistoryAvailability.Unavailable($"Required history handler '{change.kind}' is not loaded."); } + try { return handler.QueryInternal(m_context, change, direction); @@ -591,21 +578,32 @@ private bool TryMerge( out EditorHistoryChange? merged ) { merged = null; - if (!string.Equals(older.kind, newer.kind, StringComparison.Ordinal) || - older.mergeKey is null || - !string.Equals(older.mergeKey, newer.mergeKey, StringComparison.Ordinal) || - !TryGetHandler(older.kind, out EditorHistoryHandler? handler) || - handler is null) + if (!string.Equals( + older.kind, + newer.kind, + StringComparison.Ordinal + ) + || older.mergeKey is null + || !string.Equals( + older.mergeKey, + newer.mergeKey, + StringComparison.Ordinal + ) + || !TryGetHandler( + older.kind, + out EditorHistoryHandler? handler + ) + || handler is null) { return false; } + return handler.TryMergeInternal(older, newer, out merged); } private void EnforceBudgets() { - while (m_undo.Count > 0 && - (residentBytes > m_options.maxResidentBytes || diskBytes > m_options.maxDiskBytes)) + while (m_undo.Count > 0 && (residentBytes > m_options.maxResidentBytes || diskBytes > m_options.maxDiskBytes)) { m_undo[0].Dispose(); m_undo.RemoveAt(0); @@ -636,6 +634,7 @@ List redo undo[i].Dispose(); undo.RemoveRange(0, newestUnsafe + 1); } + if (redo.Exists(static operation => !operation.isReloadSafe)) DisposeAll(redo); } @@ -667,9 +666,8 @@ EditorHistoryDirection direction EditorHistoryAvailability availability = Query(data.change, direction); return availability.isAvailable ? null : availability.message; } - bool available = direction == EditorHistoryDirection.Undo - ? operation.canUndo - : operation.canRedo; + + bool available = direction == EditorHistoryDirection.Undo ? operation.canUndo : operation.canRedo; return available ? null : $"'{operation.name}' cannot currently be {direction.ToString().ToLowerInvariant()}."; } @@ -692,20 +690,13 @@ string transition { EditorHistoryResult result = callback(); if (!result.succeeded) - m_log.Write( - LogLevel.Warn, - "Editor history operation '{0}' could not {1}: {2}", - [name, transition, result.message]); + m_log.Write(LogLevel.Warn, "Editor history operation '{0}' could not {1}: {2}", [name, transition, result.message]); return result; } catch (Exception exception) { - m_log.Write( - LogLevel.Error, - "Editor history operation '{0}' failed to {1}: {2}", - [name, transition, exception]); - return EditorHistoryResult.StateIntegrityLost( - $"The {transition} callback for '{name}' threw before it could prove rollback: {exception.Message}"); + m_log.Write(LogLevel.Error, "Editor history operation '{0}' failed to {1}: {2}", [name, transition, exception]); + return EditorHistoryResult.StateIntegrityLost($"The {transition} callback for '{name}' threw before it could prove rollback: {exception.Message}"); } } @@ -720,10 +711,10 @@ internal sealed class HandlerUpdate( EditorHistory owner, IReadOnlyDictionary previous, IReadOnlyDictionary candidate - ) { + ) + { private bool m_activated; private bool m_finished; - internal void Activate() { if (m_finished) @@ -756,10 +747,7 @@ internal void Complete() } catch (Exception exception) { - owner.m_log.Write( - LogLevel.Error, - "Editor history could not release reload-unsafe entries: {0}", - [exception]); + owner.m_log.Write(LogLevel.Error, "Editor history could not release reload-unsafe entries: {0}", [exception]); } } } @@ -770,14 +758,12 @@ private sealed record IsolationState( bool isFaulted, string? faultReason ); - private sealed class Isolation( EditorHistory owner, IsolationState state ) : IDisposable { private EditorHistory? m_owner = owner; - /// /// Releases the resources owned by this instance. /// @@ -796,7 +782,6 @@ private sealed class DelegateOperation : EditorHistoryOperation private readonly Func m_undo; private readonly object? m_mergeKey; private Func m_redo; - internal DelegateOperation( string operationName, Func undo, @@ -821,7 +806,6 @@ internal DelegateOperation( /// The validated editor history result that represents the completed operation. /// protected override EditorHistoryResult Undo() => m_undo(); - /// /// Reapplies the state represented by this history operation. /// @@ -829,7 +813,6 @@ internal DelegateOperation( /// The validated editor history result that represents the completed operation. /// protected override EditorHistoryResult Redo() => m_redo(); - /// /// Attempts to merge without changing state when the operation cannot complete. /// @@ -841,11 +824,11 @@ internal DelegateOperation( /// protected override bool TryMerge(EditorHistoryOperation newer) { - if (m_mergeKey is null || newer is not DelegateOperation candidate || - !Equals(m_mergeKey, candidate.m_mergeKey)) + if (m_mergeKey is null || newer is not DelegateOperation candidate || !Equals(m_mergeKey, candidate.m_mergeKey)) { return false; } + m_redo = candidate.m_redo; return true; } @@ -856,7 +839,6 @@ private sealed class DataOperation : EditorHistoryOperation private readonly EditorHistory m_owner; private readonly string m_name; private EditorHistoryChange m_change; - internal DataOperation( EditorHistory owner, string operationName, @@ -871,32 +853,26 @@ EditorHistoryChange change /// Gets the human-readable name used for presentation and diagnostics. /// public override string name => m_name; - /// /// Gets whether this value can undo. /// public override bool canUndo => m_owner.Query(m_change, EditorHistoryDirection.Undo).isAvailable; - /// /// Gets whether this value can redo. /// public override bool canRedo => m_owner.Query(m_change, EditorHistoryDirection.Redo).isAvailable; - /// /// Gets whether this value is reload safe. /// public override bool isReloadSafe => true; - /// /// Gets the estimated resident-memory cost in bytes. /// public override long estimatedMemorySize => m_change.residentSize; - /// /// Gets the estimated spill-file cost in bytes. /// public override long estimatedDiskSize => m_change.diskSize; - internal EditorHistoryChange change => m_change; /// @@ -906,7 +882,6 @@ EditorHistoryChange change /// The validated editor history result that represents the completed operation. /// protected override EditorHistoryResult Undo() => m_owner.Apply(m_change, EditorHistoryDirection.Undo); - /// /// Reapplies the state represented by this history operation. /// @@ -914,7 +889,6 @@ EditorHistoryChange change /// The validated editor history result that represents the completed operation. /// protected override EditorHistoryResult Redo() => m_owner.Apply(m_change, EditorHistoryDirection.Redo); - /// /// Attempts to merge without changing state when the operation cannot complete. /// @@ -926,12 +900,11 @@ EditorHistoryChange change /// protected override bool TryMerge(EditorHistoryOperation newer) { - if (newer is not DataOperation candidate || - !m_owner.TryMerge(m_change, candidate.m_change, out EditorHistoryChange? merged) || - merged is null) + if (newer is not DataOperation candidate || !m_owner.TryMerge(m_change, candidate.m_change, out EditorHistoryChange? merged) || merged is null) { return false; } + EditorHistoryChange retained; try { @@ -941,6 +914,7 @@ protected override bool TryMerge(EditorHistoryOperation newer) { merged.Dispose(); } + m_change.Dispose(); m_change = retained; return true; @@ -962,13 +936,11 @@ protected override void Dispose(bool disposing) private sealed class ValueOperation : EditorHistoryOperation { private const double C_MERGE_WINDOW_SECONDS = 1.0; - private readonly T m_before; private readonly Action m_apply; private readonly object m_mergeKey; private T m_after; private long m_lastEditTimestamp; - internal ValueOperation( string operationName, T before, @@ -1024,10 +996,15 @@ protected override EditorHistoryResult Redo() /// protected override bool TryMerge(EditorHistoryOperation newer) { - if (newer is not ValueOperation candidate || - !Equals(m_mergeKey, candidate.m_mergeKey) || - Stopwatch.GetElapsedTime(m_lastEditTimestamp, candidate.m_lastEditTimestamp).TotalSeconds > - C_MERGE_WINDOW_SECONDS) + if (newer is not ValueOperation candidate + || !Equals( + m_mergeKey, + candidate.m_mergeKey + ) + || Stopwatch.GetElapsedTime( + m_lastEditTimestamp, + candidate.m_lastEditTimestamp + ).TotalSeconds > C_MERGE_WINDOW_SECONDS) return false; m_after = candidate.m_after; m_lastEditTimestamp = candidate.m_lastEditTimestamp; @@ -1041,42 +1018,43 @@ Guid transactionId ) : EditorHistoryOperation { private readonly List m_children = []; - /// /// Gets the human-readable name used for presentation and diagnostics. /// public override string name => operationName; - internal Guid id => transactionId; - internal int count => m_children.Count; - /// /// Gets whether this value is reload safe. /// public override bool isReloadSafe => m_children.TrueForAll(static operation => operation.isReloadSafe); - /// /// Gets whether this value can undo. /// public override bool canUndo => m_children.TrueForAll(static operation => operation.canUndo); - /// /// Gets whether this value can redo. /// public override bool canRedo => m_children.TrueForAll(static operation => operation.canRedo); - /// /// Gets the estimated resident-memory cost in bytes. /// public override long estimatedMemorySize => Sum(m_children, static operation => operation.estimatedMemorySize); - /// /// Gets the estimated spill-file cost in bytes. /// public override long estimatedDiskSize => Sum(m_children, static operation => operation.estimatedDiskSize); - internal void Add(EditorHistoryOperation operation) => m_children.Add(operation); + internal void Add(EditorHistoryOperation operation) + { + if (m_children.Count > 0 && m_children[^1].TryMergeInternal(operation)) + { + operation.Dispose(); + return; + } + + m_children.Add(operation); + } /// /// Restores the state that preceded this history operation. @@ -1094,15 +1072,14 @@ protected override EditorHistoryResult Undo() continue; for (int rollback = i + 1; rollback < m_children.Count; rollback++) { - EditorHistoryResult compensation = InvokeChild( - m_children[rollback].RedoInternal, - m_children[rollback].name, - "redo compensation"); + EditorHistoryResult compensation = InvokeChild(m_children[rollback].RedoInternal, m_children[rollback].name, "redo compensation"); if (!compensation.succeeded) compensationFailures.Add($"'{m_children[rollback].name}': {compensation.message}"); } + return CombineFailure(result, compensationFailures, "undo"); } + return EditorHistoryResult.Success(); } @@ -1122,15 +1099,14 @@ protected override EditorHistoryResult Redo() continue; for (int rollback = i - 1; rollback >= 0; rollback--) { - EditorHistoryResult compensation = InvokeChild( - m_children[rollback].UndoInternal, - m_children[rollback].name, - "undo compensation"); + EditorHistoryResult compensation = InvokeChild(m_children[rollback].UndoInternal, m_children[rollback].name, "undo compensation"); if (!compensation.succeeded) compensationFailures.Add($"'{m_children[rollback].name}': {compensation.message}"); } + return CombineFailure(result, compensationFailures, "redo"); } + return EditorHistoryResult.Success(); } @@ -1145,8 +1121,7 @@ string transition } catch (Exception exception) { - return EditorHistoryResult.StateIntegrityLost( - $"Child '{childName}' threw during {transition}: {exception.Message}"); + return EditorHistoryResult.StateIntegrityLost($"Child '{childName}' threw during {transition}: {exception.Message}"); } } @@ -1157,11 +1132,11 @@ string transition ) { if (original.statePreserved && compensationFailures.Count == 0) return original; - string compensation = compensationFailures.Count == 0 - ? "The failing child could not prove that it preserved its input state." - : $"Compensation failures: {string.Join("; ", compensationFailures)}"; - return EditorHistoryResult.StateIntegrityLost( - $"Transaction {transition} failed: {original.message} {compensation}"); + string compensation = compensationFailures.Count == 0 ? "The failing child could not prove that it preserved its input state." : $"Compensation failures: {string.Join( + "; ", + compensationFailures + )}"; + return EditorHistoryResult.StateIntegrityLost($"Transaction {transition} failed: {original.message} {compensation}"); } /// diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs index 21e19be08..d3c0897bf 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; - using Inno.Core.Logging; using Inno.Editor.Core; namespace Inno.Editor.Interactions; - internal sealed class EditorMenuCatalog( EditorExtensionCatalog catalog, EditorActionRouter actions, Logger log -) { +) +{ private readonly HashSet m_sourceFailures = new(StringComparer.Ordinal); - internal EditorMenuModel Build(EditorMenuContext context) { ArgumentNullException.ThrowIfNull(context); @@ -24,14 +22,7 @@ internal EditorMenuModel Build(EditorMenuContext context) { if (!string.Equals(menu.area, context.area, StringComparison.Ordinal)) continue; - placements.Add(new Placement( - NormalizePath(menu.path), - registration.id, - menu.order, - menu.separatorBefore, - argument: null, - status: null, - isGroup: false)); + placements.Add(new Placement(NormalizePath(menu.path), registration.id, menu.order, menu.separatorBefore, argument: null, status: null, isGroup: false)); } } @@ -45,64 +36,25 @@ internal EditorMenuModel Build(EditorMenuContext context) registration.source.Build(context, builder); string sourceName = registration.type.FullName ?? registration.type.Name; m_sourceFailures.Remove(sourceName); - placements.AddRange(builder.groups.Select(static group => new Placement( - NormalizePath(group.path), - actionId: null, - group.order, - group.separatorBefore, - argument: null, - status: null, - isGroup: true))); - placements.AddRange(builder.items.Select(static item => new Placement( - NormalizePath(item.path), - item.actionId, - item.order, - item.separatorBefore, - item.argument, - status: null, - isGroup: false))); + placements.AddRange(builder.groups.Select(static group => new Placement(NormalizePath(group.path), actionId: null, group.order, group.separatorBefore, argument: null, status: null, isGroup: true))); + placements.AddRange(builder.items.Select(static item => new Placement(NormalizePath(item.path), item.actionId, item.order, item.separatorBefore, item.argument, status: null, isGroup: false))); } catch (Exception exception) { string sourceName = registration.type.FullName ?? registration.type.Name; if (m_sourceFailures.Add(sourceName)) { - log.Write( - LogLevel.Error, - "Editor menu source '{0}' failed: {1}", - [sourceName, exception]); + log.Write(LogLevel.Error, "Editor menu source '{0}' failed: {1}", [sourceName, exception]); } } } - if (string.Equals( - context.area, - EditorBuiltInInteractionIds.C_MAIN_MENU_AREA, - StringComparison.Ordinal)) - { - foreach (EditorExtensionCatalog.PanelRegistration panel in catalog.extensions.panels) - { - string menuPath = string.IsNullOrEmpty(panel.attribute.menuPath) - ? panel.attribute.title - : $"{panel.attribute.menuPath}/{panel.attribute.title}"; - placements.Add(new Placement( - $"Panel/{menuPath}", - EditorBuiltInInteractionIds.C_TOGGLE_PANEL, - panel.attribute.order, - panel.attribute.separatorBefore, - panel.attribute.id, - new EditorActionState(isVisible: true, isEnabled: true, isChecked: panel.panel.isOpen), - isGroup: false)); - } - } - var root = new MutableNode(string.Empty, 0, false); - foreach (Placement placement in placements - .OrderBy(static value => value.order) - .ThenBy(static value => value.path, StringComparer.Ordinal)) + foreach (Placement placement in placements.OrderBy(static value => value.order).ThenBy(static value => value.path, StringComparer.Ordinal)) { AddPlacement(root, placement, context); } + return new EditorMenuModel(Freeze(root.children.Values)); } @@ -112,12 +64,9 @@ private void AddPlacement( EditorMenuContext context ) { EditorActionContext actionContext = context.CreateActionContext(placement.argument); - EditorActionState state = placement.isGroup - ? EditorActionState.enabled - : placement.status ?? actions.Query(placement.actionId!, actionContext); + EditorActionState state = placement.isGroup ? EditorActionState.enabled : placement.status ?? actions.Query(placement.actionId!, actionContext); if (!state.isVisible) return; - string[] segments = placement.path.Split('/'); MutableNode current = root; for (int i = 0; i < segments.Length; i++) @@ -125,10 +74,10 @@ EditorMenuContext context string segment = segments[i]; if (!current.children.TryGetValue(segment, out MutableNode? child)) { - child = new MutableNode(segment, placement.order, - i == segments.Length - 1 && placement.separatorBefore); + child = new MutableNode(segment, placement.order, i == segments.Length - 1 && placement.separatorBefore); current.children.Add(segment, child); } + current = child; } @@ -146,33 +95,21 @@ EditorMenuContext context private static IReadOnlyList Freeze(IEnumerable nodes) { var result = new List(); - foreach (MutableNode node in nodes - .OrderBy(static value => value.order) - .ThenBy(static value => value.label, StringComparer.Ordinal)) + foreach (MutableNode node in nodes.OrderBy(static value => value.order).ThenBy(static value => value.label, StringComparer.Ordinal)) { IReadOnlyList children = Freeze(node.children.Values); - EditorActionState state = node.actionId is null - ? new EditorActionState(children.Count > 0, children.Count > 0) - : node.state; + EditorActionState state = node.actionId is null ? new EditorActionState(children.Count > 0, children.Count > 0) : node.state; if (!state.isVisible && children.Count == 0) continue; - result.Add(new EditorMenuItem( - node.label, - node.actionId ?? string.Empty, - node.order, - node.separatorBefore, - state, - children, - node.argument)); + result.Add(new EditorMenuItem(node.label, node.actionId ?? string.Empty, node.order, node.separatorBefore, state, children, node.argument)); } + return result; } private static string NormalizePath(string path) { - string normalized = string.Join( - '/', - path.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); + string normalized = string.Join('/', path.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); if (string.IsNullOrEmpty(normalized)) throw new InvalidOperationException("An editor menu path must contain at least one segment."); return normalized; @@ -187,12 +124,12 @@ private sealed record Placement( EditorActionState? status, bool isGroup ); - private sealed class MutableNode( string label, int order, bool separatorBefore - ) { + ) + { internal string label = label; internal int order = order; internal bool separatorBefore = separatorBefore; diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Properties/ScriptingApi.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Properties/ScriptingApi.cs index 1cc55080b..d5b8f332f 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Properties/ScriptingApi.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Properties/ScriptingApi.cs @@ -1,25 +1,19 @@ using Inno.Scripting.Api; using Inno.Editor.Interactions; -[assembly: ScriptingApiNamespace( - "InnoEditor.Interactions", - "Inno.Editor.Interactions", - ScriptingApiScope.Editor)] - +[assembly: ScriptingApiNamespace("InnoEditor.Interactions", "Inno.Editor.Interactions", ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorInteractions), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorInteraction), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorSelectionState), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(IEditorSelectionCoordinator), ScriptingApiScope.Editor)] - [assembly: ScriptingApiExport(typeof(EditorAction), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorAction<>), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(EditorAction<,>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorAction<, >), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorArgumentAction<>), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(EditorPresentationAction<,>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorPresentationAction<, >), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorActionAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorActionContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorActionContext<>), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(EditorActionContext<,>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorActionContext<, >), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorActionArgumentContext<>), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorActionState), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorShortcutAttribute), ScriptingApiScope.Editor)] @@ -35,7 +29,6 @@ [assembly: ScriptingApiExport(typeof(EditorHistoryPayload), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorHistoryResult), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorHistoryTransaction), ScriptingApiScope.Editor)] - [assembly: ScriptingApiExport(typeof(IEditorDocumentService), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDocumentProvider), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDocumentContext), ScriptingApiScope.Editor)] @@ -49,7 +42,6 @@ [assembly: ScriptingApiExport(typeof(EditorViewportToolContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorViewportToolSession), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorPlanarNavigation), ScriptingApiScope.Editor)] - [assembly: ScriptingApiExport(typeof(EditorMenuAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorMenuBuilder), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorMenuContext), ScriptingApiScope.Editor)] @@ -57,20 +49,20 @@ [assembly: ScriptingApiExport(typeof(EditorMenuModel), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorMenuSource), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorMenuSourceAttribute), ScriptingApiScope.Editor)] - [assembly: ScriptingApiExport(typeof(EditorToolbarIcon), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorToolbarItem), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorToolbarItemAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorToolbarModel), ScriptingApiScope.Editor)] - [assembly: ScriptingApiExport(typeof(EditorDragData), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDragContext), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDrop), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(EditorDrop<,>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorDrop<, >), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropAttribute), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropContext), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(EditorDropContext<,>), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorDropContext<, >), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropPlacement), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropResult), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropStatus), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorDropVisual), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorEditGesture), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorSelectionSnapshot), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs index 6e5431741..4579a1559 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs @@ -1,16 +1,13 @@ using System; - using Inno.Core.Identity; namespace Inno.Editor.Interactions; - /// /// Provides a lightweight fluent handle for one interaction area and optional target. /// public readonly struct EditorInteraction { private readonly EditorInteractions m_interactions; - internal EditorInteraction( EditorInteractions interactions, string area, @@ -26,34 +23,26 @@ internal EditorInteraction( /// Gets the stable interaction area. /// public string area { get; } - /// /// Gets the optional target represented by this handle. /// public object? target { get; } - /// /// Gets whether this handle's target is the current editor selection. /// - public bool isSelected => target is not null && - Equals(m_interactions.selection.selectedTarget, target); + public bool isSelected => m_interactions.selection.Contains(target); /// /// Marks this area and target as the active keyboard context. /// public void Focus() => m_interactions.Focus(area, target); - /// /// Selects this target, or clears selection when the target is . /// /// /// when the built-in selection action executed; otherwise, . /// - public bool Select() - => Execute(target is null - ? EditorBuiltInInteractionIds.C_CLEAR_SELECTION - : EditorBuiltInInteractionIds.C_SELECT); - + public bool Select() => Execute(target is null ? EditorBuiltInInteractionIds.C_CLEAR_SELECTION : EditorBuiltInInteractionIds.C_SELECT); /// /// Queries an action for this area and target. /// @@ -73,7 +62,6 @@ public EditorActionState Query( string action, object? argument = null ) => m_interactions.Query(action, area, target, argument); - /// /// Executes an action for this area and target. /// @@ -93,7 +81,6 @@ public bool Execute( string action, object? argument = null ) => m_interactions.Execute(action, area, target, argument); - /// /// Queues an action until the current UI traversal completes. /// @@ -110,7 +97,6 @@ public void Enqueue( string action, object? argument = null ) => m_interactions.Enqueue(action, area, target, argument); - /// /// Presents an active action in place of this target's normal content. /// @@ -130,7 +116,6 @@ public bool Present( string action, object? argument = null ) => m_interactions.Present(action, area, target, argument); - /// /// Gets whether an action owns an active multi-frame operation for this target. /// @@ -144,7 +129,6 @@ public bool Present( /// Thrown when is empty. /// public bool IsActive(string action) => m_interactions.IsActive(action, area, target); - /// /// Builds the complete contextual menu for this area and target. /// @@ -152,7 +136,13 @@ public bool Present( /// The immutable resolved menu model. /// public EditorMenuModel BuildMenu() => m_interactions.BuildMenu(area, target); - + /// + /// Builds searchable commands from this area's menus and applicable unplaced Actions. + /// + /// + /// Current command states and neutral placement arguments; execution revalidates their targets. + /// + public EditorMenuModel BuildCommands() => m_interactions.BuildCommands(area, target); /// /// Builds the complete compact toolbar for this area and target. /// @@ -160,7 +150,6 @@ public bool Present( /// The immutable resolved toolbar model. /// public EditorToolbarModel BuildToolbar() => m_interactions.BuildToolbar(area, target); - /// /// Resolves the shortcut displayed for an action in this area. /// @@ -179,9 +168,7 @@ public bool Present( public bool TryGetShortcut( string action, out HotKeyGesture gesture - ) - => m_interactions.TryGetShortcut(action, area, target, out gesture); - + ) => m_interactions.TryGetShortcut(action, area, target, out gesture); /// /// Gets the currently active drag identity, if its source remains registered. /// @@ -192,7 +179,6 @@ out HotKeyGesture gesture /// when a live drag source exists; otherwise, . /// public bool TryGetActiveDragIdentity(out RuntimeIdentity identity) => m_interactions.TryGetActiveDragIdentity(out identity); - /// /// Begins a managed drag originating from this area. /// @@ -203,7 +189,6 @@ out HotKeyGesture gesture /// The source object's domain-qualified transient identity. /// public RuntimeIdentity BeginDrag(EditorDragData data) => m_interactions.BeginDrag(area, data); - /// /// Queries this handle as a drop target. /// @@ -219,9 +204,7 @@ out HotKeyGesture gesture public EditorDropStatus QueryDrop( RuntimeIdentity identity, EditorDropPlacement placement = EditorDropPlacement.None - ) - => m_interactions.QueryDrop(identity, area, target, placement); - + ) => m_interactions.QueryDrop(identity, area, target, placement); /// /// Delivers a managed drop to this target. /// @@ -237,6 +220,5 @@ public EditorDropStatus QueryDrop( public EditorDropResult Drop( RuntimeIdentity identity, EditorDropPlacement placement = EditorDropPlacement.None - ) - => m_interactions.Drop(identity, area, target, placement); + ) => m_interactions.Drop(identity, area, target, placement); } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs index 8f01095d7..226a2c57a 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.Loader; - using Inno.Core.Events; using Inno.Core.Identity; using Inno.Core.Logging; @@ -11,14 +9,15 @@ using Inno.Editor.Core; namespace Inno.Editor.Interactions; - /// /// Provides the single presentation-independent entry point for editor actions, menus, selection, focus, and drag-and-drop. /// public sealed class EditorInteractions : IEditorSelectionCoordinator, IEditorHistoryIsolation { private readonly EditorContext m_editor; - private readonly IReadOnlyDictionary m_identityDomains; + private readonly Dictionary m_identityDomains; + private readonly int m_identityOwnerThread = Environment.CurrentManagedThreadId; + private bool m_identityDomainsRetired; private readonly EditorHistory m_history; private readonly EditorDocumentService m_documents; private readonly Logger m_log; @@ -28,12 +27,11 @@ public sealed class EditorInteractions : IEditorSelectionCoordinator, IEditorHis private EditorToolbarCatalog? m_toolbars; private EditorDropRouter? m_drops; private string m_focusedArea = EditorBuiltInInteractionIds.C_GLOBAL_AREA; - private object? m_focusedTarget; - private object? m_previousGenerationSelection; - private object? m_previousGenerationFocus; - private Guid? m_pendingSelectionId; - private Guid? m_pendingFocusId; - + private EditorTargetReference? m_focus; + private EditorTargetReference[]? m_previousGenerationSelection; + private EditorTargetReference? m_previousGenerationFocus; + private EditorTargetReference? m_previousGenerationAnchor; + private bool m_generationTransitionPrepared; internal EditorInteractions( EditorContext editor, IEnumerable identityDomains, @@ -43,26 +41,32 @@ Logger log ArgumentNullException.ThrowIfNull(identityDomains); m_identityDomains = identityDomains.ToDictionary(static allocator => allocator.domainId); m_log = log ?? throw new ArgumentNullException(nameof(log)); - m_documents = new EditorDocumentService(); - m_history = new EditorHistory(new EditorHistoryOptions - { - cacheDirectory = Path.Combine(editor.projectDirectory, "Library", "Editor", "History") - }, log); + selection = new EditorSelectionState(this); + m_documents = new EditorDocumentService(events); + m_history = new EditorHistory(new EditorHistoryOptions { cacheDirectory = Path.Combine(editor.projectDirectory, "Library", "Editor", "History") }, log); m_history.Attach(editor, this); } + /// + /// Gets synchronous Editor lifecycle notifications implemented by the Core event dispatcher. + /// + /// + /// Own and dispose a hub rather than retaining callbacks beyond their extension generation. + /// + [ScriptingApiIgnore] + public EventDispatcher events { get; } = new(); + + internal void PrepareOperation() => events.Emit(new EditorOperationPreparingEvent()); /// /// Gets the shared read-only editor selection state. /// - public EditorSelectionState selection { get; } = new(); + public EditorSelectionState selection { get; } object? IEditorSelectionCoordinator.selectedTarget => selection.selectedTarget; - /// /// Gets the transactional Undo and Redo history owned by this editor runtime. /// public IEditorHistory history => m_history; - /// /// Gets the headless reload-safe document lifetime used by dedicated asset editors and Inspectors. /// @@ -83,7 +87,8 @@ Logger log /// /// Do not retain the result across callbacks or generation changes. Candidate modules are never exposed. /// - public bool TryGetModule(out TModule? module) where TModule : EditorModule + public bool TryGetModule(out TModule? module) + where TModule : EditorModule { if (m_catalog is not null) return m_catalog.TryGetModule(out module); @@ -106,18 +111,44 @@ public bool TryGetModule(out TModule? module) where TModule : EditorMod /// [ScriptingApiIgnore] public IDisposable BeginHistoryIsolation() => m_history.BeginIsolation(); - internal EditorHistory historyHost => m_history; - /// /// Gets the area that most recently received keyboard focus. /// public string focusedArea => m_focusedArea; + /// + /// Gets immutable discovery metadata for the active, available panels. + /// + /// + /// Current panel declarations; the snapshot contains no panel or extension instances. + /// + public IReadOnlyList GetPanelDeclarations() => m_catalog?.GetPanelDeclarations() ?? Array.Empty(); + /// + /// Reads whether one available panel is currently open. + /// + /// + /// The stable panel identity. + /// + /// + /// Receives the open state, or false when the panel is unavailable. + /// + /// + /// True when the current generation contains the requested available panel. + /// + public bool TryGetPanelState( + string panelId, + out bool isOpen + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(panelId); + isOpen = false; + return m_catalog?.TryGetPanelState(panelId, out isOpen) == true; + } + /// /// Gets the target associated with the focused area. /// - public object? focusedTarget => m_focusedTarget; + public object? focusedTarget => m_focus?.Resolve(this); /// /// Creates a lightweight interaction handle for one area and optional target. @@ -138,7 +169,6 @@ public EditorInteraction For( string area, object? target = null ) => new(this, area, target); - /// /// Replaces the editor selection after closing presentations owned by other targets. /// @@ -147,11 +177,115 @@ public EditorInteraction For( /// public void SetSelection(object? target) { - PrepareSelectionChange(target); + PrepareOperation(); if (target is null) selection.Clear(); else selection.Select(target); + PrepareSelectionChange(target); + } + + /// + /// Replaces the ordered selection; duplicates are removed and the final target becomes primary. + /// + /// + /// Registered objects resolved for the current callback. + /// + /// + /// The range anchor, or null to use the first selected target. + /// + /// + /// The target collection or one member is null. + /// + /// + /// A target has no live identity registration. + /// + public void SetSelection( + IEnumerable targets, + IdentityObject? anchor + ) { + PrepareOperation(); + selection.Replace(targets, anchor); + PrepareSelectionChange(selection.selectedTarget); + } + + /// + /// Adds or removes a member while preserving the existing range anchor. + /// + /// + /// A currently registered selection target. + /// + /// + /// The target is null. + /// + public void ToggleSelection(IdentityObject target) + { + ArgumentNullException.ThrowIfNull(target); + var values = new List(); + _ = selection.CopyTargets(values); + bool removed = values.Remove(target); + if (!removed) + values.Add(target); + SetSelection(values, selection.anchor as IdentityObject ?? target); + } + + /// + /// Selects the inclusive visible range between the current anchor and the clicked target. + /// + /// + /// Targets in the current visible sort and filter order. + /// + /// + /// The clicked range endpoint, which becomes primary. + /// + /// + /// Whether to preserve existing members outside the selected range. + /// + /// + /// The visible targets or endpoint is null. + /// + /// + /// The endpoint is not in the visible target order. + /// + public void SelectRange( + IReadOnlyList orderedTargets, + IdentityObject target, + bool additive = false + ) { + ArgumentNullException.ThrowIfNull(orderedTargets); + ArgumentNullException.ThrowIfNull(target); + IdentityObject anchor = selection.anchor as IdentityObject ?? target; + int first = -1; + int last = -1; + for (int i = 0; i < orderedTargets.Count; i++) + { + if (ReferenceEquals(orderedTargets[i], anchor)) + first = i; + if (ReferenceEquals(orderedTargets[i], target)) + last = i; + } + + if (last < 0) + throw new ArgumentException("The selection endpoint is not in the visible order.", nameof(target)); + if (first < 0) + { + first = last; + anchor = target; + } + + var values = new List(); + if (additive) + _ = selection.CopyTargets(values); + for (int i = Math.Min(first, last); i <= Math.Max(first, last); i++) + { + IdentityObject value = orderedTargets[i]; + if (!ReferenceEquals(value, target) && !values.Contains(value)) + values.Add(value); + } + + _ = values.Remove(target); + values.Add(target); + SetSelection(values, anchor); } /// @@ -170,7 +304,6 @@ public bool TryGetDragSource( RuntimeIdentity identity, out IdentityObject? source ) => Drops.TryGetSource(identity, out source); - /// /// Gets the active drag identity used to select the matching native payload domain. /// @@ -181,7 +314,6 @@ out IdentityObject? source /// when a live drag source exists; otherwise, . /// public bool TryGetActiveDragIdentity(out RuntimeIdentity identity) => Drops.TryGetActiveIdentity(out identity); - /// /// Toggles one panel in the currently active extension generation. /// @@ -200,6 +332,35 @@ public bool TogglePanel(string panelId) return m_catalog?.TryTogglePanel(panelId) == true; } + /// + /// Makes a temporary Core identity domain available to selection, focus and drag resolution. + /// + /// + /// The borrowed allocator owned by the temporary runtime session. + /// + /// + /// A routing lease; retire it after restoring Edit selection and before releasing the session. + /// + /// + /// The allocator is null. + /// + /// + /// The domain is already registered or the caller is not the owner thread. + /// + /// + /// The interaction runtime has retired its identity routing. + /// + [ScriptingApiIgnore] + public IDisposable RegisterIdentityDomain(IdentityAllocator allocator) + { + ArgumentNullException.ThrowIfNull(allocator); + EnsureIdentityDomainOwner(); + ObjectDisposedException.ThrowIf(m_identityDomainsRetired, this); + if (!m_identityDomains.TryAdd(allocator.domainId, allocator)) + throw new InvalidOperationException("The Editor identity domain is already registered."); + return new IdentityDomainLease(this, allocator.domainId); + } + /// /// Resolves a domain-qualified runtime identity through the Editor's complete identity-domain set. /// @@ -217,9 +378,7 @@ public bool TryResolveIdentity( RuntimeIdentity identity, out IdentityObject? target ) { - target = m_identityDomains.TryGetValue(identity.domainId, out IdentityAllocator? allocator) - ? allocator.Get(identity) - : null; + target = m_identityDomains.TryGetValue(identity.domainId, out IdentityAllocator? allocator) ? allocator.Get(identity) : null; return target is not null; } @@ -244,10 +403,7 @@ public bool TryResolveIdentity( Guid persistentId, out IdentityObject? target ) { - target = persistentId != Guid.Empty - && m_identityDomains.TryGetValue(domainId, out IdentityAllocator? allocator) - ? allocator.Get(persistentId) - : null; + target = persistentId != Guid.Empty && m_identityDomains.TryGetValue(domainId, out IdentityAllocator? allocator) ? allocator.Get(persistentId) : null; return target is not null; } @@ -305,40 +461,34 @@ internal void Update() internal void PrepareGenerationTransition() { - if (m_previousGenerationSelection is not null || m_previousGenerationFocus is not null) + if (m_generationTransitionPrepared) throw new InvalidOperationException("An interaction generation transition is already active."); - - m_previousGenerationSelection = selection.selectedTarget; - m_previousGenerationFocus = m_focusedTarget; - PrepareRetiringTarget( - selection.selectedTarget, - persistentId => m_pendingSelectionId = persistentId, - selection.Clear); - PrepareRetiringTarget( - m_focusedTarget, - persistentId => m_pendingFocusId = persistentId, - () => m_focusedTarget = null); + m_previousGenerationSelection = selection.CaptureReferences(); + m_previousGenerationAnchor = selection.anchorReference; + m_previousGenerationFocus = m_focus; + m_generationTransitionPrepared = true; Actions.ResetTransientState(); Drops.Cancel(); } internal void RollbackGenerationTransition() { - m_pendingSelectionId = null; - m_pendingFocusId = null; - if (m_previousGenerationSelection is object previousSelection) - selection.Select(previousSelection); - else - selection.Clear(); - m_focusedTarget = m_previousGenerationFocus; - m_previousGenerationSelection = null; - m_previousGenerationFocus = null; + Actions.ResetTransientState(); + if (m_previousGenerationSelection is not null) + selection.Restore(m_previousGenerationSelection, m_previousGenerationAnchor); + m_focus = m_previousGenerationFocus; + CompleteGenerationTransition(); } internal void CompleteGenerationTransition() { + Actions.InvalidateDiscovery(); + selection.Rebind(); + m_focus = m_focus?.Rebind(this); m_previousGenerationSelection = null; + m_previousGenerationAnchor = null; m_previousGenerationFocus = null; + m_generationTransitionPrepared = false; } internal void Shutdown(IReadOnlyList actions) @@ -348,88 +498,89 @@ internal void Shutdown(IReadOnlyList m_catalog = null; selection.Clear(); m_focusedArea = EditorBuiltInInteractionIds.C_GLOBAL_AREA; - m_focusedTarget = null; - m_pendingSelectionId = null; - m_pendingFocusId = null; + m_focus = null; m_previousGenerationSelection = null; + m_previousGenerationAnchor = null; m_previousGenerationFocus = null; m_documents.Shutdown(); m_history.Dispose(); + m_identityDomains.Clear(); + m_identityDomainsRetired = true; } - internal bool DispatchShortcut(KeyPressedEvent keyEvent) - => Actions.DispatchShortcut( - keyEvent, - m_focusedArea, - m_focusedTarget ?? selection.selectedTarget); - + internal bool DispatchShortcut(KeyPressedEvent keyEvent) => Actions.DispatchShortcut(keyEvent, m_focusedArea, focusedTarget ?? selection.selectedTarget); internal void Focus( string area, object? target ) { m_focusedArea = area; - m_focusedTarget = target; + m_focus = target is null ? null : EditorTargetReference.Capture(target); } internal void PrepareSelectionChange(object? target) => Actions.LosePresentationExcept(target); - internal EditorActionState Query( string action, string area, object? target, object? argument - ) - => Actions.Query(action, CreateActionContext(area, target, argument)); - + ) => Actions.Query(action, CreateActionContext(area, target, argument)); internal bool Execute( string action, string area, object? target, object? argument - ) - => Actions.Execute(action, CreateActionContext(area, target, argument)); - + ) => Actions.Execute(action, CreateActionContext(area, target, argument)); internal void Enqueue( string action, string area, object? target, object? argument - ) - => Actions.Enqueue(action, CreateActionContext(area, target, argument)); - + ) => Actions.Enqueue(action, CreateActionContext(area, target, argument)); internal bool Present( string action, string area, object? target, object? argument - ) - => Actions.Present(action, CreateActionContext(area, target, argument)); - + ) => Actions.Present(action, CreateActionContext(area, target, argument)); internal bool IsActive( string action, string area, object? target ) => Actions.IsActive(action, CreateActionContext(area, target, null)); - internal EditorMenuModel BuildMenu( string area, object? target ) => Menus.Build(new EditorMenuContext(m_editor, this, area, target)); + internal EditorMenuModel BuildCommands( + string area, + object? target + ) { + EditorMenuModel menu = BuildMenu(area, target); + var placed = new HashSet(StringComparer.Ordinal); + Collect(menu.items); + IReadOnlyList additional = Actions.BuildUnplacedCommands(CreateActionContext(area, target, null), placed); + return new EditorMenuModel([..menu.items, ..additional]); + void Collect(IReadOnlyList items) + { + foreach (EditorMenuItem item in items) + { + if (item.actionId.Length != 0) + _ = placed.Add(item.actionId); + Collect(item.children); + } + } + } internal EditorToolbarModel BuildToolbar( string area, object? target - ) - => Toolbars.Build(CreateActionContext(area, target, argument: null)); - + ) => Toolbars.Build(CreateActionContext(area, target, argument: null)); internal bool TryGetShortcut( string action, string area, object? target, out HotKeyGesture gesture - ) - => Actions.TryGetShortcut(action, area, target, out gesture); - + ) => Actions.TryGetShortcut(action, area, target, out gesture); internal RuntimeIdentity BeginDrag( string area, EditorDragData data @@ -446,18 +597,9 @@ internal EditorDropStatus QueryDrop( object? target, EditorDropPlacement placement ) { - if (target is null || - !Drops.TryGetSource(identity, out IdentityObject? source) || - source is null) + if (target is null || !Drops.TryGetSource(identity, out IdentityObject? source) || source is null) return EditorDropStatus.rejected; - return Drops.Query(identity, new EditorDropContext( - m_editor, - this, - area, - new EditorDragData(source, string.Empty), - source, - target, - placement)); + return Drops.Query(identity, new EditorDropContext(m_editor, this, area, new EditorDragData(source, string.Empty), source, target, placement)); } internal EditorDropResult Drop( @@ -466,74 +608,48 @@ internal EditorDropResult Drop( object? target, EditorDropPlacement placement ) { - if (target is null || - !Drops.TryGetSource(identity, out IdentityObject? source) || - source is null) + if (target is null || !Drops.TryGetSource(identity, out IdentityObject? source) || source is null) return EditorDropResult.rejected; - return Drops.Drop(identity, new EditorDropContext( - m_editor, - this, - area, - new EditorDragData(source, string.Empty), - source, - target, - placement)); + return Drops.Drop(identity, new EditorDropContext(m_editor, this, area, new EditorDragData(source, string.Empty), source, target, placement)); } - private EditorActionRouter Actions => m_actions - ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); - - private EditorMenuCatalog Menus => m_menus - ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); - - private EditorDropRouter Drops => m_drops - ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); - - private EditorToolbarCatalog Toolbars => m_toolbars - ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); + private EditorActionRouter Actions => m_actions ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); + private EditorMenuCatalog Menus => m_menus ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); + private EditorDropRouter Drops => m_drops ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); + private EditorToolbarCatalog Toolbars => m_toolbars ?? throw new InvalidOperationException("Editor interactions are not attached to a runtime."); private EditorActionContext CreateActionContext( string area, object? target, object? argument - ) - => new(m_editor, this, area, target, argument); - + ) => new(m_editor, this, area, target, argument); private void ResolveGenerationTargets() { - if (m_pendingSelectionId is Guid selectionId) - { - m_pendingSelectionId = null; - IdentityObject? replacement = IdentityAllocator.hasCurrent - ? IdentityAllocator.current.Get(selectionId) - : null; - if (replacement is not null) - selection.Select(replacement); - else - selection.Clear(); - } - if (m_pendingFocusId is Guid focusId) - { - m_pendingFocusId = null; - m_focusedTarget = IdentityAllocator.hasCurrent - ? IdentityAllocator.current.Get(focusId) - : null; - } + selection.Rebind(); + m_focus = m_focus?.Rebind(this); + } + private void EnsureIdentityDomainOwner() + { + if (Environment.CurrentManagedThreadId != m_identityOwnerThread) + throw new InvalidOperationException("Editor identity domains must be changed on the owning thread."); } - private static void PrepareRetiringTarget( - object? target, - Action retainIdentity, - Action clear - ) { - if (target is null || - !target.GetType().Assembly.IsCollectible && - AssemblyLoadContext.GetLoadContext(target.GetType().Assembly)?.IsCollectible != true) + private sealed class IdentityDomainLease( + EditorInteractions owner, + IdentityDomainId domain + ) : IDisposable + { + private EditorInteractions? m_owner = owner; + /// + public void Dispose() { - return; + EditorInteractions? active = m_owner; + if (active is null) + return; + active.EnsureIdentityDomainOwner(); + active.m_identityDomains.Remove(domain); + m_owner = null; } - if (target is IdentityObject identityObject) - retainIdentity(identityObject.identity.persistentId); - clear(); } + } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs deleted file mode 100644 index f8c9230cc..000000000 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorSelectionState.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Inno.Editor.Interactions; - -/// -/// Stores editor-wide object selection state. -/// -public sealed class EditorSelectionState -{ - private object? m_selectedTarget; - - /// - /// Gets the selected target, or when nothing is selected. - /// - public object? selectedTarget => m_selectedTarget; - - /// - /// Gets a monotonically increasing selection change version. - /// - public ulong version { get; private set; } - - /// - /// Selects a target object. - /// - /// - /// Target to select. - /// - internal void Select(object target) - { - ArgumentNullException.ThrowIfNull(target); - if (Equals(m_selectedTarget, target)) - { - return; - } - - m_selectedTarget = target; - version++; - } - - /// - /// Clears the current selection. - /// - internal void Clear() - { - if (m_selectedTarget is null) - { - return; - } - - m_selectedTarget = null; - version++; - } - - /// - /// Tries to read the current target as a requested type. - /// - /// - /// Requested target type. - /// - /// - /// Typed target when successful. - /// - /// - /// when the current target is compatible. - /// - public bool TryGet([NotNullWhen(true)] out TTarget? target) - { - if (m_selectedTarget is TTarget typedTarget) - { - target = typedTarget; - return true; - } - - target = default; - return false; - } - -} diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs new file mode 100644 index 000000000..b890da532 --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs @@ -0,0 +1,64 @@ +using System; +using Inno.Core.Identity; + +namespace Inno.Editor.Interactions; +internal sealed class EditorTargetReference +{ + private readonly object? m_value; + private readonly WeakReference? m_target; + private EditorTargetReference( + RuntimeIdentity? runtimeIdentity, + Guid persistentId, + object? value, + WeakReference? target + ) { + this.runtimeIdentity = runtimeIdentity; + this.persistentId = persistentId; + m_value = value; + m_target = target; + } + + internal RuntimeIdentity? runtimeIdentity { get; } + internal Guid persistentId { get; } + + internal static EditorTargetReference Capture(object? value) + { + if (value is IdentityObject target) + { + RuntimeIdentity runtime = target.identity.runtimeIdentity ?? throw new InvalidOperationException("An editor interaction target must belong to a live identity domain."); + return new EditorTargetReference(runtime, target.identity.persistentId, null, null); + } + + if (value is null or string || value.GetType().IsValueType && !value.GetType().Assembly.IsCollectible) + return new EditorTargetReference(null, Guid.Empty, value, null); + return new EditorTargetReference(null, Guid.Empty, null, new WeakReference(value)); + } + + internal object? Resolve(EditorInteractions interactions) + { + if (runtimeIdentity is RuntimeIdentity runtime) + return interactions.TryResolveIdentity(runtime, out IdentityObject? target) ? target : null; + return m_target is not null ? m_target.TryGetTarget(out object? value) ? value : null : m_value; + } + + internal EditorTargetReference Rebind(EditorInteractions interactions) + { + if (runtimeIdentity is not RuntimeIdentity runtime || interactions.TryResolveIdentity(runtime, out _)) + return this; + if (interactions.TryResolveIdentity(runtime.domainId, persistentId, out IdentityObject? replacement) && replacement is not null) + { + return Capture(replacement); + } + + return this; + } + + internal bool Matches( + EditorInteractions interactions, + object? target + ) { + if (target is IdentityObject identity && runtimeIdentity is RuntimeIdentity runtime) + return identity.identity.runtimeIdentity == runtime; + return Equals(Resolve(interactions), target); + } +} diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs index dbf8b4f01..09ba9d282 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/IEditorSelectionCoordinator.cs @@ -1,10 +1,35 @@ +using System; +using Inno.Core.Identity; +using Inno.Scripting.Api; + namespace Inno.Editor.Interactions; /// -/// Provides the narrow selection boundary used by editor features that replace object instances. +/// Coordinates host-owned selection and the Core identity lifetime of temporary presentations. /// public interface IEditorSelectionCoordinator { + /// + /// Registers the Core identity domain used by a temporary scene presentation. + /// + /// + /// The borrowed allocator owned by that presentation's runtime session. + /// + /// + /// A routing lease released after restoring the preceding selection. + /// + /// + /// The allocator is null. + /// + /// + /// The domain is already active or the caller is not the owner thread. + /// + /// + /// The coordinator has retired. + /// + [ScriptingApiIgnore] + IDisposable RegisterIdentityDomain(IdentityAllocator allocator); + /// /// Gets the currently selected target, or when selection is empty. /// diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs new file mode 100644 index 000000000..2c07a9d0e --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Core.Identity; + +namespace Inno.Editor.Interactions; +/// +/// Captures ordered selection identities without retaining any domain object or extension instance. +/// +public sealed class EditorSelectionSnapshot +{ + private readonly EditorTargetReference[] m_targets; + internal EditorSelectionSnapshot( + IEnumerable targets, + RuntimeIdentity? anchor + ) { + m_targets = targets.ToArray(); + this.identities = Array.AsReadOnly(m_targets.Where(value => value.runtimeIdentity is not null).Select(value => value.runtimeIdentity!.Value).ToArray()); + this.anchor = anchor; + } + + /// + /// Gets unique selection identities in order; the final identity is the primary selection. + /// + public IReadOnlyList identities { get; } + /// + /// Gets the range anchor identity, or null when there is no registered anchor. + /// + public RuntimeIdentity? anchor { get; } + + /// + /// Resolves this immutable selection in its original domains, including same-identity recovery after a generation change. + /// + /// + /// The target type accepted by the current presenter. + /// + /// + /// The active session supplying the original identity domains. + /// + /// + /// A temporary caller-owned collection to clear and fill. + /// + /// + /// The number of currently available compatible targets; missing members remain represented by this snapshot. + /// + public int CopyTargets( + EditorInteractions interactions, + ICollection destination + ) + where TTarget : class + { + ArgumentNullException.ThrowIfNull(interactions); + ArgumentNullException.ThrowIfNull(destination); + destination.Clear(); + foreach (EditorTargetReference reference in m_targets) + if (reference.Rebind(interactions).Resolve(interactions) is TTarget target) + destination.Add(target); + return destination.Count; + } + + /// + /// Gets the primary identity, or null when this snapshot contains no registered targets. + /// + public RuntimeIdentity? primary => identities.Count == 0 ? null : identities[^1]; +} diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs new file mode 100644 index 000000000..3f67cbbe8 --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Inno.Core.Identity; + +namespace Inno.Editor.Interactions; +/// +/// Owns ordered transient selection through domain-qualified identities instead of retaining live objects. +/// +public sealed class EditorSelectionState +{ + private readonly EditorInteractions m_interactions; + private EditorTargetReference[] m_targets = []; + private EditorTargetReference? m_anchor; + private EditorSelectionSnapshot? m_snapshot; + internal EditorSelectionState(EditorInteractions interactions) + { + m_interactions = interactions; + } + + /// + /// Gets the current primary target for this callback, or null when unavailable or unselected. + /// + public object? selectedTarget => m_targets.Length == 0 ? null : m_targets[^1].Resolve(m_interactions); + /// + /// Gets the number of selected identities, including temporarily unavailable members. + /// + public int count => m_targets.Length; + /// + /// Gets the current range anchor, or null when the anchor is unavailable. + /// + public object? anchor => m_anchor?.Resolve(m_interactions); + /// + /// Gets the selection revision, incremented only when membership, order or resolved generation changes. + /// + public ulong version { get; private set; } + + /// + /// Checks membership by the target's owning identity domain and generation. + /// + /// + /// The immediate target to inspect. + /// + /// + /// True when the target is a member of this selection. + /// + public bool Contains(object? target) + { + if (target is null) + return false; + foreach (EditorTargetReference entry in m_targets) + { + if (entry.Matches(m_interactions, target)) + return true; + } + + return false; + } + + /// + /// Copies currently available selected targets into a caller-owned temporary collection. + /// + /// + /// The target type accepted by the caller. + /// + /// + /// The collection to clear and fill; do not retain live entries across callbacks. + /// + /// + /// The number of targets resolved in this callback. + /// + /// + /// The destination is null. + /// + public int CopyTargets(ICollection destination) + where TTarget : class + { + ArgumentNullException.ThrowIfNull(destination); + destination.Clear(); + foreach (EditorTargetReference entry in m_targets) + { + if (entry.Resolve(m_interactions) is TTarget target) + destination.Add(target); + } + + return destination.Count; + } + + /// + /// Captures the ordered runtime identities without retaining their resolved objects. + /// + /// + /// An immutable callback-independent snapshot; resolve identities afresh before using targets. + /// + public EditorSelectionSnapshot Capture() + { + if (m_snapshot is not null) + return m_snapshot; + return m_snapshot = new EditorSelectionSnapshot(m_targets, m_anchor?.runtimeIdentity); + } + + /// + /// Resolves the primary selected target as the requested type for immediate use. + /// + /// + /// The expected target type. + /// + /// + /// Receives the available primary target, or null. + /// + /// + /// True when the current primary target has the requested type. + /// + public bool TryGet([NotNullWhen(true)] out TTarget? target) + { + if (selectedTarget is TTarget typed) + { + target = typed; + return true; + } + + target = default; + return false; + } + + internal void Select(object target) + { + ArgumentNullException.ThrowIfNull(target); + if (m_targets.Length == 1 && m_targets[0].Matches(m_interactions, target)) + return; + EditorTargetReference entry = EditorTargetReference.Capture(target); + m_targets = [entry]; + m_anchor = entry; + Changed(); + } + + internal void Replace( + IEnumerable targets, + IdentityObject? anchor + ) { + ArgumentNullException.ThrowIfNull(targets); + var entries = new List(); + var seen = new HashSet(); + foreach (IdentityObject target in targets) + { + ArgumentNullException.ThrowIfNull(target); + EditorTargetReference entry = EditorTargetReference.Capture(target); + if (seen.Add(entry.runtimeIdentity!.Value)) + entries.Add(entry); + } + + EditorTargetReference? nextAnchor = anchor is null ? entries.Count == 0 ? null : entries[0] : EditorTargetReference.Capture(anchor); + bool equal = entries.Count == m_targets.Length && nextAnchor?.runtimeIdentity == m_anchor?.runtimeIdentity; + for (int i = 0; equal && i < entries.Count; i++) + equal = entries[i].runtimeIdentity == m_targets[i].runtimeIdentity; + if (equal) + return; + m_targets = entries.ToArray(); + m_anchor = nextAnchor; + Changed(); + } + + internal void Clear() + { + if (m_targets.Length == 0) + return; + m_targets = []; + m_anchor = null; + Changed(); + } + + internal EditorTargetReference? anchorReference => m_anchor; + + internal EditorTargetReference[] CaptureReferences() => (EditorTargetReference[])m_targets.Clone(); + internal void Restore( + EditorTargetReference[] targets, + EditorTargetReference? anchor + ) { + m_targets = targets; + m_anchor = anchor; + Changed(); + } + + internal void Rebind() + { + bool changed = false; + for (int i = 0; i < m_targets.Length; i++) + { + EditorTargetReference rebound = m_targets[i].Rebind(m_interactions); + if (rebound.runtimeIdentity == m_targets[i].runtimeIdentity) + continue; + m_targets[i] = rebound; + changed = true; + } + + if (m_anchor is not null) + m_anchor = m_anchor.Rebind(m_interactions); + if (changed) + Changed(); + } + + private void Changed() + { + m_snapshot = null; + version++; + } +} diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs index 76e65fdcd..03308f701 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Viewport/EditorViewportToolContext.cs @@ -2,7 +2,6 @@ using Inno.Core.Mathematics; namespace Inno.Editor.Interactions; - /// /// Coordinates pointer capture, transforms, and one history transaction for a viewport gesture. /// @@ -10,9 +9,8 @@ public sealed class EditorViewportToolContext { private readonly IEditorHistory m_history; private readonly IEditorViewportCoordinateConverter m_coordinates; - private EditorHistoryTransaction? m_transaction; + private EditorEditGesture? m_transaction; private int? m_capturedPointerId; - internal EditorViewportToolContext( IEditorHistory history, IEditorViewportCoordinateConverter coordinates @@ -25,12 +23,10 @@ IEditorViewportCoordinateConverter coordinates /// Gets whether this tool owns a pointer. /// public bool hasPointerCapture => m_capturedPointerId.HasValue; - /// /// Gets the captured pointer identity, or . /// public int? capturedPointerId => m_capturedPointerId; - /// /// Gets whether a history transaction is active for the current gesture. /// @@ -72,7 +68,7 @@ public void BeginHistoryGesture(string name) throw new InvalidOperationException("A viewport history gesture requires pointer capture."); if (m_transaction is not null) throw new InvalidOperationException("The current viewport gesture already owns a history transaction."); - m_transaction = m_history.BeginTransaction(name); + m_transaction = new EditorEditGesture(m_history, name); } /// @@ -83,14 +79,19 @@ public void BeginHistoryGesture(string name) /// public void CompleteHistoryGesture(bool commit) { - EditorHistoryTransaction? transaction = m_transaction; + EditorEditGesture? transaction = m_transaction; if (transaction is null) return; - m_transaction = null; if (commit) transaction.Commit(); else - _ = transaction.Rollback(); + { + EditorHistoryResult result = transaction.Cancel(); + if (!result.succeeded) + throw new InvalidOperationException($"The viewport gesture could not roll back: {result.message}"); + } + + m_transaction = null; transaction.Dispose(); } @@ -104,7 +105,6 @@ public void CompleteHistoryGesture(bool commit) /// World coordinates. /// public Vector2 ScreenToWorld(Vector2 screenPosition) => m_coordinates.ScreenToWorld(screenPosition); - /// /// Converts world coordinates to viewport-local pixel coordinates. /// @@ -115,9 +115,7 @@ public void CompleteHistoryGesture(bool commit) /// Viewport-local pixel coordinates. /// public Vector2 WorldToScreen(Vector2 worldPosition) => m_coordinates.WorldToScreen(worldPosition); - internal bool Accepts(int pointerId) => !m_capturedPointerId.HasValue || m_capturedPointerId.Value == pointerId; - internal void Cancel() { CompleteHistoryGesture(commit: false); diff --git a/src/composition/editor/framework/Inno.Editor.Settings/Inno.Editor.Settings.csproj b/src/composition/editor/framework/Inno.Editor.Settings/Inno.Editor.Settings.csproj index 970e97138..18eecd14a 100644 --- a/src/composition/editor/framework/Inno.Editor.Settings/Inno.Editor.Settings.csproj +++ b/src/composition/editor/framework/Inno.Editor.Settings/Inno.Editor.Settings.csproj @@ -10,21 +10,12 @@ - - - + + + + - + diff --git a/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs b/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs index b21593c24..87b97364f 100644 --- a/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs +++ b/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettings.cs @@ -1,7 +1,7 @@ +using Inno.Core.Events; using System; using System.Collections.Generic; using System.IO; - using Inno.Core.Logging; using Inno.Core.Serialization; using Inno.Extensibility.Types; @@ -9,19 +9,18 @@ using Inno.Editor.Interactions; namespace Inno.Editor.Settings; - /// /// Owns the discovered Settings catalog and the project-root Settings document. /// [EditorModule("editor-settings", order: int.MinValue)] public sealed class EditorSettings : EditorModule { + private readonly EventDispatcher m_events; private readonly EditorSettingsCatalog m_catalog; private readonly IEditorHistory m_history; private readonly Logger m_log; private readonly object m_sync = new(); private readonly EditorSettingsStore m_store; - /// /// Creates the Settings service for one project and its shared editor history. /// @@ -32,7 +31,7 @@ public sealed class EditorSettings : EditorModule /// The runtime that owns the shared Undo and Redo history. /// /// - /// Thrown when or is + /// Thrown when or is /// . /// /// @@ -62,20 +61,46 @@ SerializationRegistry serialization m_catalog = new EditorSettingsCatalog(types); m_store = new EditorSettingsStore(context.projectDirectory, serialization); m_history = interactions.history; + m_events = interactions.events; m_log = logs.CreateLogger(); } /// - /// Occurs after a complete Settings Apply, Undo, or Redo changes the effective document. - /// The committed service is the event's only argument. + /// Observes committed Apply, Undo and Redo changes through an owned Core Event hub. /// - public event Action? changed; + /// + /// Synchronous callback reading the current committed settings; observer failures are logged and isolated. + /// + /// + /// A subscription owner that must be disposed before its feature or extension generation retires. + /// + /// + /// The observer is null. + /// + public IDisposable ListenChanges(Action observer) + { + ArgumentNullException.ThrowIfNull(observer); + EventHub hub = m_events.CreateHub(); + hub.Listen(change => + { + if (!ReferenceEquals(change.settings, this)) + return; + try + { + observer(this); + } + catch (Exception exception) + { + m_log.Write(LogLevel.Error, "Editor Settings changed subscriber failed: {0}", [exception]); + } + }); + return hub; + } /// /// Gets the current discovered type-catalog revision. /// public long catalogRevision => m_catalog.snapshot.revision; - /// /// Gets all discovered path definitions in deterministic path order. /// @@ -98,9 +123,7 @@ public EditorSettingObject Get(string path) EditorSetting definition = ResolveField(path); lock (m_sync) { - return m_store.TryGet(definition.path, out EditorSettingObject? value) && value is not null - ? value - : definition.CreateDefault(); + return m_store.TryGet(definition.path, out EditorSettingObject? value) && value is not null ? value : definition.CreateDefault(); } } @@ -133,10 +156,8 @@ public bool Apply( IReadOnlySet? resets = null ) { ArgumentNullException.ThrowIfNull(values); - var normalizedValues = new Dictionary( - values.Count, - StringComparer.Ordinal); - foreach ((string path, EditorSettingObject value) in values) + var normalizedValues = new Dictionary(values.Count, StringComparer.Ordinal); + foreach ((string path, EditorSettingObject value)in values) { ArgumentNullException.ThrowIfNull(value); EditorSetting definition = ResolveField(path); @@ -162,26 +183,27 @@ public bool Apply( foreach (string path in normalizedResets) storageChanged |= replacement.Remove(path); } - foreach ((string path, EditorSettingObject candidate) in normalizedValues) + + foreach ((string path, EditorSettingObject candidate)in normalizedValues) { if (normalizedResets?.Contains(path) == true) continue; - if (replacement.TryGetValue(path, out EditorSettingObject? current) && - current.ValueEquals(candidate)) + if (replacement.TryGetValue(path, out EditorSettingObject? current) && current.ValueEquals(candidate)) { continue; } - if (!replacement.ContainsKey(path) && - ResolveField(path).CreateDefault().ValueEquals(candidate)) + + if (!replacement.ContainsKey(path) && ResolveField(path).CreateDefault().ValueEquals(candidate)) { continue; } + replacement[path] = candidate.Copy(); storageChanged = true; } + if (!storageChanged) return false; - before = m_store.GetDocument(); m_store.Replace(replacement); after = m_store.GetDocument(); @@ -207,7 +229,6 @@ public bool Apply( /// Releases resources retained by this feature after it has stopped. /// protected override void OnDispose() => m_catalog.Dispose(); - internal void RestoreFromHistory(ReadOnlySpan document) { lock (m_sync) @@ -226,27 +247,7 @@ internal void ValidateDocument(ReadOnlySpan document) m_store.ValidateDocument(document); } - internal void NotifyChanged() - { - Action? handlers = changed; - if (handlers is null) - return; - foreach (Delegate subscription in handlers.GetInvocationList()) - { - try - { - ((Action)subscription)(this); - } - catch (Exception exception) - { - m_log.Write( - LogLevel.Error, - "Editor Settings changed subscriber failed: {0}", - [exception]); - } - } - } - + internal void NotifyChanged() => m_events.Emit(new EditorSettingsChangedEvent(this)); private EditorSetting ResolveField(string path) { string normalized = EditorSettingsPathUtility.Normalize(path); diff --git a/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettingsChangedEvent.cs b/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettingsChangedEvent.cs new file mode 100644 index 000000000..74d805b2d --- /dev/null +++ b/src/composition/editor/framework/Inno.Editor.Settings/Runtime/EditorSettingsChangedEvent.cs @@ -0,0 +1,7 @@ +using Inno.Core.Events; + +namespace Inno.Editor.Settings; +internal sealed class EditorSettingsChangedEvent(EditorSettings settings) : Event +{ + internal EditorSettings settings { get; } = settings; +} diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs index 52d9d8c4e..b5c5804f8 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; - using Inno.Core.Events; using Inno.Core.Execution; using Inno.Core.Layers; @@ -11,10 +10,8 @@ using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.PlayMode; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; namespace Inno.Editor.Hosting; - /// /// Bridges the engine layer lifecycle to the attribute-discovered editor runtime. /// @@ -30,7 +27,6 @@ internal sealed class EditorLayer : Layer private bool m_isShutdownPrepared; private float m_totalTime; private double m_nextPersistenceRetryTime; - internal EditorLayer( IPresentationContext presentation, EditorContext context, @@ -41,23 +37,13 @@ internal EditorLayer( m_presentation = presentation ?? throw new ArgumentNullException(nameof(presentation)); m_context = context; m_log = logs.CreateLogger(); - EditorWidget.SetupStyle(); m_resources.Own(m_diagnostics); - m_runtime = m_resources.Own(new ImGuiEditorRuntime( - context, - types, - logs, - CreateHostServices(hostServices, m_playModeLoop))); + m_runtime = m_resources.Own(new ImGuiEditorRuntime(context, types, logs, CreateHostServices(hostServices, m_playModeLoop))); } internal int panelCount => m_runtime.panelCount; - internal bool isFocused { get; set; } - - internal float totalTime - { - set => m_totalTime = value; - } + internal float totalTime { set => m_totalTime = value; } internal void DisposeUnattached() { @@ -65,7 +51,6 @@ internal void DisposeUnattached() } internal void QuiescePlayMode() => m_playModeLoop.Quiesce(); - /// /// Starts the editor extension runtime and subscribes its host-level input handling. /// @@ -91,7 +76,6 @@ public override void OnDetach() /// The elapsed frame time in seconds. /// public override void OnUpdate(float deltaTime) => m_playModeLoop.Tick(deltaTime); - /// /// Updates editor presentation after the active Play Mode session has advanced. /// @@ -129,11 +113,9 @@ internal bool PrepareShutdown() { if (m_diagnostics.PublishPersistenceFailure(exception)) { - m_log.Write( - LogLevel.Error, - "Project editor state could not be saved to '{0}': {1}", - [m_context.layoutPath, exception]); + m_log.Write(LogLevel.Error, "Project editor state could not be saved to '{0}': {1}", [m_context.layoutPath, exception]); } + return false; } } @@ -141,12 +123,11 @@ internal bool PrepareShutdown() private void SaveLayoutIfChanged() { bool layoutChanged = CaptureLayout(force: false); - if (!layoutChanged && - (!m_diagnostics.hasPersistenceFailure || - m_context.frame.totalTime < m_nextPersistenceRetryTime)) + if (!layoutChanged && (!m_diagnostics.hasPersistenceFailure || m_context.frame.totalTime < m_nextPersistenceRetryTime)) { return; } + try { _ = m_context.SaveLayoutIfChanged(); @@ -158,10 +139,7 @@ private void SaveLayoutIfChanged() m_nextPersistenceRetryTime = m_context.frame.totalTime + 1.0; if (m_diagnostics.PublishPersistenceFailure(exception)) { - m_log.Write( - LogLevel.Error, - "Project editor state could not be saved to '{0}': {1}", - [m_context.layoutPath, exception]); + m_log.Write(LogLevel.Error, "Project editor state could not be saved to '{0}': {1}", [m_context.layoutPath, exception]); } } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs deleted file mode 100644 index 471924f4f..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetEditorModule.cs +++ /dev/null @@ -1,734 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.ExceptionServices; - -using Inno.Assets; -using Inno.Assets.Pipeline; -using Inno.Core.Logging; -using Inno.Extensibility.Types; -using Inno.Editor.Core; -using Inno.Editor.Assets; -using Inno.Editor.Inspection; -using Inno.Editor.Interactions; -using Inno.Editor.Settings; -using Inno.Plugins.Authoring; -using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; - -namespace Inno.Editor.Panel.FileBrowser; - -/// -/// Owns shared Asset Browser state and asset-type extension dispatch. -/// -[EditorModule("asset-browser", order: 100)] -public sealed partial class AssetEditorModule : EditorModule, IInspectionIconProvider -{ - private readonly AssetPipeline m_pipeline; - private readonly AssetEditorRegistry m_editors; - private readonly AssetCreationRegistry m_creations; - private readonly AssetIconRegistry m_icons; - private readonly EditorSettings m_settings; - private readonly EditorInteractions m_interactions; - private readonly Logger m_log; - private readonly PluginEnvironment m_plugins; - private EditorContext? m_context; - - /// - /// Creates the Asset Browser feature module. - /// - /// - /// The active editor interaction entry point. - /// - /// - /// The project Settings service that owns semantic icon values. - /// - /// - /// The isolated authoring asset pipeline used by browser operations. - /// - /// - /// The active Plugin catalog that authoritatively identifies Plugin-owned source mounts. - /// - /// - /// The active type catalog used to build extension registry snapshots. - /// - /// - /// The application log router used for Asset Browser operation diagnostics. - /// - /// - /// Thrown when any required service is . - /// - public AssetEditorModule( - EditorInteractions interactions, - EditorSettings settings, - AssetPipeline pipeline, - PluginEnvironment plugins, - TypeCatalog types, - LogRouter logs - ) { - m_interactions = interactions ?? throw new ArgumentNullException(nameof(interactions)); - m_settings = settings ?? throw new ArgumentNullException(nameof(settings)); - m_pipeline = pipeline ?? throw new ArgumentNullException(nameof(pipeline)); - m_plugins = plugins ?? throw new ArgumentNullException(nameof(plugins)); - ArgumentNullException.ThrowIfNull(types); - ArgumentNullException.ThrowIfNull(logs); - m_log = logs.CreateLogger(); - m_editors = new AssetEditorRegistry(types); - m_creations = new AssetCreationRegistry(types); - m_icons = new AssetIconRegistry(m_settings, types); - browser = new AssetBrowserState(interactions, pipeline); - } - - /// - /// Gets shared Asset Browser navigation and selection state. - /// - public AssetBrowserState browser { get; } - - /// - /// Captures an immutable snapshot of the current observable state. - /// - /// - /// The lifecycle or domain state applied by this operation. - /// - protected override void Capture(EditorState state) - { - state.Set("root", browser.root.ToString()); - state.Set("assetsDirectory", browser.GetDirectory(AssetBrowserRoot.Assets)); - state.Set("pluginsDirectory", browser.GetDirectory(AssetBrowserRoot.Plugins)); - } - - /// - /// Restores the supplied snapshot while preserving current invariants. - /// - /// - /// The lifecycle or domain state applied by this operation. - /// - protected override void Restore(EditorState state) - { - AssetBrowserRoot restoredRoot = Enum.TryParse( - state.Get("root", string.Empty), - out AssetBrowserRoot parsedRoot) - ? parsedRoot - : AssetBrowserRoot.Assets; - string assetsDirectory = RestoreDirectory( - AssetBrowserRoot.Assets, - state.Get("assetsDirectory", string.Empty)); - string pluginsDirectory = RestoreDirectory( - AssetBrowserRoot.Plugins, - state.Get("pluginsDirectory", string.Empty)); - browser.Restore(restoredRoot, assetsDirectory, pluginsDirectory); - } - - private string RestoreDirectory( - AssetBrowserRoot root, - string path - ) { - string directory = NormalizePath(path); - if (string.IsNullOrEmpty(directory)) - return string.Empty; - AssetPath isolated = AssetPath.Parse(directory); - if ((root == AssetBrowserRoot.Assets) != (isolated.source == AssetSourceId.project)) - return string.Empty; - while (!IsAvailableDirectory(directory)) - { - string parent = GetParentDirectory(directory); - if (string.Equals(parent, directory, StringComparison.Ordinal)) - return string.Empty; - directory = parent; - } - return directory; - } - - private bool IsAvailableDirectory(string path) - => m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(path), out AssetFileEntry entry) - && entry.isDirectory; - - internal EditorInteractions interactions => m_interactions; - - internal AssetPipeline pipeline => m_pipeline; - - internal IReadOnlyList creationTemplates => m_creations.templates; - - internal bool CanCreateAsset( - string directory, - string templateId - ) { - if (!m_pipeline.isInitialized || !m_creations.TryGet(templateId, out _)) - return false; - AssetPath path = AssetPath.Parse(NormalizePath(directory)); - AssetSourceMount? mount = m_pipeline.sourceMounts.FirstOrDefault(source => source.id == path.source); - return mount is { isReadOnly: false } - && (string.IsNullOrEmpty(path.localPath) - || m_pipeline.TryGetFileSystemEntry(path, out AssetFileEntry entry) && entry.isDirectory); - } - - internal AssetFileEntry CreateAsset( - string directory, - string templateId - ) { - if (!m_creations.TryGet(templateId, out AssetCreationRegistry.Registration? registration) - || registration is null) - { - throw new InvalidOperationException($"Asset creation template '{templateId}' is unavailable."); - } - if (!CanCreateAsset(directory, templateId)) - throw new InvalidOperationException("The selected Asset directory is not writable."); - - AssetCreationMenuAttribute declaration = registration.declaration; - AssetPath parent = AssetPath.Parse(NormalizePath(directory)); - AssetSourceMount mount = m_pipeline.sourceMounts.Single(source => source.id == parent.source); - string prefix = parent.localPath.TrimEnd('/'); - if (prefix.Length != 0) - prefix += "/"; - int suffix = 1; - AssetPath path; - do - { - string number = suffix == 1 ? string.Empty : " " + suffix; - path = new AssetPath( - parent.source, - prefix + declaration.defaultName + number + declaration.extension); - suffix++; - } - while (File.Exists(mount.Resolve(path.localPath)) - || File.Exists(mount.Resolve(path.localPath) + ".imeta")); - - AssetObject candidate = registration.template.Create() - ?? throw new InvalidOperationException( - $"Asset creation template '{templateId}' returned no asset."); - if (candidate.GetType() != registration.template.assetType) - { - throw new InvalidOperationException( - $"Asset creation template '{templateId}' returned '{candidate.GetType().FullName}' " + - $"instead of '{registration.template.assetType.FullName}'."); - } - AssetSourceStore sources = m_pipeline.CreateSourceStore(); - byte[] source = registration.template.Encode(new AssetCreationContext(sources), candidate) - ?? throw new InvalidOperationException( - $"Asset creation template '{templateId}' returned no source data."); - return CreateSource(path, source); - } - - internal bool IsPluginSource(AssetSourceId source) => m_plugins.TryGet(source, out _); - - internal bool IsPluginRoot(AssetFileEntry entry) - { - ArgumentNullException.ThrowIfNull(entry); - return string.IsNullOrEmpty(entry.assetPath.localPath) && IsPluginSource(entry.source); - } - - internal string folderIcon => m_settings - .Get("Editor/Appearance/Icons/Folder") - .GetAsString("value", Inno.Adapter.Presentation.ImGui.ImGuiIcon.Folder)!; - - internal bool TryCreateContext( - EditorContext editor, - string relativePath, - out AssetEditorContext? context - ) { - if (!m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry)) - { - context = null; - return false; - } - _ = m_pipeline.TryGetInfo(AssetPath.Parse(relativePath), out AssetInfo? info); - _ = m_pipeline.TryGetAssetType(AssetPath.Parse(relativePath), out Type? assetType); - context = new AssetEditorContext( - editor, - m_interactions, - entry.assetPath.ToString(), - entry.name, - entry.isDirectory, - info, - assetType, - () => CreateDefaultDragData(entry)); - return true; - } - - internal bool Open(AssetEditorContext context) - { - AssetEditor editor = m_editors.Resolve(context.assetType); - if (!editor.CanOpen(context)) - return false; - try - { - editor.Open(context); - return true; - } - catch (Exception exception) - { - m_log.Write( - LogLevel.Error, - "Failed to open asset '{0}': {1}", - [context.relativePath, exception]); - return false; - } - } - - internal EditorValidationResult ValidateRename( - AssetEditorContext context, - string newName - ) { - AssetEditor editor = m_editors.Resolve(context.assetType); - return ToEditorValidation(ValidateRename( - editor, - context, - Normalize(context.relativePath), - newName, - validateEditor: true, - out _)); - } - - internal void Rename( - AssetEditorContext context, - string newName - ) { - AssetEditor editor = m_editors.Resolve(context.assetType); - AssetOperationValidation validation = ValidateRename( - editor, - context, - Normalize(context.relativePath), - newName, - validateEditor: false, - out string targetPath); - if (!validation.isValid) - throw new InvalidOperationException(validation.message); - string sourcePath = Normalize(context.relativePath); - EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); - if (!result.succeeded) - throw new InvalidOperationException(result.message); - var data = new AssetHistoryData( - AssetHistoryOperationKind.Move, - sourcePath, - targetPath, - isDirectory: context.isDirectory, - archive: []); - try - { - m_interactions.history.RecordApplied( - "Rename Asset", - new EditorHistoryChange( - AssetHistoryKinds.SourceOperation, - EditorHistoryPayload.FromBytes(data.Encode()))); - } - catch (Exception exception) - { - RollbackAndRethrow( - exception, - () => RequireMove(editor, context, targetPath, sourcePath)); - } - } - - /// - /// Determines whether an asset source can move into a target directory without conflicts. - /// - /// - /// The current source-relative asset path. - /// - /// - /// The destination source-relative directory, or an empty string for the root. - /// - /// - /// when the requested move is valid. - /// - internal bool CanMoveToDirectory( - string sourcePath, - string targetDirectory - ) - => TryPrepareMove( - sourcePath, - targetDirectory, - validateEditor: true, - out _, - out _, - out _); - - /// - /// Moves an asset into a directory and records the committed path transaction in Editor history. - /// - /// - /// The current source-relative asset path. - /// - /// - /// The destination source-relative directory, or an empty string for the root. - /// - /// - /// The active history that receives the committed move. - /// - /// - /// The source entry at its committed destination path. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when validation or the Asset transaction fails. - /// - internal AssetFileEntry MoveToDirectoryWithHistory( - string sourcePath, - string targetDirectory, - IEditorHistory history - ) { - ArgumentNullException.ThrowIfNull(history); - if (!TryPrepareMove( - sourcePath, - targetDirectory, - validateEditor: true, - out AssetEditorContext? context, - out AssetEditor? editor, - out string targetPath) || - context is null || - editor is null) - { - throw new InvalidOperationException( - $"Asset '{sourcePath}' cannot be moved to '{targetDirectory}'."); - } - - string normalizedSource = Normalize(sourcePath); - EditorHistoryResult result = MoveAsset(editor, context, normalizedSource, targetPath); - if (!result.succeeded) - throw new InvalidOperationException(result.message); - - var data = new AssetHistoryData( - AssetHistoryOperationKind.Move, - normalizedSource, - targetPath, - context.isDirectory, - archive: []); - try - { - history.RecordApplied( - "Move Asset", - new EditorHistoryChange( - AssetHistoryKinds.SourceOperation, - EditorHistoryPayload.FromBytes(data.Encode()))); - } - catch (Exception exception) - { - RollbackAndRethrow( - exception, - () => RequireMove(editor, context, targetPath, normalizedSource)); - } - - return m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out AssetFileEntry moved) - ? moved - : throw new InvalidOperationException( - $"Moved asset '{targetPath}' is unavailable after the transaction."); - } - - internal bool Delete(AssetEditorContext context) - { - AssetEditor editor = m_editors.Resolve(context.assetType); - AssetOperationValidation validation = editor.ValidateDelete(context); - if (!validation.isValid) - { - m_log.Write( - LogLevel.Warn, - "Asset delete was rejected for '{0}': {1}", - [context.relativePath, validation.message]); - return false; - } - string path = context.relativePath; - m_pipeline.Delete(AssetPath.Parse(path)); - try - { - editor.OnDeleted(context); - } - catch (Exception exception) - { - m_log.Write( - LogLevel.Error, - "Asset editor delete hook failed for '{0}': {1}", - [path, exception]); - } - return true; - } - - internal bool DeleteWithHistory( - EditorActionContext context, - AssetEditorContext asset - ) { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(asset); - byte[] archive = AssetSourceArchive.Capture(m_pipeline, asset.relativePath, out bool isDirectory); - if (!Delete(asset)) - return false; - var data = new AssetHistoryData( - AssetHistoryOperationKind.Delete, - Normalize(asset.relativePath), - string.Empty, - isDirectory, - archive); - try - { - context.history.RecordApplied( - "Delete Asset", - new EditorHistoryChange( - AssetHistoryKinds.SourceOperation, - EditorHistoryPayload.FromBytes(data.Encode()))); - } - catch (Exception exception) - { - RollbackAndRethrow( - exception, - () => AssetSourceArchive.Restore( - m_pipeline, - Normalize(asset.relativePath), - isDirectory, - archive)); - } - return true; - } - - internal void MoveFromHistory( - string sourcePath, - string targetPath - ) { - EditorContext editorContext = m_context - ?? throw new InvalidOperationException("Asset editor module is not attached."); - if (!TryCreateContext(editorContext, sourcePath, out AssetEditorContext? context) || context is null) - throw new InvalidOperationException($"Asset '{sourcePath}' is no longer available."); - AssetEditor editor = m_editors.Resolve(context.assetType); - EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); - if (!result.succeeded) - throw new InvalidOperationException(result.message); - } - - internal void DeleteFromHistory(string relativePath) - { - EditorContext editorContext = m_context - ?? throw new InvalidOperationException("Asset editor module is not attached."); - if (!TryCreateContext(editorContext, relativePath, out AssetEditorContext? context) || context is null) - throw new InvalidOperationException($"Asset '{relativePath}' is no longer available."); - if (!Delete(context)) - throw new InvalidOperationException($"Asset '{relativePath}' could not be deleted."); - } - - internal void SelectPath(string relativePath) - { - object? target = m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry) - ? entry - : null; - _ = m_interactions.For(FileBrowserInteractionIds.C_AREA, target).Select(); - } - - internal bool TryCreateDragData( - AssetEditorContext context, - out EditorDragData? data - ) { - AssetEditor editor = m_editors.Resolve(context.assetType); - if (!editor.CanStartDrag(context)) - { - data = null; - return false; - } - data = editor.CreateDragData(context); - return data is not null; - } - - /// - /// Resolves the presentation icon registered for an asset type or source extension. - /// - /// - /// The source entry whose presentation icon should be resolved. - /// - /// - /// The most specific registered icon, or the built-in directory or file icon when no - /// registration matches the entry. - /// - /// - /// Thrown when is . - /// - public string GetIcon(AssetFileEntry entry) - { - ArgumentNullException.ThrowIfNull(entry); - if (entry.isDirectory) - return folderIcon; - _ = m_pipeline.TryGetAssetType(entry.assetPath, out Type? assetType); - if (m_icons.TryResolve(assetType, entry.assetPath.ToString(), out string icon)) - { - return icon; - } - return m_settings - .Get("Editor/Appearance/Icons/File") - .GetAsString("value", Inno.Adapter.Presentation.ImGui.ImGuiIcon.File)!; - } - - /// - /// Initializes this feature when its owning runtime becomes active. - /// - /// - /// The context that supplies state and services for this operation. - /// - protected override void OnStart(EditorContext context) - { - m_context = context ?? throw new ArgumentNullException(nameof(context)); - } - - /// - /// Stops this feature before its owning runtime releases the active generation. - /// - /// - /// The context that supplies state and services for this operation. - /// - protected override void OnStop(EditorContext context) - { - m_context = null; - } - - /// - /// Releases resources retained by this feature after it has stopped. - /// - protected override void OnDispose() - { - m_creations.Dispose(); - m_editors.Dispose(); - m_icons.Dispose(); - } - - private AssetOperationValidation ValidateRename( - AssetEditor editor, - AssetEditorContext context, - string sourcePath, - string newName, - bool validateEditor, - out string targetPath - ) { - newName = newName.Trim(); - targetPath = string.Empty; - if (string.IsNullOrEmpty(newName)) - return AssetOperationValidation.Invalid("An asset name is required."); - if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || - newName.Contains('/') || newName.Contains('\\')) - { - return AssetOperationValidation.Invalid( - "Asset names cannot contain path separators or invalid file-name characters."); - } - - string renamedEntry = ComposeRenamedEntryName(sourcePath, newName, context.isDirectory); - string parent = Normalize(Path.GetDirectoryName(sourcePath)); - targetPath = string.IsNullOrEmpty(parent) ? renamedEntry : $"{parent}/{renamedEntry}"; - if (!string.Equals(sourcePath, targetPath, StringComparison.OrdinalIgnoreCase) && - m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) - { - return AssetOperationValidation.Invalid($"Asset '{targetPath}' already exists."); - } - return validateEditor - ? editor.ValidateRename(context, targetPath) - : AssetOperationValidation.valid; - } - - private bool TryPrepareMove( - string sourcePath, - string targetDirectory, - bool validateEditor, - out AssetEditorContext? context, - out AssetEditor? editor, - out string targetPath - ) { - context = null; - editor = null; - targetPath = string.Empty; - string source = Normalize(sourcePath); - string directory = Normalize(targetDirectory); - AssetPath isolatedSource = AssetPath.Parse(source); - AssetPath isolatedDirectory = AssetPath.Parse(directory); - if (isolatedSource.source != AssetSourceId.project || - isolatedDirectory.source != AssetSourceId.project) - { - return false; - } - EditorContext? editorContext = m_context; - if (editorContext is null || - string.IsNullOrEmpty(source) || - !TryCreateContext(editorContext, source, out context) || - context is null) - { - return false; - } - if (!string.IsNullOrEmpty(directory) && - (!m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(directory), out AssetFileEntry destination) || - !destination.isDirectory)) - { - return false; - } - - string parent = Normalize(Path.GetDirectoryName(source)); - if (string.Equals(parent, directory, StringComparison.OrdinalIgnoreCase)) - return false; - if (context.isDirectory && - (string.Equals(source, directory, StringComparison.OrdinalIgnoreCase) || - directory.StartsWith(source + "/", StringComparison.OrdinalIgnoreCase))) - { - return false; - } - - string name = Path.GetFileName(source); - targetPath = string.IsNullOrEmpty(directory) ? name : $"{directory}/{name}"; - if (m_pipeline.TryGetFileSystemEntry(AssetPath.Parse(targetPath), out _)) - return false; - - editor = m_editors.Resolve(context.assetType); - return !validateEditor || editor.ValidateRename(context, targetPath).isValid; - } - - private EditorHistoryResult MoveAsset( - AssetEditor editor, - AssetEditorContext context, - string sourcePath, - string targetPath - ) { - m_pipeline.Move(AssetPath.Parse(sourcePath), AssetPath.Parse(targetPath)); - try - { - editor.OnRenamed(context, sourcePath, targetPath); - } - catch (Exception exception) - { - m_log.Write( - LogLevel.Error, - "Asset editor rename hook failed for '{0}': {1}", - [targetPath, exception]); - } - return EditorHistoryResult.Success(); - } - - private void RequireMove( - AssetEditor editor, - AssetEditorContext context, - string sourcePath, - string targetPath - ) { - EditorHistoryResult result = MoveAsset(editor, context, sourcePath, targetPath); - if (!result.succeeded) - throw new InvalidOperationException(result.message); - } - - private static void RollbackAndRethrow( - Exception failure, - Action rollback - ) { - try - { - rollback(); - } - catch (Exception rollbackException) - { - throw new AggregateException( - "An Asset mutation could not be recorded and its compensation also failed.", - failure, - rollbackException); - } - ExceptionDispatchInfo.Capture(failure).Throw(); - } - - private static EditorValidationResult ToEditorValidation(AssetOperationValidation validation) - => new(validation.isValid, validation.message); - - private static string Normalize(string? path) - => string.IsNullOrWhiteSpace(path) - ? string.Empty - : path.Replace('\\', '/').Trim('/'); - - private static EditorDragData CreateDefaultDragData(AssetFileEntry entry) => new(entry, entry.name); -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetTypeDistance.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetTypeDistance.cs deleted file mode 100644 index 0b8651454..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetTypeDistance.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace Inno.Editor.Panel.FileBrowser; - -internal static class AssetTypeDistance -{ - internal static bool TryGet( - Type concreteType, - Type candidateType, - out int distance - ) { - if (!candidateType.IsAssignableFrom(concreteType)) - { - distance = int.MaxValue; - return false; - } - - if (concreteType == candidateType) - { - distance = 0; - return true; - } - - distance = 1; - Type? current = concreteType.BaseType; - while (current is not null) - { - if (current == candidateType) - return true; - distance++; - current = current.BaseType; - } - - distance = candidateType.IsInterface ? 1 : distance; - return true; - } -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserChangeTracker.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserChangeTracker.cs index 0da02ceee..269c627d4 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserChangeTracker.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserChangeTracker.cs @@ -1,31 +1,29 @@ - +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.IO; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed class FileBrowserChangeTracker(AssetEditorModule assets) +internal sealed class FileBrowserChangeTracker(AssetBrowserModule assets) { private EditorContext? m_context; - internal void Attach(EditorContext context) { ArgumentNullException.ThrowIfNull(context); m_context = context; - assets.pipeline.Changed -= OnAssetsChanged; - assets.pipeline.Changed += OnAssetsChanged; - assets.pipeline.SourceMountsChanged -= OnSourceMountsChanged; - assets.pipeline.SourceMountsChanged += OnSourceMountsChanged; + assets.edits.pipeline.Changed -= OnAssetsChanged; + assets.edits.pipeline.Changed += OnAssetsChanged; + assets.edits.pipeline.SourceMountsChanged -= OnSourceMountsChanged; + assets.edits.pipeline.SourceMountsChanged += OnSourceMountsChanged; } internal void Detach() { - assets.pipeline.Changed -= OnAssetsChanged; - assets.pipeline.SourceMountsChanged -= OnSourceMountsChanged; + assets.edits.pipeline.Changed -= OnAssetsChanged; + assets.edits.pipeline.SourceMountsChanged -= OnSourceMountsChanged; m_context = null; } @@ -35,12 +33,10 @@ private void OnSourceMountsChanged() if (context is null) return; string currentDirectory = FileBrowserUtility.NormalizePath(assets.browser.currentDirectory); - if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(currentDirectory), out AssetFileEntry current) - || !current.isDirectory) + if (!assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(currentDirectory), out AssetFileEntry current) || !current.isDirectory) assets.browser.SetCurrentDirectory(string.Empty); string selectedPath = FileBrowserUtility.NormalizePath(assets.browser.GetSelectedPath(context)); - if (!string.IsNullOrEmpty(selectedPath) - && !assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(selectedPath), out _)) + if (!string.IsNullOrEmpty(selectedPath) && !assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(selectedPath), out _)) assets.browser.Select(context, null); } @@ -49,15 +45,11 @@ private void OnAssetsChanged(AssetChangeSet changes) EditorContext? context = m_context; if (context is null) return; - for (int i = 0; i < changes.changes.Count; i++) { AssetChange change = changes.changes[i]; if (change.kind == AssetChangeKind.Moved) - ApplyMove( - context, - change.previousAssetPath?.ToString() ?? string.Empty, - change.assetPath.ToString()); + ApplyMove(context, change.previousAssetPath?.ToString() ?? string.Empty, change.assetPath.ToString()); else if (change.kind is AssetChangeKind.Missing or AssetChangeKind.Removed) ApplyRemoval(context, change.assetPath.ToString()); } @@ -72,22 +64,17 @@ string newPath newPath = Normalize(newPath); if (string.IsNullOrEmpty(oldPath) || string.IsNullOrEmpty(newPath)) return; - string currentDirectory = Normalize(assets.browser.currentDirectory); if (IsSameOrDescendant(currentDirectory, oldPath)) { - string suffix = currentDirectory.Length == oldPath.Length - ? string.Empty - : currentDirectory[oldPath.Length..].TrimStart('/'); + string suffix = currentDirectory.Length == oldPath.Length ? string.Empty : currentDirectory[oldPath.Length..].TrimStart('/'); assets.browser.SetCurrentDirectory(Combine(newPath, suffix)); } string selectedPath = Normalize(assets.browser.GetSelectedPath(context)); if (!IsSameOrDescendant(selectedPath, oldPath)) return; - string selectedSuffix = selectedPath.Length == oldPath.Length - ? string.Empty - : selectedPath[oldPath.Length..].TrimStart('/'); + string selectedSuffix = selectedPath.Length == oldPath.Length ? string.Empty : selectedPath[oldPath.Length..].TrimStart('/'); assets.browser.Select(context, Combine(newPath, selectedSuffix)); } @@ -98,26 +85,28 @@ string removedPath removedPath = Normalize(removedPath); if (string.IsNullOrEmpty(removedPath)) return; - string selectedPath = Normalize(assets.browser.GetSelectedPath(context)); if (IsSameOrDescendant(selectedPath, removedPath)) assets.browser.Select(context, null); - string currentDirectory = Normalize(assets.browser.currentDirectory); if (!IsSameOrDescendant(currentDirectory, removedPath)) return; - string fallback = FileBrowserUtility.GetParentDirectory(removedPath); while (!string.Equals( - fallback, - FileBrowserUtility.GetParentDirectory(fallback), - StringComparison.Ordinal) && - (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(fallback), out var entry) || !entry.isDirectory)) + fallback, + FileBrowserUtility.GetParentDirectory(fallback), + StringComparison.Ordinal + ) + && (!assets.edits.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(fallback), + out var entry + ) + || !entry.isDirectory)) { fallback = FileBrowserUtility.GetParentDirectory(fallback); } - if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(fallback), out AssetFileEntry available) - || !available.isDirectory) + + if (!assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(fallback), out AssetFileEntry available) || !available.isDirectory) fallback = string.Empty; assets.browser.SetCurrentDirectory(fallback); } @@ -125,17 +114,10 @@ string removedPath private static bool IsSameOrDescendant( string path, string ancestor - ) - => string.Equals(path, ancestor, StringComparison.OrdinalIgnoreCase) || - path.StartsWith(ancestor + "/", StringComparison.OrdinalIgnoreCase); - + ) => string.Equals(path, ancestor, StringComparison.OrdinalIgnoreCase) || path.StartsWith(ancestor + "/", StringComparison.OrdinalIgnoreCase); private static string Combine( string left, string right ) => string.IsNullOrEmpty(right) ? left : $"{left}/{right}"; - - private static string Normalize(string? path) - => string.IsNullOrWhiteSpace(path) - ? string.Empty - : path.Replace('\\', '/').Trim('/'); + private static string Normalize(string? path) => string.IsNullOrWhiteSpace(path) ? string.Empty : path.Replace('\\', '/').Trim('/'); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserContextMenu.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserContextMenu.cs index 866a176dd..05b93ec91 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserContextMenu.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserContextMenu.cs @@ -1,34 +1,31 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; -using Inno.Native.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - internal sealed class FileBrowserContextMenu( - AssetEditorModule assets, + AssetBrowserModule assets, FileBrowserRename rename -) { +) +{ internal void DrawEntry( EditorContext context, string id, string relativePath, FileBrowserPresentation presentation ) { - if (NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Right))) - assets.browser.Select(context, relativePath); - if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry)) + if (!assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry)) return; - string creationDirectory = entry.isDirectory - ? entry.assetPath.ToString() - : GetParentDirectory(entry.assetPath.ToString()); - bool isOpen = EditorMenuRenderer.ContextMenu( - id, - CreateDirectoryInteraction(creationDirectory), - assets.interactions.For(FileBrowserInteractionIds.C_AREA, entry)); + if (Ui.IsItemClicked(ImGuiMouseButton.Right) && !assets.edits.interactions.selection.Contains(entry)) + assets.edits.interactions.SetSelection(entry); + string creationDirectory = entry.isDirectory ? entry.assetPath.ToString() : GetParentDirectory(entry.assetPath.ToString()); + bool isOpen = EditorUI.current.menus.ContextMenu(id, CreateDirectoryInteraction(creationDirectory), assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, entry)); if (isOpen) rename.MarkInteraction(presentation); } @@ -39,11 +36,9 @@ internal void DrawDirectory( string relativePath, FileBrowserPresentation presentation ) { - if (IsReadOnlySource(assets.pipeline, relativePath)) + if (IsReadOnlySource(assets.edits.pipeline, relativePath)) return; - bool isOpen = EditorMenuRenderer.ContextMenu( - id, - CreateDirectoryInteraction(relativePath)); + bool isOpen = EditorUI.current.menus.ContextMenu(id, CreateDirectoryInteraction(relativePath)); if (isOpen) rename.MarkInteraction(presentation); } @@ -53,17 +48,15 @@ internal void DrawBackground( string id, FileBrowserPresentation presentation ) { - if (IsReadOnlyLocation(assets.pipeline, assets.browser)) + if (IsReadOnlyLocation(assets.edits.pipeline, assets.browser)) return; - bool isOpen = EditorMenuRenderer.WindowContextMenu( - id, - CreateDirectoryInteraction(assets.browser.currentDirectory)); + bool isOpen = EditorUI.current.menus.WindowContextMenu(id, CreateDirectoryInteraction(assets.browser.currentDirectory)); if (isOpen) rename.MarkInteraction(presentation); } - private Inno.Editor.Interactions.EditorInteraction CreateDirectoryInteraction(string relativePath) - => assets.interactions.For( - FileBrowserInteractionIds.C_AREA, - NormalizePath(relativePath)); + private Inno.Editor.Interactions.EditorInteraction CreateDirectoryInteraction(string relativePath) => assets.edits.interactions.For( + FileBrowserInteractionIds.C_AREA, + NormalizePath(relativePath) + ); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs index 1519d6792..4fe8acd30 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs @@ -1,4 +1,5 @@ - +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.Linq; @@ -7,7 +8,6 @@ using Inno.Editor.Core; namespace Inno.Editor.Panel.FileBrowser; - internal enum FileBrowserEntryTypeFilter { All, @@ -26,8 +26,7 @@ internal readonly record struct FileBrowserDisplayEntry( string displayName, bool isPluginRoot = false ); - -internal sealed class FileBrowserData(AssetEditorModule assets) +internal sealed class FileBrowserData(AssetBrowserModule assets) { internal IReadOnlyList CollectVisibleEntries( EditorContext context, @@ -36,8 +35,7 @@ internal IReadOnlyList CollectVisibleEntries( string searchFilter ) { List entries = []; - if (assets.browser.root == AssetBrowserRoot.Plugins && - string.IsNullOrEmpty(assets.browser.currentDirectory)) + if (assets.browser.root == AssetBrowserRoot.Plugins && string.IsNullOrEmpty(assets.browser.currentDirectory)) { CollectPluginRoots(entries, scopeFilter); } @@ -49,7 +47,6 @@ string searchFilter } else CollectEntriesRecursive(assets.browser.currentDirectory, entries); - ApplyTypeFilter(entries, typeFilter); ApplySearchFilter(entries, searchFilter); entries.Sort(static ( @@ -57,21 +54,11 @@ string searchFilter right ) => { - int byName = string.Compare( - left.displayName, - right.displayName, - StringComparison.OrdinalIgnoreCase); + int byName = string.Compare(left.displayName, right.displayName, StringComparison.OrdinalIgnoreCase); if (byName != 0) return byName; - int byExtension = string.Compare( - left.entry.extension, - right.entry.extension, - StringComparison.OrdinalIgnoreCase); - return byExtension != 0 - ? byExtension - : string.CompareOrdinal( - left.entry.assetPath.ToString(), - right.entry.assetPath.ToString()); + int byExtension = string.Compare(left.entry.extension, right.entry.extension, StringComparison.OrdinalIgnoreCase); + return byExtension != 0 ? byExtension : string.CompareOrdinal(left.entry.assetPath.ToString(), right.entry.assetPath.ToString()); }); return entries; } @@ -98,8 +85,7 @@ string searchFilter string filter = searchFilter.Trim(); if (!string.IsNullOrEmpty(filter)) { - entries.RemoveAll(item => - !item.displayName.Contains(filter, StringComparison.OrdinalIgnoreCase)); + entries.RemoveAll(item => !item.displayName.Contains(filter, StringComparison.OrdinalIgnoreCase)); } } @@ -107,15 +93,15 @@ private void CollectPluginRoots( List entries, FileBrowserEntryScopeFilter scopeFilter ) { - AssetSourceMount[] mounts = assets.pipeline.sourceMounts - .Where(mount => assets.IsPluginSource(mount.id)) - .OrderBy(static mount => mount.id.value, StringComparer.OrdinalIgnoreCase) - .ToArray(); + AssetSourceMount[] mounts = assets.edits.pipeline.sourceMounts.Where(mount => assets.edits.IsPluginSource(mount.id)).OrderBy( + static mount => mount.id.value, + StringComparer.OrdinalIgnoreCase + ).ToArray(); for (int i = 0; i < mounts.Length; i++) { AssetSourceMount mount = mounts[i]; var rootPath = new AssetPath(mount.id, string.Empty); - if (!assets.pipeline.TryGetFileSystemEntry(rootPath, out AssetFileEntry root)) + if (!assets.edits.pipeline.TryGetFileSystemEntry(rootPath, out AssetFileEntry root)) continue; entries.Add(new FileBrowserDisplayEntry(root, mount.id.value, isPluginRoot: true)); if (scopeFilter == FileBrowserEntryScopeFilter.Recursive) @@ -147,10 +133,7 @@ internal List SortTreeEntries(IReadOnlyList entr { if (left.isDirectory != right.isDirectory) return left.isDirectory ? -1 : 1; - return string.Compare( - left.name, - right.name, - StringComparison.OrdinalIgnoreCase); + return string.Compare(left.name, right.name, StringComparison.OrdinalIgnoreCase); }); return sorted; } @@ -158,7 +141,7 @@ internal List SortTreeEntries(IReadOnlyList entr internal IReadOnlyList GetVisibleChildren(string relativePath) { AssetSourceId source = AssetPath.Parse(relativePath).source; - IReadOnlyList children = assets.pipeline.GetFileSystemChildren(AssetPath.Parse(relativePath)); + IReadOnlyList children = assets.edits.pipeline.GetFileSystemChildren(AssetPath.Parse(relativePath)); if (children.Count == 0) return children; List isolated = []; @@ -167,6 +150,7 @@ internal IReadOnlyList GetVisibleChildren(string relativePath) if (children[i].source == source) isolated.Add(children[i]); } + return isolated; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserDragDrop.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserDragDrop.cs index be502428c..f1236289b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserDragDrop.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserDragDrop.cs @@ -1,21 +1,21 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - /// /// Binds managed File Browser drag sessions to native entry and directory items. /// /// /// The assets used to initialize this instance. /// -internal sealed class FileBrowserDragDrop(AssetEditorModule assets) +internal sealed class FileBrowserDragDrop(AssetBrowserModule assets) { /// /// Publishes the supplied source entry as a managed File Browser drag payload. @@ -30,18 +30,19 @@ internal void DrawAssetSource( EditorContext context, AssetFileEntry entry ) { - if (!assets.TryCreateContext( - context, - entry.assetPath.ToString(), - out AssetEditorContext? assetContext) || - assetContext is null || - !assets.TryCreateDragData(assetContext, out EditorDragData? data) || - data is null) + if (!assets.edits.TryCreateContext( + context, + entry.assetPath.ToString(), + out AssetEditorContext? assetContext + ) + || assetContext is null + || !assets.edits.TryCreateDragData( + assetContext, + out EditorDragData? data + ) + || data is null) return; - _ = EditorDragDropRenderer.Source( - assets.interactions.For(FileBrowserInteractionIds.C_AREA, entry), - data, - () => NativeImGui.TextUnformatted(data.label)); + _ = EditorDragDropRenderer.Source(assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, entry), data, () => Ui.Text(data.label)); } /// @@ -57,17 +58,13 @@ internal void DrawDirectoryTarget( EditorContext context, string relativePath ) { - if (FileBrowserUtility.IsReadOnlySource(assets.pipeline, relativePath)) + if (FileBrowserUtility.IsReadOnlySource(assets.edits.pipeline, relativePath)) return; - System.Numerics.Vector2 minimum = NativeImGui.GetItemRectMin(); - System.Numerics.Vector2 maximum = NativeImGui.GetItemRectMax(); - EditorDropWidgetResult result = EditorDragDropRenderer.Target( - assets.interactions.For( - FileBrowserInteractionIds.C_AREA, - relativePath)); + System.Numerics.Vector2 minimum = Ui.GetItemMinimum(); + System.Numerics.Vector2 maximum = Ui.GetItemMaximum(); + EditorDropWidgetResult result = EditorDragDropRenderer.Target(assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, relativePath)); if (!result.isPreviewing || !result.status.canDrop) return; - - EditorWidget.DropTargetHighlight(minimum, maximum); + EditorUI.current.widgets.DropTargetHighlight(minimum, maximum); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserNavigation.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserNavigation.cs index c345762b6..fb76db4da 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserNavigation.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserNavigation.cs @@ -1,8 +1,7 @@ - - +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; @@ -10,16 +9,12 @@ using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed class FileBrowserNavigation(AssetEditorModule assets) +internal sealed class FileBrowserNavigation(AssetBrowserModule assets) { private readonly Stack m_backHistory = []; private readonly Stack m_forwardHistory = []; - private BrowserLocation m_historyCurrent = BrowserLocation.AssetsRoot; - internal bool canGoBack => m_backHistory.Count > 0; - internal bool canGoForward => m_forwardHistory.Count > 0; internal void NavigateTo( @@ -28,15 +23,10 @@ internal void NavigateTo( string? selectedPathAfterNavigation = null ) { directory = NormalizePath(directory); - AssetBrowserRoot root = string.IsNullOrEmpty(directory) - ? assets.browser.root - : AssetPath.Parse(directory).source == AssetSourceId.project - ? AssetBrowserRoot.Assets - : AssetBrowserRoot.Plugins; + AssetBrowserRoot root = string.IsNullOrEmpty(directory) ? assets.browser.root : AssetPath.Parse(directory).source == AssetSourceId.project ? AssetBrowserRoot.Assets : AssetBrowserRoot.Plugins; var target = new BrowserLocation(root, directory); if (CurrentLocation == target) return; - m_backHistory.Push(CurrentLocation); m_forwardHistory.Clear(); ApplyLocation(context, target); @@ -103,9 +93,8 @@ FileBrowserTree tree NavigateTo(context, entry.assetPath.ToString(), entry.assetPath.ToString()); return; } - _ = assets.interactions - .For(FileBrowserInteractionIds.C_AREA, entry) - .Execute(FileBrowserInteractionIds.C_OPEN); + + _ = assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, entry).Execute(FileBrowserInteractionIds.C_OPEN); } internal void SyncExternalDirectoryChange( @@ -125,9 +114,8 @@ string directory private readonly record struct BrowserLocation( AssetBrowserRoot root, string directory - ) { - internal static BrowserLocation AssetsRoot { get; } = new( - AssetBrowserRoot.Assets, - string.Empty); + ) + { + internal static BrowserLocation AssetsRoot { get; } = new(AssetBrowserRoot.Assets, string.Empty); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Filters.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Filters.cs index 6afa59665..d766dd373 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Filters.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Filters.cs @@ -1,42 +1,33 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - internal sealed partial class FileBrowserPanel - { - private void DrawEntryFilterCombo() { - if (!EditorWidget.BeginBoundedCombo( - "##AssetEntryFilter", - "Filter", - ImGuiComboFlags.WidthFitPreview)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##AssetEntryFilter", "Filter", ImGuiComboFlags.WidthFitPreview)) return; - DrawEntryTypeFilterOption("All", FileBrowserEntryTypeFilter.All); DrawEntryTypeFilterOption("Folders", FileBrowserEntryTypeFilter.FoldersOnly); DrawEntryTypeFilterOption("Files", FileBrowserEntryTypeFilter.FilesOnly); - NativeImGui.Separator(); + Ui.Separator(); DrawEntryScopeFilterOption("Current", FileBrowserEntryScopeFilter.CurrentOnly); DrawEntryScopeFilterOption("Recursive", FileBrowserEntryScopeFilter.Recursive); - - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } private void DrawEntryTypeFilterOption( @@ -44,7 +35,7 @@ private void DrawEntryTypeFilterOption( FileBrowserEntryTypeFilter filter ) { bool selected = m_entryTypeFilter == filter; - if (NativeImGui.Checkbox(label, ref selected)) + if (Ui.Checkbox(label, ref selected)) { if (selected) m_entryTypeFilter = filter; @@ -58,7 +49,7 @@ private void DrawEntryScopeFilterOption( FileBrowserEntryScopeFilter filter ) { bool selected = m_entryScopeFilter == filter; - if (NativeImGui.Checkbox(label, ref selected)) + if (Ui.Checkbox(label, ref selected)) { if (selected) m_entryScopeFilter = filter; @@ -69,11 +60,6 @@ FileBrowserEntryScopeFilter filter private void DrawSearchInput() { - _ = EditorWidget.SearchInput( - "AssetSearch", - ImGuiIcon.MagnifyingGlass, - ref m_filter, - (nuint)C_SEARCH_BUFFER_SIZE); + _ = EditorUI.current.widgets.SearchInput("AssetSearch", ImGuiIcon.MagnifyingGlass, ref m_filter, (nuint)C_SEARCH_BUFFER_SIZE); } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs index 1dfc5ddf1..c64592a05 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs @@ -1,61 +1,51 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed unsafe partial class FileBrowserPanel - +internal sealed partial class FileBrowserPanel { - private void DrawGridRegion( EditorContext context, IReadOnlyList entries ) { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float sliderHeight = NativeImGui.GetFrameHeight() + style->WindowPadding.Y * 2f + style->ItemSpacing.Y; - bool entriesVisible = NativeImGui.BeginChild( - "##EntriesScroll", - new Vector2(0f, -sliderHeight), - (int)ImGuiChildFlags.None); + EditorUiLayout style = UiDrawing.GetLayout(); + float sliderHeight = Ui.GetFrameHeight() + style.windowPadding.Y * 2f + style.itemSpacing.Y; + bool entriesVisible = Ui.BeginChild("##EntriesScroll", new Vector2(0f, -sliderHeight), ImGuiChildFlags.None); try { if (entriesVisible) { DrawGrid(context, entries); HandleBackgroundSelection(context); - m_contextMenu.DrawBackground( - context, - "##asset_grid_background_context", - FileBrowserPresentation.Grid); + m_contextMenu.DrawBackground(context, "##asset_grid_background_context", FileBrowserPresentation.Grid); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } + DrawGridScaleSlider(); } private void HandleBackgroundSelection(EditorContext context) { - if (!NativeImGui.IsWindowHovered() || - NativeImGui.IsAnyItemHovered() || - !NativeImGui.IsMouseReleased((int)ImGuiMouseButton.Left)) + if (!Ui.IsWindowHovered() || Ui.IsAnyItemHovered() || !Ui.IsMouseReleased(ImGuiMouseButton.Left)) { return; } @@ -65,17 +55,17 @@ private void HandleBackgroundSelection(EditorContext context) private void DrawGridScaleSlider() { - m_gridScale = Math.Clamp(m_gridScale, EditorWidget.style.assetGridMinimumScale, EditorWidget.style.assetGridMaximumScale); + m_gridScale = Math.Clamp(m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); DrawFooterTopSplitter(); - Vector2 cursor = NativeImGui.GetCursorPos(); - float labelOffsetY = MathF.Max(0f, (NativeImGui.GetFrameHeight() - NativeImGui.GetTextLineHeight()) * 0.5f); - NativeImGui.SetCursorPosY(cursor.Y + labelOffsetY); - NativeImGui.TextUnformatted("Scale"); - NativeImGui.SameLine(0f, -1f); - NativeImGui.SetCursorPosY(cursor.Y); - NativeImGui.SetNextItemWidth(-1f); - _ = NativeImGui.SliderFloat("##GridScale", ref m_gridScale, EditorWidget.style.assetGridMinimumScale, EditorWidget.style.assetGridMaximumScale, "%.1f"); - m_gridScale = Math.Clamp(m_gridScale, EditorWidget.style.assetGridMinimumScale, EditorWidget.style.assetGridMaximumScale); + Vector2 cursor = Ui.GetCursorPosition(); + float labelOffsetY = MathF.Max(0f, (Ui.GetFrameHeight() - Ui.GetTextLineHeight()) * 0.5f); + Ui.SetCursorPosY(cursor.Y + labelOffsetY); + Ui.Text("Scale"); + Ui.SameLine(0f, -1f); + Ui.SetCursorPosY(cursor.Y); + Ui.SetNextItemWidth(-1f); + _ = Ui.SliderFloat("##GridScale", ref m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); + m_gridScale = Math.Clamp(m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); } private void DrawGrid( @@ -83,25 +73,28 @@ private void DrawGrid( IReadOnlyList entries ) { float cellSize = GetGridCellSize(); - float available = MathF.Max(cellSize, NativeImGui.GetContentRegionAvail().X); + float available = MathF.Max(cellSize, Ui.GetContentRegionAvailable().X); int columns = Math.Max(1, (int)(available / cellSize)); - - bool tableStarted = NativeImGui.BeginTable("##FileBrowserGrid", columns, (int)(ImGuiTableFlags.SizingFixedFit | - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoSavedSettings), default(System.Numerics.Vector2), 0f); + bool tableStarted = Ui.BeginTable( + "##FileBrowserGrid", + columns, + (ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoSavedSettings), + default(System.Numerics.Vector2), + 0f + ); if (!tableStarted) return; try { for (int i = 0; i < entries.Count; i++) { - NativeImGui.TableNextColumn(); + Ui.TableNextColumn(); DrawGridItem(context, entries[i]); } } finally { - NativeImGui.EndTable(); + Ui.EndTable(); } } @@ -115,93 +108,55 @@ FileBrowserDisplayEntry item string name = item.isPluginRoot ? item.displayName : entry.name; bool selected = m_assets.browser.IsSelected(context, entry); bool editing = m_rename.IsEditing(context, entry.assetPath.ToString(), FileBrowserPresentation.Grid); - Vector2 itemSize = new(cellSize - EditorWidget.style.assetGridCellPadding, cellSize - EditorWidget.style.assetGridCellPadding); - - NativeImGui.PushID(entry.assetPath.ToString()); - NativeImGui.PushStyleColor((int)ImGuiCol.Header, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.HeaderHovered, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.HeaderActive, EditorPalette.transparent); + Vector2 itemSize = new(cellSize - EditorUI.current.widgets.style.assetGridCellPadding, cellSize - EditorUI.current.widgets.style.assetGridCellPadding); + Ui.PushId(entry.assetPath.ToString()); + Ui.PushStyleColor(ImGuiCol.Header, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.HeaderHovered, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.HeaderActive, EditorPalette.transparent); try { ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags.AllowDoubleClick; if (editing) selectableFlags |= ImGuiSelectableFlags.AllowOverlap; - bool activated = NativeImGui.Selectable( - "##GridItem", - selected, - (int)selectableFlags, - itemSize); - bool itemHovered = NativeImGui.IsItemHovered(); - bool itemActive = NativeImGui.IsItemActive(); - Vector2 itemMin = NativeImGui.GetItemRectMin(); - Vector2 itemMax = NativeImGui.GetItemRectMax(); - Vector2 layoutCursor = NativeImGui.GetCursorScreenPos(); - bool doubleClicked = itemHovered && - NativeImGui.IsMouseDoubleClicked((int)ImGuiMouseButton.Left); + bool activated = Ui.Selectable("##GridItem", selected, selectableFlags, itemSize); + bool itemHovered = Ui.IsItemHovered(); + bool itemActive = Ui.IsItemActive(); + Vector2 itemMin = Ui.GetItemMinimum(); + Vector2 itemMax = Ui.GetItemMaximum(); + Vector2 layoutCursor = Ui.GetCursorScreenPosition(); + bool doubleClicked = itemHovered && Ui.IsMouseDoubleClicked(ImGuiMouseButton.Left); if (activated) { - HandleEntryActivation( - context, - entry, - FileBrowserPresentation.Grid, - doubleClicked); + HandleEntryActivation(context, entry, FileBrowserPresentation.Grid, doubleClicked); } if (!editing) { m_dragDrop.DrawAssetSource(context, entry); - m_contextMenu.DrawEntry( - context, - "##asset_grid_context", - entry.assetPath.ToString(), - FileBrowserPresentation.Grid); + m_contextMenu.DrawEntry(context, "##asset_grid_context", entry.assetPath.ToString(), FileBrowserPresentation.Grid); if (entry.isDirectory && !entry.isReadOnly) m_dragDrop.DrawDirectoryTarget(context, entry.assetPath.ToString()); } - DrawGridItemVisual( - icon, - name, - selected, - m_gridScale, - itemHovered, - itemActive, - itemMin, - itemMax, - drawName: !editing); + + DrawGridItemVisual(icon, name, selected, m_gridScale, itemHovered, itemActive, itemMin, itemMax, drawName: !editing); if (editing) { - float width = MathF.Max( - 1f, - itemMax.X - itemMin.X - EditorWidget.style.assetGridLabelHorizontalPadding); - float lineHeight = NativeImGui.GetTextLineHeight(); - float lineAdvance = MathF.Max( - 1f, - lineHeight + EditorWidget.style.assetGridLabelLineSpacing); - float labelAreaHeight = lineHeight + - lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); - float labelAreaTop = itemMax.Y - - EditorWidget.style.assetGridLabelBottomPadding - - labelAreaHeight; - float y = labelAreaTop + - (labelAreaHeight - lineHeight) * 0.5f; - NativeImGui.SetCursorScreenPos(new Vector2( - itemMin.X + EditorWidget.style.assetGridLabelHorizontalPadding * 0.5f, - y)); - m_rename.Draw( - context, - $"grid_{entry.assetPath.ToString()}", - entry.assetPath.ToString(), - FileBrowserPresentation.Grid, - width, - lineHeight); - NativeImGui.SetCursorScreenPos(layoutCursor); - NativeImGui.Dummy(Vector2.Zero); + float width = MathF.Max(1f, itemMax.X - itemMin.X - EditorUI.current.widgets.style.assetGridLabelHorizontalPadding); + float lineHeight = Ui.GetTextLineHeight(); + float lineAdvance = MathF.Max(1f, lineHeight + EditorUI.current.widgets.style.assetGridLabelLineSpacing); + float labelAreaHeight = lineHeight + lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); + float labelAreaTop = itemMax.Y - EditorUI.current.widgets.style.assetGridLabelBottomPadding - labelAreaHeight; + float y = labelAreaTop + (labelAreaHeight - lineHeight) * 0.5f; + Ui.SetCursorScreenPosition(new Vector2(itemMin.X + EditorUI.current.widgets.style.assetGridLabelHorizontalPadding * 0.5f, y)); + m_rename.Draw(context, $"grid_{entry.assetPath.ToString()}", entry.assetPath.ToString(), FileBrowserPresentation.Grid, width, lineHeight); + Ui.SetCursorScreenPosition(layoutCursor); + Ui.Dummy(Vector2.Zero); } } finally { - NativeImGui.PopStyleColor(3); - NativeImGui.PopID(); + Ui.PopStyleColor(3); + Ui.PopId(); } } @@ -217,100 +172,57 @@ private static void DrawGridItemVisual( bool drawName ) { Vector2 size = max - min; - Vector4 bg = selected ? EditorPalette.assetAccent : EditorPalette.collectionHeader; if (active) bg = EditorPalette.GetActive(bg); else if (hovered) bg = EditorPalette.GetHovered(bg); - - uint bgColor = NativeImGui.ColorConvertFloat4ToU32(bg); - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - EditorImGui.DrawFilledRectangle( - min, - max, - bgColor, - EditorWidget.style.assetFrameRounding - ); - - ImFont* font = NativeImGui.Raw.GetFont(); - float fontSize = NativeImGui.GetFontSize(); + uint bgColor = Ui.ToPackedColor(bg); + EditorImGui.DrawFilledRectangle(min, max, bgColor, EditorUI.current.widgets.style.assetFrameRounding); + float fontSize = Ui.GetFontSize(); float iconFontSize = fontSize * scale; - Vector4 iconBounds = EditorWidget.GetGlyphVisualBounds(font, iconFontSize, icon); - Vector2 iconSize = new( - iconBounds.Z - iconBounds.X, - iconBounds.W - iconBounds.Y); - string[] nameLines = FitTextToLines( - name, - MathF.Max(1f, size.X - EditorWidget.style.assetGridLabelHorizontalPadding), - C_GRID_LABEL_LINE_COUNT); - float lineHeight = NativeImGui.GetTextLineHeight(); - float lineAdvance = MathF.Max( - 1f, - lineHeight + EditorWidget.style.assetGridLabelLineSpacing); - float labelAreaHeight = lineHeight + - lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); - float labelAreaTop = max.Y - - EditorWidget.style.assetGridLabelBottomPadding - - labelAreaHeight; - float labelHeight = lineHeight + - lineAdvance * Math.Max(0, nameLines.Length - 1); + Vector4 iconBounds = EditorUI.current.widgets.GetGlyphVisualBounds(iconFontSize, icon); + Vector2 iconSize = new(iconBounds.Z - iconBounds.X, iconBounds.W - iconBounds.Y); + string[] nameLines = FitTextToLines(name, MathF.Max(1f, size.X - EditorUI.current.widgets.style.assetGridLabelHorizontalPadding), C_GRID_LABEL_LINE_COUNT); + float lineHeight = Ui.GetTextLineHeight(); + float lineAdvance = MathF.Max(1f, lineHeight + EditorUI.current.widgets.style.assetGridLabelLineSpacing); + float labelAreaHeight = lineHeight + lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); + float labelAreaTop = max.Y - EditorUI.current.widgets.style.assetGridLabelBottomPadding - labelAreaHeight; + float labelHeight = lineHeight + lineAdvance * Math.Max(0, nameLines.Length - 1); float labelY = labelAreaTop + (labelAreaHeight - labelHeight) * 0.5f; - float iconAreaTop = min.Y + EditorWidget.style.assetGridIconTopPadding; - float iconAreaBottom = MathF.Max( - iconAreaTop + 1f, - labelAreaTop - EditorWidget.style.assetGridIconLabelSpacing); - float maximumIconWidth = MathF.Max( - 1f, - size.X - EditorWidget.style.assetGridIconHorizontalPadding * 2f); + float iconAreaTop = min.Y + EditorUI.current.widgets.style.assetGridIconTopPadding; + float iconAreaBottom = MathF.Max(iconAreaTop + 1f, labelAreaTop - EditorUI.current.widgets.style.assetGridIconLabelSpacing); + float maximumIconWidth = MathF.Max(1f, size.X - EditorUI.current.widgets.style.assetGridIconHorizontalPadding * 2f); float maximumIconHeight = MathF.Max(1f, iconAreaBottom - iconAreaTop); - float fit = MathF.Min( - 1f, - MathF.Min( - maximumIconWidth / MathF.Max(1f, iconSize.X), - maximumIconHeight / MathF.Max(1f, iconSize.Y))); + float fit = MathF.Min(1f, MathF.Min(maximumIconWidth / MathF.Max(1f, iconSize.X), maximumIconHeight / MathF.Max(1f, iconSize.Y))); if (fit < 1f) { iconFontSize *= fit; - iconBounds = EditorWidget.GetGlyphVisualBounds(font, iconFontSize, icon); - iconSize = new( - iconBounds.Z - iconBounds.X, - iconBounds.W - iconBounds.Y); + iconBounds = EditorUI.current.widgets.GetGlyphVisualBounds(iconFontSize, icon); + iconSize = new(iconBounds.Z - iconBounds.X, iconBounds.W - iconBounds.Y); } float horizontalCenter = min.X + size.X * 0.5f; - Vector2 iconAreaCenter = new( - horizontalCenter + - C_GRID_ICON_HORIZONTAL_OPTICAL_OFFSET * EditorWidget.style.zoom, - iconAreaTop + maximumIconHeight * 0.5f); - - uint textColor = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.assetText); - NativeImGui.PushClipRect(min, max, true); - EditorWidget.AddGlyphCentered( - drawList, - font, - iconFontSize, - icon, - iconAreaCenter, - textColor); + Vector2 iconAreaCenter = new(horizontalCenter + C_GRID_ICON_HORIZONTAL_OPTICAL_OFFSET * EditorUI.current.widgets.style.zoom, iconAreaTop + maximumIconHeight * 0.5f); + uint textColor = Ui.ToPackedColor(EditorPalette.assetText); + Ui.PushClipRect(min, max, true); + EditorUI.current.widgets.AddGlyphCentered(iconFontSize, icon, iconAreaCenter, textColor); for (int i = 0; drawName && i < nameLines.Length; i++) { - Vector2 lineSize = NativeImGui.CalcTextSize(nameLines[i]); - Vector2 linePosition = new( - horizontalCenter - lineSize.X * 0.5f, - labelY + lineAdvance * i); + Vector2 lineSize = Ui.MeasureText(nameLines[i]); + Vector2 linePosition = new(horizontalCenter - lineSize.X * 0.5f, labelY + lineAdvance * i); EditorImGui.DrawText(linePosition, textColor, nameLines[i]); } - NativeImGui.PopClipRect(); + + Ui.PopClipRect(); } private float GetGridCellSize() { - float fontSize = NativeImGui.GetFontSize(); + float fontSize = Ui.GetFontSize(); return MathF.Max( - EditorWidget.style.assetGridMinimumCellSize, - fontSize * (m_gridScale + EditorWidget.style.assetGridScaleBias) + - EditorWidget.style.assetGridFixedCellPadding); + EditorUI.current.widgets.style.assetGridMinimumCellSize, + fontSize * (m_gridScale + EditorUI.current.widgets.style.assetGridScaleBias) + EditorUI.current.widgets.style.assetGridFixedCellPadding + ); } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs index b06a49b38..56c9ec60f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs @@ -1,104 +1,82 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed unsafe partial class FileBrowserPanel - +internal sealed partial class FileBrowserPanel { - private void DrawBrowser(EditorContext context) { - m_navigation.SyncExternalDirectoryChange( - m_assets.browser.root, - m_assets.browser.currentDirectory); - - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float breadcrumbBarHeight = GetBreadcrumbBarHeight( - m_assets.browser.root, - m_assets.browser.currentDirectory); - Vector2 bodySize = new(0f, -(breadcrumbBarHeight + style->ItemSpacing.Y)); - bool mainVisible = NativeImGui.BeginChild( - "##FileBrowserMain", - bodySize, - (int)ImGuiChildFlags.None, - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); + m_navigation.SyncExternalDirectoryChange(m_assets.browser.root, m_assets.browser.currentDirectory); + EditorUiLayout style = UiDrawing.GetLayout(); + float breadcrumbBarHeight = GetBreadcrumbBarHeight(m_assets.browser.root, m_assets.browser.currentDirectory); + Vector2 bodySize = new(0f, -(breadcrumbBarHeight + style.itemSpacing.Y)); + bool mainVisible = Ui.BeginChild("##FileBrowserMain", bodySize, ImGuiChildFlags.None, (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); try { if (mainVisible) { - ImGuiTableFlags splitFlags = - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoKeepColumnsVisible | - ImGuiTableFlags.SizingFixedFit | - ImGuiTableFlags.NoSavedSettings; - + ImGuiTableFlags splitFlags = ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoKeepColumnsVisible | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings; float splitterWidth = GetTreeSplitterWidth(style); - float availableWidth = MathF.Max(0f, NativeImGui.GetContentRegionAvail().X); - float treeWidth = ResolveTreeWidth( - m_treePaneRatio, - availableWidth, - splitterWidth); - NativeImGui.PushStyleVar((int)ImGuiStyleVar.CellPadding, Vector2.Zero); + float availableWidth = MathF.Max(0f, Ui.GetContentRegionAvailable().X); + float treeWidth = ResolveTreeWidth(m_treePaneRatio, availableWidth, splitterWidth); + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, Vector2.Zero); try { - bool splitStarted = NativeImGui.BeginTable("##FileBrowserSplit", 3, (int)splitFlags, default(System.Numerics.Vector2), 0f); + bool splitStarted = Ui.BeginTable("##FileBrowserSplit", 3, splitFlags, default(System.Numerics.Vector2), 0f); try { if (splitStarted) { - NativeImGui.TableSetupColumn("##Tree", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), treeWidth); - NativeImGui.TableSetupColumn("##Splitter", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), splitterWidth); - NativeImGui.TableSetupColumn("##Content", (int)ImGuiTableColumnFlags.WidthStretch); - - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); + Ui.TableSetupColumn("##Tree", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), treeWidth); + Ui.TableSetupColumn("##Splitter", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), splitterWidth); + Ui.TableSetupColumn("##Content", ImGuiTableColumnFlags.WidthStretch); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); DrawTreePane(context); - - _ = NativeImGui.TableSetColumnIndex(1); + _ = Ui.TableSetColumnIndex(1); DrawTreeSplitter(splitterWidth, availableWidth, treeWidth); - - _ = NativeImGui.TableSetColumnIndex(2); + _ = Ui.TableSetColumnIndex(2); DrawContentPane(context); } } finally { if (splitStarted) - NativeImGui.EndTable(); + Ui.EndTable(); } } finally { - NativeImGui.PopStyleVar(); + Ui.PopStyleVar(); } } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } + DrawBreadcrumbBar(context, breadcrumbBarHeight); } - private static float GetTreeSplitterWidth(ImGuiStyle* style) + private static float GetTreeSplitterWidth(EditorUiLayout style) { - return MathF.Max(EditorWidget.style.assetSplitterMinimumWidth, style->DockingSeparatorSize); + return MathF.Max(EditorUI.current.widgets.style.assetSplitterMinimumWidth, style.dockingSeparatorSize); } private void DrawTreeSplitter( @@ -106,40 +84,27 @@ private void DrawTreeSplitter( float availableWidth, float treeWidth ) { - Vector2 size = new(width, MathF.Max(1f, NativeImGui.GetContentRegionAvail().Y)); - _ = NativeImGui.InvisibleButton("##TreeSplitterGrip", size); - - bool hovered = NativeImGui.IsItemHovered(); - bool active = NativeImGui.IsItemActive(); + Vector2 size = new(width, MathF.Max(1f, Ui.GetContentRegionAvailable().Y)); + _ = Ui.InvisibleButton("##TreeSplitterGrip", size); + bool hovered = Ui.IsItemHovered(); + bool active = Ui.IsItemActive(); if (hovered || active) - NativeImGui.SetMouseCursor((int)ImGuiMouseCursor.ResizeEw); - + Ui.SetMouseCursor(ImGuiMouseCursor.ResizeEw); if (active) { - Vector2 delta = NativeImGui.GetMouseDragDelta((int)ImGuiMouseButton.Left); + Vector2 delta = Ui.GetMouseDragDelta(ImGuiMouseButton.Left); if (MathF.Abs(delta.X) > 0f) { - float requestedWidth = ClampTreeWidth( - treeWidth + delta.X, - availableWidth, - width); - m_treePaneRatio = CalculateTreePaneRatio( - requestedWidth, - availableWidth, - width); - NativeImGui.ResetMouseDragDelta((int)ImGuiMouseButton.Left); + float requestedWidth = ClampTreeWidth(treeWidth + delta.X, availableWidth, width); + m_treePaneRatio = CalculateTreePaneRatio(requestedWidth, availableWidth, width); + Ui.ResetMouseDragDelta(ImGuiMouseButton.Left); } } - Vector2 min = NativeImGui.GetItemRectMin(); - Vector2 max = NativeImGui.GetItemRectMax(); + Vector2 min = Ui.GetItemMinimum(); + Vector2 max = Ui.GetItemMaximum(); Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; - EditorImGui.DrawFilledRectangle( - min, - max, - NativeImGui.ColorConvertFloat4ToU32(color), - 0f - ); + EditorImGui.DrawFilledRectangle(min, max, Ui.ToPackedColor(color), 0f); } private static float ClampTreeWidth( @@ -148,9 +113,7 @@ private static float ClampTreeWidth( float splitterWidth ) { float combinedPaneWidth = MathF.Max(0f, availableWidth - splitterWidth); - float minimumPaneWidth = MathF.Min( - EditorWidget.style.assetPaneMinimumVisibleWidth, - combinedPaneWidth * 0.5f); + float minimumPaneWidth = MathF.Min(EditorUI.current.widgets.style.assetPaneMinimumVisibleWidth, combinedPaneWidth * 0.5f); float maximumTreeWidth = MathF.Max(minimumPaneWidth, combinedPaneWidth - minimumPaneWidth); return Math.Clamp(requestedWidth, minimumPaneWidth, maximumTreeWidth); } @@ -161,10 +124,7 @@ private static float ResolveTreeWidth( float splitterWidth ) { float combinedPaneWidth = MathF.Max(0f, availableWidth - splitterWidth); - return ClampTreeWidth( - combinedPaneWidth * Math.Clamp(treePaneRatio, 0f, 1f), - availableWidth, - splitterWidth); + return ClampTreeWidth(combinedPaneWidth * Math.Clamp(treePaneRatio, 0f, 1f), availableWidth, splitterWidth); } private static float CalculateTreePaneRatio( @@ -173,35 +133,23 @@ private static float CalculateTreePaneRatio( float splitterWidth ) { float combinedPaneWidth = MathF.Max(0f, availableWidth - splitterWidth); - return combinedPaneWidth > float.Epsilon - ? Math.Clamp(treeWidth / combinedPaneWidth, 0f, 1f) - : 0.5f; + return combinedPaneWidth > float.Epsilon ? Math.Clamp(treeWidth / combinedPaneWidth, 0f, 1f) : 0.5f; } private void DrawTreePane(EditorContext context) { - NativeImGui.PushStyleColor((int)ImGuiCol.ChildBg, EditorPalette.collectionHeader); + Ui.PushStyleColor(ImGuiCol.ChildBg, EditorPalette.collectionHeader); try { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - Vector2 treePaneSize = new(-style->WindowPadding.X, 0f); - bool treeVisible = NativeImGui.BeginChild( - "##TreePane", - treePaneSize, - (int)ImGuiChildFlags.None, - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); + EditorUiLayout style = UiDrawing.GetLayout(); + Vector2 treePaneSize = new(-style.windowPadding.X, 0f); + bool treeVisible = Ui.BeginChild("##TreePane", treePaneSize, ImGuiChildFlags.None, (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); try { if (treeVisible) { - float footerHeight = NativeImGui.GetFrameHeight() + - style->WindowPadding.Y * 2f + - style->ItemSpacing.Y; - bool treeScrollVisible = NativeImGui.BeginChild( - "##TreeScroll", - new Vector2(0f, -footerHeight), - (int)ImGuiChildFlags.None, - (int)ImGuiWindowFlags.HorizontalScrollbar); + float footerHeight = Ui.GetFrameHeight() + style.windowPadding.Y * 2f + style.itemSpacing.Y; + bool treeScrollVisible = Ui.BeginChild("##TreeScroll", new Vector2(0f, -footerHeight), ImGuiChildFlags.None, ImGuiWindowFlags.HorizontalScrollbar); try { if (treeScrollVisible) @@ -212,49 +160,47 @@ private void DrawTreePane(EditorContext context) if (m_assets.browser.root == AssetBrowserRoot.Assets) m_tree.DrawEntry(context, string.Empty, "Assets", true); else - m_tree.DrawPluginRoot(context, m_assets.pipeline.sourceMounts); + m_tree.DrawPluginRoot(context, m_assets.edits.pipeline.sourceMounts); } finally { m_tree.ClearOpenRequests(); } + HandleBackgroundSelection(context); - m_contextMenu.DrawBackground( - context, - "##asset_tree_background_context", - FileBrowserPresentation.Tree); + m_contextMenu.DrawBackground(context, "##asset_tree_background_context", FileBrowserPresentation.Tree); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } + DrawRootSwitchFooter(context); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } - if (!IsReadOnlyLocation(m_assets.pipeline, m_assets.browser)) + + if (!IsReadOnlyLocation(m_assets.edits.pipeline, m_assets.browser)) m_dragDrop.DrawDirectoryTarget(context, m_assets.browser.currentDirectory); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } private void DrawRootSwitchFooter(EditorContext context) { - AssetBrowserRoot nextRoot = m_assets.browser.root == AssetBrowserRoot.Assets - ? AssetBrowserRoot.Plugins - : AssetBrowserRoot.Assets; + AssetBrowserRoot nextRoot = m_assets.browser.root == AssetBrowserRoot.Assets ? AssetBrowserRoot.Plugins : AssetBrowserRoot.Assets; DrawFooterTopSplitter(); PushButtonColors(EditorPalette.assetAccent); try { - if (NativeImGui.Button($"Switch to {nextRoot}##FileBrowserRoot", new Vector2(-1f, 0f))) + if (Ui.Button($"Switch to {nextRoot}##FileBrowserRoot", new Vector2(-1f, 0f))) { m_tree.RequestOpenRoot(); m_navigation.SwitchRoot(context, nextRoot); @@ -262,40 +208,35 @@ private void DrawRootSwitchFooter(EditorContext context) } finally { - NativeImGui.PopStyleColor(3); + Ui.PopStyleColor(3); } } private void DrawContentPane(EditorContext context) { - NativeImGui.PushStyleColor((int)ImGuiCol.ChildBg, EditorPalette.collectionHeader); + Ui.PushStyleColor(ImGuiCol.ChildBg, EditorPalette.collectionHeader); try { - bool contentVisible = NativeImGui.BeginChild( - "##ContentPane", - Vector2.Zero, - (int)ImGuiChildFlags.None, - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); + bool contentVisible = Ui.BeginChild("##ContentPane", Vector2.Zero, ImGuiChildFlags.None, (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); try { if (contentVisible) { DrawToolbar(context); - DrawEntriesRegion( - context, - m_data.CollectVisibleEntries(context, m_entryTypeFilter, m_entryScopeFilter, m_filter)); + DrawEntriesRegion(context, m_data.CollectVisibleEntries(context, m_entryTypeFilter, m_entryScopeFilter, m_filter)); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } - if (!IsReadOnlyLocation(m_assets.pipeline, m_assets.browser)) + + if (!IsReadOnlyLocation(m_assets.edits.pipeline, m_assets.browser)) m_dragDrop.DrawDirectoryTarget(context, m_assets.browser.currentDirectory); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } @@ -303,42 +244,36 @@ private void DrawEntriesRegion( EditorContext context, IReadOnlyList entries ) { - if (m_viewMode == ViewMode.List) + m_visibleSelection = entries.Select(value => (Inno.Core.Identity.IdentityObject)value.entry).ToArray(); + try { - DrawListRegion(context, entries); - return; + if (m_viewMode == ViewMode.List) + DrawListRegion(context, entries); + else + DrawGridRegion(context, entries); + } + finally + { + m_visibleSelection = Array.Empty(); } - - DrawGridRegion(context, entries); } private static void DrawFooterTopSplitter() { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - Vector2 cursor = NativeImGui.GetCursorScreenPos(); - float width = NativeImGui.GetContentRegionAvail().X; - float lineY = cursor.Y + style->WindowPadding.Y; - uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.assetBorder); - EditorImGui.DrawLine( - new Vector2(cursor.X, lineY), - new Vector2(cursor.X + width, lineY), - color, - EditorWidget.style.borderSize - ); - NativeImGui.SetCursorPosY(NativeImGui.GetCursorPosY() + style->WindowPadding.Y * 2f); + EditorUiLayout style = UiDrawing.GetLayout(); + Vector2 cursor = Ui.GetCursorScreenPosition(); + float width = Ui.GetContentRegionAvailable().X; + float lineY = cursor.Y + style.windowPadding.Y; + uint color = Ui.ToPackedColor(EditorPalette.assetBorder); + EditorImGui.DrawLine(new Vector2(cursor.X, lineY), new Vector2(cursor.X + width, lineY), color, EditorUI.current.widgets.style.borderSize); + Ui.SetCursorPosY(Ui.GetCursorPosY() + style.windowPadding.Y * 2f); } private static void DrawBreadcrumbTopSeparator() { - Vector2 min = NativeImGui.GetWindowPos(); - Vector2 size = NativeImGui.GetWindowSize(); - uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.assetBorder); - EditorImGui.DrawLine( - min, - new Vector2(min.X + size.X, min.Y), - color, - EditorWidget.style.borderSize - ); + Vector2 min = Ui.GetWindowPos(); + Vector2 size = Ui.GetWindowSize(); + uint color = Ui.ToPackedColor(EditorPalette.assetBorder); + EditorImGui.DrawLine(min, new Vector2(min.X + size.X, min.Y), color, EditorUI.current.widgets.style.borderSize); } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs index b7e3af8d7..7db4d824c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs @@ -1,50 +1,41 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed unsafe partial class FileBrowserPanel - +internal sealed partial class FileBrowserPanel { - private void DrawListRegion( EditorContext context, IReadOnlyList entries ) { - bool entriesVisible = NativeImGui.BeginChild( - "##EntriesScroll", - Vector2.Zero, - (int)ImGuiChildFlags.None); + bool entriesVisible = Ui.BeginChild("##EntriesScroll", Vector2.Zero, ImGuiChildFlags.None); try { if (entriesVisible) { DrawEntriesTable(context, entries, m_assets.browser.currentDirectory); HandleBackgroundSelection(context); - m_contextMenu.DrawBackground( - context, - "##asset_list_background_context", - FileBrowserPresentation.List); + m_contextMenu.DrawBackground(context, "##asset_list_background_context", FileBrowserPresentation.List); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } @@ -53,85 +44,60 @@ private void DrawEntriesTable( IReadOnlyList entries, string currentDirectory ) { - ImGuiTableFlags flags = - ImGuiTableFlags.RowBg | - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoSavedSettings; - - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - Vector2 tableOrigin = NativeImGui.GetCursorScreenPos(); - Vector2 tableSize = NativeImGui.GetContentRegionAvail(); - NativeImGui.PushStyleVar((int)ImGuiStyleVar.CellPadding, new Vector2(style->WindowPadding.X, style->CellPadding.Y)); + ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings; + EditorUiLayout style = UiDrawing.GetLayout(); + Vector2 tableOrigin = Ui.GetCursorScreenPosition(); + Vector2 tableSize = Ui.GetContentRegionAvailable(); + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(style.windowPadding.X, style.cellPadding.Y)); try { - bool tableStarted = NativeImGui.BeginTable( - "##FileBrowserEntries", - 3, - (int)flags, - new Vector2(0f, 0f)); + bool tableStarted = Ui.BeginTable("##FileBrowserEntries", 3, flags, new Vector2(0f, 0f)); if (!tableStarted) { - NativeImGui.Dummy(Vector2.Zero); + Ui.Dummy(Vector2.Zero); return; } + try { - NativeImGui.TableSetupColumn( - "Name", - (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), - m_listNameSeparatorPosition); - NativeImGui.TableSetupColumn( - "Type", - (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), - m_listTypeSeparatorPosition - m_listNameSeparatorPosition); - NativeImGui.TableSetupColumn( - "Source", - (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), - 1f - m_listTypeSeparatorPosition); + Ui.TableSetupColumn("Name", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), m_listNameSeparatorPosition); + Ui.TableSetupColumn("Type", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), m_listTypeSeparatorPosition - m_listNameSeparatorPosition); + Ui.TableSetupColumn("Source", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), 1f - m_listTypeSeparatorPosition); DrawHeaderRow(); - - uint rowBg = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionRow); - uint rowAltBg = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionRowAlternate); - float entryRowHeight = NativeImGui.GetTextLineHeight() + style->CellPadding.Y * 2f; + uint rowBg = Ui.ToPackedColor(EditorPalette.collectionRow); + uint rowAltBg = Ui.ToPackedColor(EditorPalette.collectionRowAlternate); + float entryRowHeight = Ui.GetTextLineHeight() + style.cellPadding.Y * 2f; for (int i = 0; i < entries.Count; i++) { FileBrowserDisplayEntry item = entries[i]; AssetFileEntry entry = item.entry; if (i > 0) - NativeImGui.TableNextRow((int)ImGuiTableRowFlags.None, EditorWidget.style.assetListRowSpacing); - NativeImGui.TableNextRow((int)ImGuiTableRowFlags.None, entryRowHeight); - NativeImGui.TableSetBgColor( - (int)ImGuiTableBgTarget.RowBg0, - i % 2 == 0 ? rowBg : rowAltBg); - + Ui.TableNextRow(ImGuiTableRowFlags.None, EditorUI.current.widgets.style.assetListRowSpacing); + Ui.TableNextRow(ImGuiTableRowFlags.None, entryRowHeight); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, i % 2 == 0 ? rowBg : rowAltBg); DrawNameCell(context, item); DrawTextCell(item.isPluginRoot ? "IPLUGIN" : GetTypeText(entry), EditorPalette.assetText); - DrawTextCell( - item.isPluginRoot - ? "~" - : GetSourceText(entry, currentDirectory), - EditorPalette.assetText); + DrawTextCell(item.isPluginRoot ? "~" : GetSourceText(entry, currentDirectory), EditorPalette.assetText); } } finally { - NativeImGui.EndTable(); + Ui.EndTable(); } - Vector2 tableEnd = NativeImGui.GetCursorScreenPos(); + Vector2 tableEnd = Ui.GetCursorScreenPosition(); float tableHeight = MathF.Max(1f, tableEnd.Y - tableOrigin.Y); var interactionSize = new Vector2(tableSize.X, tableHeight); ListColumnSeparatorState separators = HandleListColumnSeparators(tableOrigin, interactionSize); DrawListColumnSeparators(tableOrigin, tableSize.X, tableHeight, separators); - NativeImGui.SetCursorScreenPos(tableEnd); + Ui.SetCursorScreenPosition(tableEnd); // Commit the restored layout cursor so overlay separator hit targets cannot implicitly // extend the scrolling child when UI scaling changes their physical bounds. - NativeImGui.Dummy(Vector2.Zero); + Ui.Dummy(Vector2.Zero); } finally { - NativeImGui.PopStyleVar(); + Ui.PopStyleVar(); } } @@ -141,39 +107,36 @@ Vector2 size ) { float width = MathF.Max(1f, size.X); float height = MathF.Max(1f, size.Y); - float hitWidth = EditorWidget.style.assetListSeparatorHitWidth; + float hitWidth = EditorUI.current.widgets.style.assetListSeparatorHitWidth; bool nameHovered; bool nameActive; bool typeHovered; bool typeActive; - float nameX = origin.X + width * m_listNameSeparatorPosition; - NativeImGui.SetCursorScreenPos(new Vector2(nameX - hitWidth * 0.5f, origin.Y)); - NativeImGui.SetNextItemAllowOverlap(); - _ = NativeImGui.InvisibleButton("##AssetListNameSeparator", new Vector2(hitWidth, height)); - nameHovered = NativeImGui.IsItemHovered(); - nameActive = NativeImGui.IsItemActive(); + Ui.SetCursorScreenPosition(new Vector2(nameX - hitWidth * 0.5f, origin.Y)); + Ui.SetNextItemAllowOverlap(); + _ = Ui.InvisibleButton("##AssetListNameSeparator", new Vector2(hitWidth, height)); + nameHovered = Ui.IsItemHovered(); + nameActive = Ui.IsItemActive(); if (nameHovered || nameActive) - NativeImGui.SetMouseCursor((int)ImGuiMouseCursor.ResizeEw); - + Ui.SetMouseCursor(ImGuiMouseCursor.ResizeEw); if (nameActive) { - float requested = (NativeImGui.GetMousePos().X - origin.X) / width; + float requested = (Ui.GetMousePos().X - origin.X) / width; SetListColumnSeparators(requested, m_listTypeSeparatorPosition); } float typeX = origin.X + width * m_listTypeSeparatorPosition; - NativeImGui.SetCursorScreenPos(new Vector2(typeX - hitWidth * 0.5f, origin.Y)); - NativeImGui.SetNextItemAllowOverlap(); - _ = NativeImGui.InvisibleButton("##AssetListTypeSeparator", new Vector2(hitWidth, height)); - typeHovered = NativeImGui.IsItemHovered(); - typeActive = NativeImGui.IsItemActive(); + Ui.SetCursorScreenPosition(new Vector2(typeX - hitWidth * 0.5f, origin.Y)); + Ui.SetNextItemAllowOverlap(); + _ = Ui.InvisibleButton("##AssetListTypeSeparator", new Vector2(hitWidth, height)); + typeHovered = Ui.IsItemHovered(); + typeActive = Ui.IsItemActive(); if (typeHovered || typeActive) - NativeImGui.SetMouseCursor((int)ImGuiMouseCursor.ResizeEw); - + Ui.SetMouseCursor(ImGuiMouseCursor.ResizeEw); if (typeActive) { - float requested = (NativeImGui.GetMousePos().X - origin.X) / width; + float requested = (Ui.GetMousePos().X - origin.X) / width; SetListColumnSeparators(m_listNameSeparatorPosition, requested); } @@ -187,18 +150,8 @@ private void DrawListColumnSeparators( ListColumnSeparatorState state ) { float bottom = origin.Y + MathF.Max(1f, height); - DrawListColumnSeparator( - origin.X + width * m_listNameSeparatorPosition, - origin.Y, - bottom, - state.nameHovered, - state.nameActive); - DrawListColumnSeparator( - origin.X + width * m_listTypeSeparatorPosition, - origin.Y, - bottom, - state.typeHovered, - state.typeActive); + DrawListColumnSeparator(origin.X + width * m_listNameSeparatorPosition, origin.Y, bottom, state.nameHovered, state.nameActive); + DrawListColumnSeparator(origin.X + width * m_listTypeSeparatorPosition, origin.Y, bottom, state.typeHovered, state.typeActive); } private static void DrawListColumnSeparator( @@ -208,52 +161,33 @@ private static void DrawListColumnSeparator( bool hovered, bool active ) { - Vector4 color = active - ? EditorPalette.assetAccent - : hovered - ? EditorPalette.assetBorder - : EditorPalette.assetBorderSoft; - EditorImGui.DrawLine( - new Vector2(x, top), - new Vector2(x, bottom), - NativeImGui.ColorConvertFloat4ToU32(color), - EditorWidget.style.borderSize - ); + Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; + EditorImGui.DrawLine(new Vector2(x, top), new Vector2(x, bottom), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); } private void SetListColumnSeparators( float namePosition, float typePosition ) { - float minimum = EditorWidget.style.assetListMinimumColumnRatio; - m_listNameSeparatorPosition = Math.Clamp( - namePosition, - minimum, - 1f - minimum * 2f); - m_listTypeSeparatorPosition = Math.Clamp( - typePosition, - m_listNameSeparatorPosition + minimum, - 1f - minimum); + float minimum = m_ui.widgets.style.assetListMinimumColumnRatio; + m_listNameSeparatorPosition = Math.Clamp(namePosition, minimum, 1f - minimum * 2f); + m_listTypeSeparatorPosition = Math.Clamp(typePosition, m_listNameSeparatorPosition + minimum, 1f - minimum); } private static void DrawHeaderRow() { - NativeImGui.TableNextRow(); - NativeImGui.TableSetBgColor( - (int)ImGuiTableBgTarget.RowBg0, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionHeader)); - NativeImGui.TableSetBgColor( - (int)ImGuiTableBgTarget.RowBg1, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionHeader)); - _ = NativeImGui.TableSetColumnIndex(0); + Ui.TableNextRow(); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, Ui.ToPackedColor(EditorPalette.collectionHeader)); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, Ui.ToPackedColor(EditorPalette.collectionHeader)); + _ = Ui.TableSetColumnIndex(0); InsetListCellContent(); - NativeImGui.TextUnformatted("Name"); - _ = NativeImGui.TableSetColumnIndex(1); + Ui.Text("Name"); + _ = Ui.TableSetColumnIndex(1); InsetListCellContent(); - NativeImGui.TextUnformatted("Type"); - _ = NativeImGui.TableSetColumnIndex(2); + Ui.Text("Type"); + _ = Ui.TableSetColumnIndex(2); InsetListCellContent(); - NativeImGui.TextUnformatted("Source"); + Ui.Text("Source"); } private void DrawNameCell( @@ -261,61 +195,42 @@ private void DrawNameCell( FileBrowserDisplayEntry item ) { AssetFileEntry entry = item.entry; - _ = NativeImGui.TableSetColumnIndex(0); + _ = Ui.TableSetColumnIndex(0); string icon = m_assets.GetIcon(entry); - string name = item.isPluginRoot - ? item.displayName - : entry.nameWithoutExtension; + string name = item.isPluginRoot ? item.displayName : entry.nameWithoutExtension; bool selected = m_assets.browser.IsSelected(context, entry); bool editing = m_rename.IsEditing(context, entry.assetPath.ToString(), FileBrowserPresentation.List); - ImGuiTable* table = NativeImGui.Raw.GetCurrentTable(); - float rowMinimumY = table->RowPosY1; - float rowMaximumY = table->RowPosY2; - + var table = UiDrawing.GetTableBounds(); + float rowMinimumY = table.rowMinimum; + float rowMaximumY = table.rowMaximum; InsetListCellContent(); - NativeImGui.PushStyleColor((int)ImGuiCol.Header, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.HeaderHovered, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.HeaderActive, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.Header, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.HeaderHovered, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.HeaderActive, EditorPalette.transparent); try { - Vector2 iconTextPos = NativeImGui.GetCursorScreenPos(); - ImGuiSelectableFlags selectableFlags = - ImGuiSelectableFlags.SpanAllColumns | - ImGuiSelectableFlags.AllowDoubleClick | - ImGuiSelectableFlags.AllowOverlap; - bool activated = NativeImGui.Selectable( - $"##entry_{entry.assetPath.ToString()}", - selected, - (int)selectableFlags); - bool itemHovered = NativeImGui.IsItemHovered(); - bool doubleClicked = itemHovered && NativeImGui.IsMouseDoubleClicked((int)ImGuiMouseButton.Left); + Vector2 iconTextPos = Ui.GetCursorScreenPosition(); + ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags.SpanAllColumns | ImGuiSelectableFlags.AllowDoubleClick | ImGuiSelectableFlags.AllowOverlap; + bool activated = Ui.Selectable($"##entry_{entry.assetPath.ToString()}", selected, selectableFlags); + bool itemHovered = Ui.IsItemHovered(); + bool doubleClicked = itemHovered && Ui.IsMouseDoubleClicked(ImGuiMouseButton.Left); if (activated) { - HandleEntryActivation( - context, - entry, - FileBrowserPresentation.List, - doubleClicked); + HandleEntryActivation(context, entry, FileBrowserPresentation.List, doubleClicked); } - bool itemActive = NativeImGui.IsItemActive(); + bool itemActive = Ui.IsItemActive(); if (!editing) { - m_contextMenu.DrawEntry( - context, - $"##asset_context_{entry.assetPath.ToString()}", - entry.assetPath.ToString(), - FileBrowserPresentation.List); + m_contextMenu.DrawEntry(context, $"##asset_context_{entry.assetPath.ToString()}", entry.assetPath.ToString(), FileBrowserPresentation.List); } + if (selected || itemHovered) { - Vector4 highlight = itemActive - ? EditorPalette.GetHovered(EditorPalette.assetAccent) - : EditorPalette.assetAccent; - NativeImGui.TableSetBgColor( - (int)ImGuiTableBgTarget.RowBg1, - NativeImGui.ColorConvertFloat4ToU32(highlight)); + Vector4 highlight = itemActive ? EditorPalette.GetHovered(EditorPalette.assetAccent) : EditorPalette.assetAccent; + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, Ui.ToPackedColor(highlight)); } + if (!editing) { m_dragDrop.DrawAssetSource(context, entry); @@ -323,29 +238,30 @@ FileBrowserDisplayEntry item m_dragDrop.DrawDirectoryTarget(context, entry.assetPath.ToString()); } - NativeImGui.SameLine(iconTextPos.X - NativeImGui.GetWindowPos().X, 0f); + Ui.SameLine(iconTextPos.X - Ui.GetWindowPos().X, 0f); if (editing) { - EditorWidget.IconText(icon, string.Empty, false); - NativeImGui.SameLine(0f, 0f); - Vector2 renameCursor = NativeImGui.GetCursorScreenPos(); - NativeImGui.SetCursorScreenPos(new Vector2(renameCursor.X, rowMinimumY)); + EditorUI.current.widgets.IconText(icon, string.Empty, false); + Ui.SameLine(0f, 0f); + Vector2 renameCursor = Ui.GetCursorScreenPosition(); + Ui.SetCursorScreenPosition(new Vector2(renameCursor.X, rowMinimumY)); m_rename.Draw( context, $"list_{entry.assetPath.ToString()}", entry.assetPath.ToString(), FileBrowserPresentation.List, - NativeImGui.GetContentRegionAvail().X, - MathF.Max(1f, rowMaximumY - rowMinimumY)); + Ui.GetContentRegionAvailable().X, + MathF.Max(1f, rowMaximumY - rowMinimumY) + ); } else { - EditorWidget.IconText(icon, name, false); + EditorUI.current.widgets.IconText(icon, name, false); } } finally { - NativeImGui.PopStyleColor(3); + Ui.PopStyleColor(3); } } @@ -353,17 +269,16 @@ private static void DrawTextCell( string text, Vector4 color ) { - NativeImGui.TableNextColumn(); + Ui.TableNextColumn(); InsetListCellContent(); - NativeImGui.PushStyleColor((int)ImGuiCol.Text, color); - NativeImGui.TextUnformatted(text); - NativeImGui.PopStyleColor(); + Ui.PushStyleColor(ImGuiCol.Text, color); + Ui.Text(text); + Ui.PopStyleColor(); } private static void InsetListCellContent() { - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + EditorWidget.style.assetListContentHorizontalPadding); + Ui.SetCursorPosX(Ui.GetCursorPosX() + EditorUI.current.widgets.style.assetListContentHorizontalPadding); } private readonly record struct ListColumnSeparatorState( @@ -372,5 +287,4 @@ private readonly record struct ListColumnSeparatorState( bool typeHovered, bool typeActive ); - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs index 9be864440..2ba85e2eb 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs @@ -1,26 +1,23 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - -internal sealed unsafe partial class FileBrowserPanel - +internal sealed partial class FileBrowserPanel { - private void DrawToolbar(EditorContext context) { DrawNavigationBar(context); @@ -30,53 +27,42 @@ private void DrawToolbar(EditorContext context) private void DrawNavigationBar(EditorContext context) { string current = m_assets.browser.currentDirectory; - - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.breadcrumbFramePadding); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.breadcrumbFramePadding); bool canGoBack = m_navigation.canGoBack; PushButtonColors(canGoBack ? EditorPalette.assetAccent : EditorPalette.assetBorderSoft); - - NativeImGui.BeginDisabled(!canGoBack); - if (NativeImGui.SmallButton($"{ImGuiIcon.AngleLeft}##Back")) + Ui.BeginDisabled(!canGoBack); + if (Ui.SmallButton($"{ImGuiIcon.AngleLeft}##Back")) m_navigation.GoBack(context); - - NativeImGui.EndDisabled(); - NativeImGui.PopStyleColor(3); - - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarTightSpacing); + Ui.EndDisabled(); + Ui.PopStyleColor(3); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarTightSpacing); bool canGoForward = m_navigation.canGoForward; PushButtonColors(canGoForward ? EditorPalette.assetAccent : EditorPalette.assetBorderSoft); - NativeImGui.BeginDisabled(!canGoForward); - if (NativeImGui.SmallButton($"{ImGuiIcon.AngleRight}##Forward")) + Ui.BeginDisabled(!canGoForward); + if (Ui.SmallButton($"{ImGuiIcon.AngleRight}##Forward")) m_navigation.GoForward(context); - - NativeImGui.EndDisabled(); - NativeImGui.PopStyleColor(3); - - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarSectionSpacing); - - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.assetText); - NativeImGui.TextUnformatted(GetDirectoryLabel(m_assets.browser.root, current)); - NativeImGui.PopStyleColor(); - - NativeImGui.PopStyleVar(); + Ui.EndDisabled(); + Ui.PopStyleColor(3); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSectionSpacing); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetText); + Ui.Text(GetDirectoryLabel(m_assets.browser.root, current)); + Ui.PopStyleColor(); + Ui.PopStyleVar(); } private void DrawViewAndSearchBar() { - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.toolbarFramePadding); - + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.toolbarFramePadding); PushButtonColors(EditorPalette.assetAccent); - if (NativeImGui.SmallButton($"{m_viewMode}##ViewMode")) + if (Ui.SmallButton($"{m_viewMode}##ViewMode")) m_viewMode = m_viewMode == ViewMode.List ? ViewMode.Grid : ViewMode.List; - NativeImGui.PopStyleColor(3); - - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarSpacing); + Ui.PopStyleColor(3); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSpacing); DrawEntryFilterCombo(); - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarSpacing); - - NativeImGui.SetNextItemWidth(-1f); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSpacing); + Ui.SetNextItemWidth(-1f); DrawSearchInput(); - NativeImGui.PopStyleVar(); + Ui.PopStyleVar(); } private void HandleEntryActivation( @@ -92,55 +78,39 @@ bool doubleClicked return; } - ImGuiIO* io = NativeImGui.Raw.GetIO(); - if (io->KeyCtrl != 0 || io->KeySuper != 0) - m_assets.browser.ToggleSelection(context, entry); - else - m_assets.browser.Select(context, entry.assetPath.ToString()); + EditorUI.current.widgets.SelectTarget(m_assets.edits.interactions, entry, m_visibleSelection); } private void DrawBreadcrumbBar( EditorContext context, float height ) { - IReadOnlyList parts = BuildBreadcrumbParts( - m_assets.browser.root, - m_assets.browser.currentDirectory); - Vector2 framePadding = EditorWidget.style.breadcrumbFramePadding; + IReadOnlyList parts = BuildBreadcrumbParts(m_assets.browser.root, m_assets.browser.currentDirectory); + Vector2 framePadding = EditorUI.current.widgets.style.breadcrumbFramePadding; float contentWidth = CalculateBreadcrumbContentWidth(parts, framePadding); - float availableWidth = NativeImGui.GetContentRegionAvail().X; + float availableWidth = Ui.GetContentRegionAvailable().X; bool hasHorizontalOverflow = contentWidth > availableWidth; - NativeImGui.SetNextWindowContentSize(new Vector2( - hasHorizontalOverflow ? contentWidth : 0f, - 0f)); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.WindowPadding), Vector2.Zero); - if (NativeImGui.BeginChild("##BreadcrumbBar", new Vector2(0f, height), (int)(ImGuiChildFlags.None), (int)(ImGuiWindowFlags.HorizontalScrollbar))) + Ui.SetNextWindowContentSize(new Vector2(hasHorizontalOverflow ? contentWidth : 0f, 0f)); + Ui.PushStyleVar((ImGuiStyleVar.WindowPadding), Vector2.Zero); + if (Ui.BeginChild("##BreadcrumbBar", new Vector2(0f, height), (ImGuiChildFlags.None), (ImGuiWindowFlags.HorizontalScrollbar))) { DrawBreadcrumbTopSeparator(); - float contentHeight = hasHorizontalOverflow - ? height - NativeImGui.Raw.GetStyle()->ScrollbarSize - : height; - float itemHeight = EditorWidget.GetClickableTextSize("A", framePadding).Y; - NativeImGui.SetCursorPosY(MathF.Max(0f, (contentHeight - itemHeight) * 0.5f)); - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.assetBreadcrumbText); - + float contentHeight = hasHorizontalOverflow ? height - UiDrawing.GetLayout().scrollbarSize : height; + float itemHeight = EditorUI.current.widgets.GetClickableTextSize("A", framePadding).Y; + Ui.SetCursorPosY(MathF.Max(0f, (contentHeight - itemHeight) * 0.5f)); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetBreadcrumbText); for (int i = 0; i < parts.Count; i++) { BreadcrumbPart part = parts[i]; if (i > 0) { - NativeImGui.SameLine(0f, EditorWidget.style.assetBreadcrumbSpacing); - EditorWidget.CenteredText(">", new Vector2( - NativeImGui.CalcTextSize(">").X, - itemHeight)); - NativeImGui.SameLine(0f, EditorWidget.style.assetBreadcrumbSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetBreadcrumbSpacing); + EditorUI.current.widgets.CenteredText(">", new Vector2(Ui.MeasureText(">").X, itemHeight)); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetBreadcrumbSpacing); } - Vector2 itemSize = EditorWidget.GetClickableTextSize(part.label, framePadding); - if (EditorWidget.ClickableText( - $"crumb_{part.root}_{part.directory}", - part.label, - itemSize)) + Vector2 itemSize = EditorUI.current.widgets.GetClickableTextSize(part.label, framePadding); + if (EditorUI.current.widgets.ClickableText($"crumb_{part.root}_{part.directory}", part.label, itemSize)) { if (string.IsNullOrEmpty(part.directory)) m_navigation.NavigateToRoot(context, part.root); @@ -149,11 +119,11 @@ float height } } - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } - NativeImGui.EndChild(); - NativeImGui.PopStyleVar(); + Ui.EndChild(); + Ui.PopStyleVar(); } private static float GetBreadcrumbBarHeight( @@ -161,19 +131,14 @@ private static float GetBreadcrumbBarHeight( string currentDirectory ) { IReadOnlyList parts = BuildBreadcrumbParts(root, currentDirectory); - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float itemHeight = EditorWidget.GetClickableTextSize( - "A", - EditorWidget.style.breadcrumbFramePadding).Y; + EditorUiLayout style = UiDrawing.GetLayout(); + float itemHeight = EditorUI.current.widgets.GetClickableTextSize("A", EditorUI.current.widgets.style.breadcrumbFramePadding).Y; float contentHeight = MathF.Max( - EditorWidget.style.assetBreadcrumbHeight, - MathF.Ceiling(itemHeight + style->ItemSpacing.Y * 2f + EditorWidget.style.borderSize)); - float contentWidth = CalculateBreadcrumbContentWidth( - parts, - EditorWidget.style.breadcrumbFramePadding); - return contentWidth > NativeImGui.GetContentRegionAvail().X - ? contentHeight + style->ScrollbarSize - : contentHeight; + EditorUI.current.widgets.style.assetBreadcrumbHeight, + MathF.Ceiling(itemHeight + style.itemSpacing.Y * 2f + EditorUI.current.widgets.style.borderSize) + ); + float contentWidth = CalculateBreadcrumbContentWidth(parts, EditorUI.current.widgets.style.breadcrumbFramePadding); + return contentWidth > Ui.GetContentRegionAvailable().X ? contentHeight + style.scrollbarSize : contentHeight; } private static float CalculateBreadcrumbContentWidth( @@ -182,18 +147,15 @@ Vector2 framePadding ) { if (parts.Count == 0) return 0f; - float width = 0f; - float separatorWidth = NativeImGui.CalcTextSize(">").X; + float separatorWidth = Ui.MeasureText(">").X; for (int i = 0; i < parts.Count; i++) { if (i > 0) - width += separatorWidth + EditorWidget.style.assetBreadcrumbSpacing * 2f; - - width += NativeImGui.CalcTextSize(parts[i].label).X + framePadding.X * 2f; + width += separatorWidth + EditorUI.current.widgets.style.assetBreadcrumbSpacing * 2f; + width += Ui.MeasureText(parts[i].label).X + framePadding.X * 2f; } return MathF.Ceiling(width); } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs index 056d05a89..75b17b80f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs @@ -1,33 +1,31 @@ +using Inno.Core.Identity; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - /// /// Asset browser panel with a tree pane and filtered table view. /// [EditorPanel("asset.file-browser", "File", order: 300, menuPath: "Content")] internal sealed partial class FileBrowserPanel : EditorPanel { + private IReadOnlyList m_visibleSelection = Array.Empty(); private const int C_SEARCH_BUFFER_SIZE = 256; private const int C_GRID_LABEL_LINE_COUNT = 2; private const float C_GRID_ICON_HORIZONTAL_OPTICAL_OFFSET = -1f; - - private readonly AssetEditorModule m_assets; + private readonly AssetBrowserModule m_assets; + private readonly EditorUiSession m_ui; private readonly FileBrowserData m_data; private readonly FileBrowserNavigation m_navigation; private readonly FileBrowserDragDrop m_dragDrop; @@ -35,23 +33,20 @@ internal sealed partial class FileBrowserPanel : EditorPanel private readonly FileBrowserRename m_rename; private readonly FileBrowserContextMenu m_contextMenu; private readonly FileBrowserTree m_tree; - private float m_treePaneRatio = 0.5f; private string m_filter = string.Empty; private ViewMode m_viewMode = ViewMode.List; private FileBrowserEntryTypeFilter m_entryTypeFilter = FileBrowserEntryTypeFilter.All; private FileBrowserEntryScopeFilter m_entryScopeFilter = FileBrowserEntryScopeFilter.CurrentOnly; - private float m_gridScale = EditorWidget.style.assetGridDefaultScale; - private float m_listNameSeparatorPosition = EditorWidget.style.assetListNameSeparatorPosition; - private float m_listTypeSeparatorPosition = EditorWidget.style.assetListTypeSeparatorPosition; - + private float m_gridScale; + private float m_listNameSeparatorPosition; + private float m_listTypeSeparatorPosition; private enum ViewMode { List, Grid } - /// /// Keeps scrolling inside the tree, entry list, and breadcrumb regions. /// @@ -85,51 +80,49 @@ protected override void Restore(EditorState state) { if (Enum.TryParse(state.Get("viewMode", string.Empty), out ViewMode viewMode)) m_viewMode = viewMode; - if (Enum.TryParse( - state.Get("entryTypeFilter", string.Empty), - out FileBrowserEntryTypeFilter typeFilter)) + if (Enum.TryParse(state.Get("entryTypeFilter", string.Empty), out FileBrowserEntryTypeFilter typeFilter)) { m_entryTypeFilter = typeFilter; } - if (Enum.TryParse( - state.Get("entryScopeFilter", string.Empty), - out FileBrowserEntryScopeFilter scopeFilter)) + + if (Enum.TryParse(state.Get("entryScopeFilter", string.Empty), out FileBrowserEntryScopeFilter scopeFilter)) { m_entryScopeFilter = scopeFilter; } + m_filter = state.Get("filter", string.Empty); float restoredTreePaneRatio = state.Get("treePaneRatio", 0.5f); - m_treePaneRatio = float.IsFinite(restoredTreePaneRatio) - ? Math.Clamp(restoredTreePaneRatio, 0f, 1f) - : 0.5f; + m_treePaneRatio = float.IsFinite(restoredTreePaneRatio) ? Math.Clamp(restoredTreePaneRatio, 0f, 1f) : 0.5f; m_gridScale = Math.Clamp( - state.Get("gridScale", EditorWidget.style.assetGridDefaultScale), - EditorWidget.style.assetGridMinimumScale, - EditorWidget.style.assetGridMaximumScale); + state.Get("gridScale", m_ui.widgets.style.assetGridDefaultScale), + m_ui.widgets.style.assetGridMinimumScale, + m_ui.widgets.style.assetGridMaximumScale + ); SetListColumnSeparators( - state.Get("listNameSeparator", EditorWidget.style.assetListNameSeparatorPosition), - state.Get("listTypeSeparator", EditorWidget.style.assetListTypeSeparatorPosition)); + state.Get("listNameSeparator", m_ui.widgets.style.assetListNameSeparatorPosition), + state.Get("listTypeSeparator", m_ui.widgets.style.assetListTypeSeparatorPosition) + ); } /// /// Creates the panel. /// - internal FileBrowserPanel(AssetEditorModule assets) - { + internal FileBrowserPanel( + AssetBrowserModule assets, + EditorUiSession ui + ) { m_assets = assets; + m_ui = ui ?? throw new ArgumentNullException(nameof(ui)); + m_gridScale = ui.widgets.style.assetGridDefaultScale; + m_listNameSeparatorPosition = ui.widgets.style.assetListNameSeparatorPosition; + m_listTypeSeparatorPosition = ui.widgets.style.assetListTypeSeparatorPosition; m_data = new FileBrowserData(assets); m_navigation = new FileBrowserNavigation(assets); m_dragDrop = new FileBrowserDragDrop(assets); m_changeTracker = new FileBrowserChangeTracker(assets); m_rename = new FileBrowserRename(assets); m_contextMenu = new FileBrowserContextMenu(assets, m_rename); - m_tree = new FileBrowserTree( - m_data, - m_navigation, - m_dragDrop, - m_rename, - m_contextMenu, - assets); + m_tree = new FileBrowserTree(m_data, m_navigation, m_dragDrop, m_rename, m_contextMenu, assets); } /// @@ -173,5 +166,4 @@ protected override void OnDraw(EditorContext context) PopBrowserStyle(); } } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs index 197559569..53d424e8b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs @@ -1,12 +1,13 @@ +using Inno.Core.Identity; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; - using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.ImGui.ImGuiWidget; namespace Inno.Editor.Panel.FileBrowser; - internal enum FileBrowserPresentation { Tree, @@ -14,21 +15,18 @@ internal enum FileBrowserPresentation Grid } -internal sealed class FileBrowserRename(AssetEditorModule assets) +internal sealed class FileBrowserRename(AssetBrowserModule assets) { - private AssetFileEntry? m_activeTarget; + private RuntimeIdentity? m_activeTarget; private FileBrowserPresentation m_lastPresentation = FileBrowserPresentation.List; private FileBrowserPresentation m_activePresentation; - internal void Update(EditorContext context) { - AssetFileEntry? target = assets.interactions.selection.selectedTarget as AssetFileEntry; - bool isActive = target is not null && - assets.interactions.For(FileBrowserInteractionIds.C_AREA, target) - .IsActive(FileBrowserInteractionIds.C_RENAME); - if (isActive && Equals(target, m_activeTarget)) + AssetFileEntry? target = assets.edits.interactions.selection.selectedTarget as AssetFileEntry; + bool isActive = target is not null && assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, target).IsActive(FileBrowserInteractionIds.C_RENAME); + if (isActive && target?.identity.runtimeIdentity == m_activeTarget) return; - m_activeTarget = isActive ? target : null; + m_activeTarget = isActive ? target!.identity.runtimeIdentity : null; m_activePresentation = m_lastPresentation; } @@ -43,9 +41,13 @@ internal bool IsEditing( FileBrowserPresentation presentation ) { Update(context); - return m_activeTarget is AssetFileEntry target && - m_activePresentation == presentation && - string.Equals(target.assetPath.ToString(), relativePath, StringComparison.Ordinal); + return ResolveTarget() is AssetFileEntry target + && m_activePresentation == presentation + && string.Equals( + target.assetPath.ToString(), + relativePath, + StringComparison.Ordinal + ); } internal void Draw( @@ -58,14 +60,18 @@ float rowHeight ) { if (!IsEditing(context, relativePath, presentation) || m_activeTarget is null) return; - _ = assets.interactions - .For(FileBrowserInteractionIds.C_AREA, m_activeTarget) - .Present( - FileBrowserInteractionIds.C_RENAME, - new InlineRenamePresentation( - id, - MathF.Max(1f, width), - MathF.Max(1f, rowHeight))); + _ = assets.edits.interactions.For( + FileBrowserInteractionIds.C_AREA, + ResolveTarget() + ).Present( + FileBrowserInteractionIds.C_RENAME, + new InlineRenamePresentation(id, MathF.Max(1f, width), MathF.Max(1f, rowHeight)) + ); } + private AssetFileEntry? ResolveTarget() => m_activeTarget is RuntimeIdentity identity + && assets.edits.interactions.TryResolveIdentity( + identity, + out IdentityObject? target + ) ? target as AssetFileEntry : null; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs index 41f2814da..1141b27c9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs @@ -1,4 +1,6 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.Linq; @@ -7,14 +9,10 @@ using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - internal sealed class FileBrowserTree { private readonly FileBrowserData m_data; @@ -22,20 +20,18 @@ internal sealed class FileBrowserTree private readonly FileBrowserDragDrop m_dragDrop; private readonly FileBrowserRename m_rename; private readonly FileBrowserContextMenu m_contextMenu; - private readonly AssetEditorModule m_assets; - + private readonly AssetBrowserModule m_assets; private bool m_rootOpenRequest = true; private bool m_currentDirectoryOpenRequest; private string m_currentDirectoryOpenTarget = string.Empty; private string? m_lastCurrentDirectoryOpenTarget; - internal FileBrowserTree( FileBrowserData data, FileBrowserNavigation navigation, FileBrowserDragDrop dragDrop, FileBrowserRename rename, FileBrowserContextMenu contextMenu, - AssetEditorModule assets + AssetBrowserModule assets ) { m_data = data; m_navigation = navigation; @@ -51,43 +47,31 @@ internal void DrawEntry( string label, bool isRoot ) { - List sorted = m_data.SortTreeEntries( - m_data.GetVisibleChildren(relativePath)); - bool isDirectory = isRoot || IsDirectoryPath(m_assets.pipeline, relativePath); + List sorted = m_data.SortTreeEntries(m_data.GetVisibleChildren(relativePath)); + bool isDirectory = isRoot || IsDirectoryPath(m_assets.edits.pipeline, relativePath); bool selected = string.Equals(m_assets.browser.GetSelectedPath(context), relativePath, StringComparison.Ordinal); bool isLeaf = !isDirectory || (!isRoot && sorted.Count == 0); - bool isCurrentDirectory = isDirectory && - string.Equals(m_assets.browser.currentDirectory, relativePath, StringComparison.Ordinal) && - (m_assets.browser.root == AssetBrowserRoot.Assets) == - (AssetPath.Parse(relativePath).source == AssetSourceId.project); + bool isCurrentDirectory = isDirectory + && string.Equals( + m_assets.browser.currentDirectory, + relativePath, + StringComparison.Ordinal + ) + && (m_assets.browser.root == AssetBrowserRoot.Assets) == (AssetPath.Parse(relativePath).source == AssetSourceId.project); string icon = m_assets.folderIcon; - if (!isDirectory - && m_assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry iconEntry)) + if (!isDirectory && m_assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry iconEntry)) icon = m_assets.GetIcon(iconEntry); - bool editing = !isRoot && m_rename.IsEditing( - context, - relativePath, - FileBrowserPresentation.Tree); + bool editing = !isRoot && m_rename.IsEditing(context, relativePath, FileBrowserPresentation.Tree); string nodeId = $"tree_{(isRoot ? $"root_{relativePath}" : relativePath)}"; if (ShouldOpenTreeEntry(relativePath, isRoot, isDirectory)) - EditorWidget.SetNextTreeNodeOpen(true); - - TreeNodeResult result = EditorWidget.TreeNode( + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( nodeId, - drawContext => DrawRowContent( - context, - nodeId, - relativePath, - label, - icon, - isCurrentDirectory, - editing, - drawContext.rowHeight), - new TreeNodeOptions { selected = selected, isLeaf = isLeaf }); - + drawContext => DrawRowContent(context, nodeId, relativePath, label, icon, isCurrentDirectory, editing, drawContext.rowHeight), + new TreeNodeOptions { selected = selected, isLeaf = isLeaf } + ); AssetFileEntry? treeEntry = null; - if (!isRoot - && m_assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry resolvedEntry)) + if (!isRoot && m_assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry resolvedEntry)) treeEntry = resolvedEntry; if (treeEntry is not null && result.isDoubleClicked) { @@ -104,29 +88,23 @@ bool isRoot m_navigation.NavigateTo(context, relativePath); m_assets.browser.Select(context, null); } + if (treeEntry is not null) { if (!editing) { m_dragDrop.DrawAssetSource(context, treeEntry); - m_contextMenu.DrawEntry( - context, - $"##asset_tree_context_{treeEntry.assetPath.ToString()}", - treeEntry.assetPath.ToString(), - FileBrowserPresentation.Tree); + m_contextMenu.DrawEntry(context, $"##asset_tree_context_{treeEntry.assetPath.ToString()}", treeEntry.assetPath.ToString(), FileBrowserPresentation.Tree); if (treeEntry.isDirectory && !treeEntry.isReadOnly) m_dragDrop.DrawDirectoryTarget(context, treeEntry.assetPath.ToString()); } } else if (isRoot) { - m_contextMenu.DrawDirectory( - context, - $"##asset_tree_root_context_{relativePath}", - relativePath, - FileBrowserPresentation.Tree); + m_contextMenu.DrawDirectory(context, $"##asset_tree_root_context_{relativePath}", relativePath, FileBrowserPresentation.Tree); m_dragDrop.DrawDirectoryTarget(context, relativePath); } + if (!result.isOpen) return; try @@ -139,7 +117,7 @@ bool isRoot } finally { - NativeImGui.TreePop(); + Ui.TreePop(); } } @@ -147,31 +125,25 @@ internal void DrawPluginRoot( EditorContext context, IReadOnlyList sourceMounts ) { - AssetSourceMount[] plugins = sourceMounts - .Where(mount => m_assets.IsPluginSource(mount.id)) - .OrderBy(static mount => mount.id.value, StringComparer.OrdinalIgnoreCase) - .ToArray(); - bool hasPluginDirectoryOpenRequest = m_currentDirectoryOpenRequest && - m_assets.IsPluginSource( - AssetPath.Parse(m_currentDirectoryOpenTarget).source); + AssetSourceMount[] plugins = sourceMounts.Where(mount => m_assets.edits.IsPluginSource(mount.id)).OrderBy( + static mount => mount.id.value, + StringComparer.OrdinalIgnoreCase + ).ToArray(); + bool hasPluginDirectoryOpenRequest = m_currentDirectoryOpenRequest && m_assets.edits.IsPluginSource(AssetPath.Parse(m_currentDirectoryOpenTarget).source); if (m_rootOpenRequest || hasPluginDirectoryOpenRequest) - EditorWidget.SetNextTreeNodeOpen(true); - bool isCurrentDirectory = m_assets.browser.root == AssetBrowserRoot.Plugins && - string.IsNullOrEmpty(m_assets.browser.currentDirectory); - - TreeNodeResult result = EditorWidget.TreeNode( + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + bool isCurrentDirectory = m_assets.browser.root == AssetBrowserRoot.Plugins && string.IsNullOrEmpty(m_assets.browser.currentDirectory); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( "tree_virtual_plugins", - _ => EditorWidget.IconText(m_assets.folderIcon, "Plugins", isCurrentDirectory), - new TreeNodeOptions - { - selected = false, - isLeaf = false - }); + _ => EditorUI.current.widgets.IconText(m_assets.folderIcon, "Plugins", isCurrentDirectory), + new TreeNodeOptions { selected = false, isLeaf = false } + ); if (result.isClicked || result.isDoubleClicked) { m_navigation.NavigateToRoot(context, AssetBrowserRoot.Plugins); m_assets.browser.Select(context, null); } + if (!result.isOpen) return; try @@ -187,7 +159,7 @@ IReadOnlyList sourceMounts } finally { - NativeImGui.TreePop(); + Ui.TreePop(); } } @@ -203,26 +175,19 @@ float rowHeight ) { if (!editing) { - EditorWidget.IconText(icon, label, isCurrentDirectory); + EditorUI.current.widgets.IconText(icon, label, isCurrentDirectory); return; } - EditorWidget.IconText(icon, string.Empty, false); - NativeImGui.SameLine(0f, 0f); - m_rename.Draw( - context, - id, - relativePath, - FileBrowserPresentation.Tree, - NativeImGui.GetContentRegionAvail().X, - rowHeight); + EditorUI.current.widgets.IconText(icon, string.Empty, false); + Ui.SameLine(0f, 0f); + m_rename.Draw(context, id, relativePath, FileBrowserPresentation.Tree, Ui.GetContentRegionAvailable().X, rowHeight); } internal void PrepareOpenRequests() { string currentDirectory = NormalizePath(m_assets.browser.currentDirectory); - if (!m_currentDirectoryOpenRequest && - !string.Equals(m_lastCurrentDirectoryOpenTarget, currentDirectory, StringComparison.Ordinal)) + if (!m_currentDirectoryOpenRequest && !string.Equals(m_lastCurrentDirectoryOpenTarget, currentDirectory, StringComparison.Ordinal)) { m_currentDirectoryOpenTarget = currentDirectory; m_currentDirectoryOpenRequest = true; @@ -237,13 +202,10 @@ internal void ClearOpenRequests() } internal void RequestOpenRoot() => m_rootOpenRequest = true; - internal void RequestOpenTreeToPath(string path) { string normalizedPath = NormalizePath(path); - string treePath = IsDirectoryPath(m_assets.pipeline, normalizedPath) - ? normalizedPath - : GetParentDirectory(normalizedPath); + string treePath = IsDirectoryPath(m_assets.edits.pipeline, normalizedPath) ? normalizedPath : GetParentDirectory(normalizedPath); m_currentDirectoryOpenTarget = treePath; m_currentDirectoryOpenRequest = true; m_lastCurrentDirectoryOpenTarget = treePath; @@ -260,13 +222,12 @@ bool isDirectory { if (string.IsNullOrEmpty(relativePath)) { - return m_rootOpenRequest || - m_currentDirectoryOpenRequest; + return m_rootOpenRequest || m_currentDirectoryOpenRequest; } - return m_currentDirectoryOpenRequest && - IsAncestorOrSelf(relativePath, m_currentDirectoryOpenTarget); + + return m_currentDirectoryOpenRequest && IsAncestorOrSelf(relativePath, m_currentDirectoryOpenTarget); } - return m_currentDirectoryOpenRequest && - IsAncestorOrSelf(relativePath, m_currentDirectoryOpenTarget); + + return m_currentDirectoryOpenRequest && IsAncestorOrSelf(relativePath, m_currentDirectoryOpenTarget); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs index 1424a36bd..7d00d7660 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs @@ -1,20 +1,18 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; - internal static class FileBrowserUtility { internal readonly record struct BreadcrumbPart( @@ -22,48 +20,44 @@ internal readonly record struct BreadcrumbPart( AssetBrowserRoot root, string directory ); - internal static void PushBrowserStyle() { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.assetText); - NativeImGui.PushStyleColor((int)(ImGuiCol.WindowBg), EditorPalette.collectionHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.ChildBg), EditorPalette.collectionHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.assetBorder); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableHeaderBg), EditorPalette.collectionHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableBorderStrong), EditorPalette.assetBorder); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableBorderLight), EditorPalette.assetBorderSoft); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableRowBg), EditorPalette.collectionRow); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); - NativeImGui.PushStyleColor((int)(ImGuiCol.Header), EditorPalette.assetAccent); - NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderHovered), EditorPalette.assetAccent); - NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderActive), EditorPalette.assetAccent); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.WindowPadding), EditorWidget.style.assetWindowPadding); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.ChildBorderSize), EditorWidget.style.borderSize); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.CellPadding), EditorWidget.style.assetCellPadding); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.ItemSpacing), EditorWidget.style.assetItemSpacing); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameRounding), EditorWidget.style.assetFrameRounding); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetText); + Ui.PushStyleColor((ImGuiCol.WindowBg), EditorPalette.collectionHeader); + Ui.PushStyleColor((ImGuiCol.ChildBg), EditorPalette.collectionHeader); + Ui.PushStyleColor((ImGuiCol.Border), EditorPalette.assetBorder); + Ui.PushStyleColor((ImGuiCol.TableHeaderBg), EditorPalette.collectionHeader); + Ui.PushStyleColor((ImGuiCol.TableBorderStrong), EditorPalette.assetBorder); + Ui.PushStyleColor((ImGuiCol.TableBorderLight), EditorPalette.assetBorderSoft); + Ui.PushStyleColor((ImGuiCol.TableRowBg), EditorPalette.collectionRow); + Ui.PushStyleColor((ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); + Ui.PushStyleColor((ImGuiCol.Header), EditorPalette.assetAccent); + Ui.PushStyleColor((ImGuiCol.HeaderHovered), EditorPalette.assetAccent); + Ui.PushStyleColor((ImGuiCol.HeaderActive), EditorPalette.assetAccent); + Ui.PushStyleVar((ImGuiStyleVar.WindowPadding), EditorUI.current.widgets.style.assetWindowPadding); + Ui.PushStyleVar((ImGuiStyleVar.ChildBorderSize), EditorUI.current.widgets.style.borderSize); + Ui.PushStyleVar((ImGuiStyleVar.CellPadding), EditorUI.current.widgets.style.assetCellPadding); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.assetItemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), EditorUI.current.widgets.style.assetFrameRounding); } internal static void PopBrowserStyle() { - NativeImGui.PopStyleVar(5); - NativeImGui.PopStyleColor(12); + Ui.PopStyleVar(5); + Ui.PopStyleColor(12); } internal static void PushButtonColors(Vector4 color) { - NativeImGui.PushStyleColor((int)(ImGuiCol.Button), color); - NativeImGui.PushStyleColor((int)(ImGuiCol.ButtonHovered), EditorPalette.GetHovered(color)); - NativeImGui.PushStyleColor((int)(ImGuiCol.ButtonActive), EditorPalette.GetActive(color)); + Ui.PushStyleColor((ImGuiCol.Button), color); + Ui.PushStyleColor((ImGuiCol.ButtonHovered), EditorPalette.GetHovered(color)); + Ui.PushStyleColor((ImGuiCol.ButtonActive), EditorPalette.GetActive(color)); } internal static bool IsDirectoryPath( AssetPipeline assets, string relativePath - ) - => assets.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry) - && entry.isDirectory; - + ) => assets.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry entry) && entry.isDirectory; internal static string GetDirectoryLabel( AssetBrowserRoot root, string relativePath @@ -104,6 +98,7 @@ string currentDirectory result = string.IsNullOrEmpty(directory) ? "~" : $"~/{directory}"; } } + return entry.isReadOnly ? $"{result} (read-only)" : result; } @@ -113,9 +108,7 @@ internal static string GetTypeText(AssetFileEntry entry) return AssetSample.fileType.TrimStart('.').ToUpperInvariant(); if (entry.isDirectory) return "FOLDER"; - string extension = string.IsNullOrEmpty(entry.extension) - ? Path.GetExtension(entry.assetPath.localPath) - : entry.extension; + string extension = string.IsNullOrEmpty(entry.extension) ? Path.GetExtension(entry.assetPath.localPath) : entry.extension; return string.IsNullOrEmpty(extension) ? "FILE" : extension.TrimStart('.').ToUpperInvariant(); } @@ -124,25 +117,20 @@ internal static IReadOnlyList BuildBreadcrumbParts( string relativePath ) { if (root == AssetBrowserRoot.Plugins && string.IsNullOrEmpty(relativePath)) - return [new BreadcrumbPart("Plugins", AssetBrowserRoot.Plugins, string.Empty)]; + return[new BreadcrumbPart("Plugins", AssetBrowserRoot.Plugins, string.Empty)]; AssetPath sourcePath = AssetPath.Parse(NormalizePath(relativePath)); string sourceRoot = new AssetPath(sourcePath.source, string.Empty).ToString(); var parts = new List(); if (root == AssetBrowserRoot.Plugins) { parts.Add(new BreadcrumbPart("Plugins", AssetBrowserRoot.Plugins, string.Empty)); - parts.Add(new BreadcrumbPart( - GetSourceRootLabel(sourcePath.source), - AssetBrowserRoot.Plugins, - sourceRoot)); + parts.Add(new BreadcrumbPart(GetSourceRootLabel(sourcePath.source), AssetBrowserRoot.Plugins, sourceRoot)); } else { - parts.Add(new BreadcrumbPart( - "Assets", - AssetBrowserRoot.Assets, - sourceRoot)); + parts.Add(new BreadcrumbPart("Assets", AssetBrowserRoot.Assets, sourceRoot)); } + if (string.IsNullOrEmpty(sourcePath.localPath)) return parts; string[] segments = sourcePath.localPath.Split('/', StringSplitOptions.RemoveEmptyEntries); @@ -150,24 +138,17 @@ string relativePath for (int i = 0; i < segments.Length; i++) { path = string.IsNullOrEmpty(path) ? segments[i] : $"{path}/{segments[i]}"; - parts.Add(new BreadcrumbPart( - segments[i], - root, - new AssetPath(sourcePath.source, path).ToString())); + parts.Add(new BreadcrumbPart(segments[i], root, new AssetPath(sourcePath.source, path).ToString())); } + return parts; } internal static bool IsReadOnlyLocation( AssetPipeline assets, AssetBrowserState browser - ) - => browser.root == AssetBrowserRoot.Plugins || - IsReadOnlySource(assets, browser.currentDirectory); - - internal static string NormalizePath(string? path) - => AssetPath.Parse(string.IsNullOrWhiteSpace(path) ? string.Empty : path.Replace('\\', '/').Trim()).ToString(); - + ) => browser.root == AssetBrowserRoot.Plugins || IsReadOnlySource(assets, browser.currentDirectory); + internal static string NormalizePath(string? path) => AssetPath.Parse(string.IsNullOrWhiteSpace(path) ? string.Empty : path.Replace('\\', '/').Trim()).ToString(); internal static string GetParentDirectory(string relativePath) { AssetPath path = AssetPath.Parse(NormalizePath(relativePath)); @@ -185,6 +166,7 @@ string relativePath if (mount.id == source) return mount.isReadOnly; } + return true; } @@ -207,9 +189,7 @@ bool isDirectory if (isDirectory) return name; string extension = Path.GetExtension(name); - return string.IsNullOrEmpty(extension) - ? name - : name[..^extension.Length]; + return string.IsNullOrEmpty(extension) ? name : name[..^extension.Length]; } /// @@ -231,11 +211,7 @@ internal static string ComposeRenamedEntryName( string sourcePath, string editedName, bool isDirectory - ) - => isDirectory - ? editedName - : editedName + Path.GetExtension(sourcePath); - + ) => isDirectory ? editedName : editedName + Path.GetExtension(sourcePath); internal static string[] FitTextToLines( string text, float maxWidth, @@ -243,22 +219,22 @@ int maxLines ) { ArgumentOutOfRangeException.ThrowIfLessThan(maxLines, 1); if (string.IsNullOrEmpty(text)) - return [string.Empty]; + return[string.Empty]; List elements = []; TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text); while (enumerator.MoveNext()) elements.Add(enumerator.GetTextElement()); - List lines = new(maxLines); int offset = 0; for (int lineIndex = 0; lineIndex < maxLines && offset < elements.Count; lineIndex++) { string remaining = string.Concat(elements.GetRange(offset, elements.Count - offset)); - if (NativeImGui.CalcTextSize(remaining).X <= maxWidth) + if (Ui.MeasureText(remaining).X <= maxWidth) { lines.Add(remaining); break; } + bool isLastLine = lineIndex == maxLines - 1; string suffix = isLastLine ? "..." : string.Empty; int count = 0; @@ -266,20 +242,23 @@ int maxLines while (offset + count < elements.Count) { string next = candidate + elements[offset + count]; - if (NativeImGui.CalcTextSize(next + suffix).X > maxWidth) + if (Ui.MeasureText(next + suffix).X > maxWidth) break; candidate = next; count++; } + if (count == 0) { - lines.Add(isLastLine && NativeImGui.CalcTextSize("...").X <= maxWidth ? "..." : elements[offset]); + lines.Add(isLastLine && Ui.MeasureText("...").X <= maxWidth ? "..." : elements[offset]); offset++; continue; } + lines.Add(candidate + suffix); offset += count; } + return lines.Count == 0 ? [string.Empty] : lines.ToArray(); } @@ -293,20 +272,20 @@ string path return false; if (ancestor.localPath.Length == 0 || string.Equals(ancestor.localPath, descendant.localPath, StringComparison.Ordinal)) return true; - return descendant.localPath.Length > ancestor.localPath.Length && - descendant.localPath.StartsWith(ancestor.localPath, StringComparison.Ordinal) && - descendant.localPath[ancestor.localPath.Length] == '/'; + return descendant.localPath.Length > ancestor.localPath.Length + && descendant.localPath.StartsWith( + ancestor.localPath, + StringComparison.Ordinal + ) + && descendant.localPath[ancestor.localPath.Length] == '/'; } private static string GetSourceRootLabel(AssetSourceId source) => source == AssetSourceId.project ? "Assets" : source.value; - private static string FormatSourcePath( AssetSourceId source, string localPath ) { - string root = source == AssetSourceId.project - ? GetSourceRootLabel(source) - : $"Plugins/{GetSourceRootLabel(source)}"; + string root = source == AssetSourceId.project ? GetSourceRootLabel(source) : $"Plugins/{GetSourceRootLabel(source)}"; return string.IsNullOrEmpty(localPath) ? root : $"{root}/{localPath}"; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/FileBrowserInteractionIds.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/FileBrowserInteractionIds.cs index d837d0d34..9a7526c75 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/FileBrowserInteractionIds.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/FileBrowserInteractionIds.cs @@ -1,5 +1,7 @@ -namespace Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; +namespace Inno.Editor.Panel.FileBrowser; internal static class FileBrowserInteractionIds { internal const string C_AREA = "panel/asset.file-browser"; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj index 10dbd4e71..8fb3f1430 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Inno.Editor.Panel.FileBrowser.csproj @@ -4,47 +4,33 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetCommand.cs index 746fdefb0..ffb94a2c9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetCommand.cs @@ -5,12 +5,12 @@ using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Editor.Interactions; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_CREATE_ASSET, FileBrowserInteractionIds.C_AREA)] -internal sealed class CreateAssetCommand(AssetEditorModule assets) : EditorAction +internal sealed class CreateAssetCommand(AssetBrowserModule assets) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -21,27 +21,27 @@ internal sealed class CreateAssetCommand(AssetEditorModule assets) : EditorActio /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) - => assets.CanCreateAsset(context.target, context.argument) - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => assets.edits.CanCreateAsset( + context.target, + context.argument + ) ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { - AssetFileEntry created = assets.CreateAsset(context.target, context.argument); - assets.BeginCreatedSourceRename(created); + AssetFileEntry created = assets.edits.CreateAsset(context.target, context.argument); + _ = context.interactions.For(FileBrowserInteractionIds.C_AREA, created).Select(); + _ = context.interactions.For(FileBrowserInteractionIds.C_AREA, created).Execute(FileBrowserInteractionIds.C_RENAME); context.interactions.OpenPanel("scene.inspector"); } } [EditorMenuSource(FileBrowserInteractionIds.C_AREA)] -internal sealed class AssetCreationMenu(AssetEditorModule assets) : EditorMenuSource +internal sealed class AssetCreationMenu(AssetBrowserModule assets) : EditorMenuSource { /// /// Builds a validated result from the current immutable input snapshot. @@ -52,26 +52,23 @@ internal sealed class AssetCreationMenu(AssetEditorModule assets) : EditorMenuSo /// /// The builder consumed by build; ownership remains with the caller unless explicitly stated otherwise. /// -public override void Build( - EditorMenuContext context, - EditorMenuBuilder builder -) { - IReadOnlyList templates = assets.creationTemplates; + public override void Build( + EditorMenuContext context, + EditorMenuBuilder builder + ) { + IReadOnlyList templates = assets.edits.creationTemplates; var declaredGroups = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (AssetCreationRegistry.Registration registration in templates) + foreach (AssetCreationMenuAttribute declaration in templates) { - AssetCreationMenuAttribute declaration = registration.declaration; string[] segments = declaration.menuPath.Split('/'); if (segments.Length > 1) { string topLevel = "Create/" + segments[0]; if (declaredGroups.Add(topLevel)) { - builder.AddGroup( - topLevel, - declaration.groupOrder, - declaration.separatorBeforeGroup); + builder.AddGroup(topLevel, declaration.groupOrder, declaration.separatorBeforeGroup); } + for (int depth = 1; depth < segments.Length - 1; depth++) { string group = "Create/" + string.Join('/', segments.Take(depth + 1)); @@ -80,11 +77,7 @@ EditorMenuBuilder builder } } - builder.Add( - "Create/" + declaration.menuPath, - FileBrowserInteractionIds.C_CREATE_ASSET, - declaration.itemOrder, - argument: declaration.id); + builder.Add("Create/" + declaration.menuPath, FileBrowserInteractionIds.C_CREATE_ASSET, declaration.itemOrder, argument: declaration.id); } } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetFolderCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetFolderCommand.cs index 7d0e6c338..c63999f5d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetFolderCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/CreateAssetFolderCommand.cs @@ -1,15 +1,15 @@ +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; using System.Linq; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_CREATE_FOLDER, FileBrowserInteractionIds.C_AREA)] [EditorMenu(FileBrowserInteractionIds.C_AREA, "Create/Folder", order: 100)] -internal sealed class CreateAssetFolderCommand(AssetEditorModule assets) : EditorAction +internal sealed class CreateAssetFolderCommand(AssetBrowserModule assets) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -20,11 +20,8 @@ internal sealed class CreateAssetFolderCommand(AssetEditorModule assets) : Edito /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => assets.pipeline.isInitialized && IsWritableDirectory(context.target) - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => assets.edits.pipeline.isInitialized + && IsWritableDirectory(context.target) ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -33,43 +30,7 @@ protected override EditorActionState Query(EditorActionContext context) /// protected override void Execute(EditorActionContext context) { - string parent = Normalize(context.target); - string candidate = Combine(parent, "New Folder"); - int suffix = 1; - while (assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(candidate), out _)) - candidate = Combine(parent, $"New Folder {suffix++}"); - assets.pipeline.CreateDirectory(AssetPath.Parse(candidate)); - var data = new AssetHistoryData( - AssetHistoryOperationKind.CreateDirectory, - candidate, - string.Empty, - isDirectory: true, - archive: []); - try - { - context.history.RecordApplied( - "Create Folder", - new EditorHistoryChange( - AssetHistoryKinds.SourceOperation, - EditorHistoryPayload.FromBytes(data.Encode()))); - } - catch (Exception failure) - { - try - { - assets.pipeline.Delete(AssetPath.Parse(candidate)); - } - catch (Exception rollbackException) - { - throw new AggregateException( - "The folder could not be recorded and its compensation also failed.", - failure, - rollbackException); - } - throw; - } - if (!assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(candidate), out AssetFileEntry target)) - return; + AssetFileEntry target = assets.edits.CreateDirectory(AssetPath.Parse(context.target)); EditorInteraction interaction = context.interactions.For(FileBrowserInteractionIds.C_AREA, target); _ = interaction.Select(); _ = interaction.Execute(FileBrowserInteractionIds.C_RENAME); @@ -79,21 +40,19 @@ private static string Combine( string parent, string name ) => string.IsNullOrEmpty(parent) ? name : $"{parent}/{name}"; - - private static string Normalize(string? path) - => string.IsNullOrWhiteSpace(path) - ? string.Empty - : path.Replace('\\', '/').Trim('/'); - + private static string Normalize(string? path) => string.IsNullOrWhiteSpace(path) ? string.Empty : path.Replace('\\', '/').Trim('/'); private bool IsWritableDirectory(string relativePath) { string normalized = Normalize(relativePath); AssetSourceId source = AssetPath.Parse(normalized).source; - AssetSourceMount? mount = assets.pipeline.sourceMounts.FirstOrDefault(candidate => candidate.id == source); + AssetSourceMount? mount = assets.edits.pipeline.sourceMounts.FirstOrDefault(candidate => candidate.id == source); if (mount is null || mount.isReadOnly) return false; - return string.IsNullOrEmpty(AssetPath.Parse(normalized).localPath) || - assets.pipeline.TryGetFileSystemEntry(AssetPath.Parse(normalized), out AssetFileEntry entry) - && entry.isDirectory; + return string.IsNullOrEmpty(AssetPath.Parse(normalized).localPath) + || assets.edits.pipeline.TryGetFileSystemEntry( + AssetPath.Parse(normalized), + out AssetFileEntry entry + ) + && entry.isDirectory; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/DeleteAssetCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/DeleteAssetCommand.cs index 5b3e2722e..eff199328 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/DeleteAssetCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/DeleteAssetCommand.cs @@ -1,13 +1,15 @@ +using System.Collections.Generic; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; using Inno.Core.Input; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_DELETE, priority: 100)] [EditorMenu(FileBrowserInteractionIds.C_AREA, "Delete", order: 200)] [EditorShortcut(FileBrowserInteractionIds.C_AREA, KeyCode.Delete)] -internal sealed class DeleteAssetCommand(AssetEditorModule assets) : EditorAction +internal sealed class DeleteAssetCommand(AssetBrowserModule assets) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -19,9 +21,10 @@ internal sealed class DeleteAssetCommand(AssetEditorModule assets) : EditorActio /// The validated editor action state that represents the completed operation. /// protected override EditorActionState Query(EditorActionContext context) - => !context.target.isReadOnly && TryGetAssetContext(context, out _) - ? EditorActionState.enabled - : EditorActionState.hidden; + { + AssetOperationValidation validation = assets.edits.ValidateDeleteSelection(context.editor, Targets(context)); + return validation.isValid ? EditorActionState.enabled : new EditorActionState(true, false, unavailableReason: validation.message); + } /// /// Executes the prepared operation and publishes only a completed result. @@ -31,13 +34,16 @@ protected override EditorActionState Query(EditorActionContext c /// protected override void Execute(EditorActionContext context) { - if (TryGetAssetContext(context, out AssetEditorContext? assetContext) && assetContext is not null) - _ = assets.DeleteWithHistory(context, assetContext); + assets.edits.DeleteSelection(context.editor, Targets(context)); + context.interactions.SetSelection((object? )null); } - private bool TryGetAssetContext( - EditorActionContext context, - out AssetEditorContext? assetContext - ) - => assets.TryCreateContext(context.editor, context.target.assetPath.ToString(), out assetContext); + private static IReadOnlyList Targets(EditorActionContext context) + { + if (!context.interactions.selection.Contains(context.target)) + return[context.target]; + var values = new List(); + context.interactions.selection.CopyTargets(values); + return values.Count == context.interactions.selection.count ? values : []; + } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/ImportAssetSampleCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/ImportAssetSampleCommand.cs index 471b7607b..fc4f2cc11 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/ImportAssetSampleCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/ImportAssetSampleCommand.cs @@ -1,15 +1,15 @@ +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_IMPORT_SAMPLE, FileBrowserInteractionIds.C_AREA)] [EditorMenu(FileBrowserInteractionIds.C_AREA, "Import Sample", order: 120)] internal sealed class ImportAssetSampleCommand( - AssetEditorModule assets, + AssetBrowserModule assets, AssetSampleImportModule imports ) : EditorAction { @@ -31,9 +31,7 @@ protected override EditorActionState Query(EditorActionContext c try { AssetPath target = AssetPath.Project(AssetSample.GetImportName(context.target.assetPath)); - return assets.pipeline.TryGetFileSystemEntry(target, out _) - ? EditorActionState.disabled - : EditorActionState.enabled; + return assets.edits.pipeline.TryGetFileSystemEntry(target, out _) ? EditorActionState.disabled : EditorActionState.enabled; } catch (ArgumentException) { @@ -47,6 +45,5 @@ protected override EditorActionState Query(EditorActionContext c /// /// The action context containing the selected sample and history transaction. /// - protected override void Execute(EditorActionContext context) - => imports.Request(context.target.assetPath); + protected override void Execute(EditorActionContext context) => imports.Request(context.target.assetPath); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/MoveAssetEntryDropHandler.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/MoveAssetEntryDropHandler.cs index dd286f2b0..ac60f5765 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/MoveAssetEntryDropHandler.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/MoveAssetEntryDropHandler.cs @@ -1,8 +1,10 @@ +using System.Collections.Generic; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; namespace Inno.Editor.Panel.FileBrowser; - /// /// Moves directory drag payloads into File Browser directory targets. /// @@ -10,8 +12,7 @@ namespace Inno.Editor.Panel.FileBrowser; /// The assets used to initialize this instance. /// [EditorDrop(FileBrowserInteractionIds.C_AREA, priority: 200)] -internal sealed class MoveAssetEntryDropHandler(AssetEditorModule assets) - : EditorDrop +internal sealed class MoveAssetEntryDropHandler(AssetBrowserModule assets) : EditorDrop { /// /// Evaluates the operation's current availability and presentation state. @@ -22,11 +23,10 @@ internal sealed class MoveAssetEntryDropHandler(AssetEditorModule assets) /// /// The context that supplies state and services for this operation. /// - protected override EditorDropStatus Query(EditorDropContext context) - => assets.CanMoveToDirectory(context.source.assetPath.ToString(), context.target) - ? EditorDropStatus.Accept() - : EditorDropStatus.rejected; - + protected override EditorDropStatus Query(EditorDropContext context) => assets.edits.CanMoveSelection( + SelectedSources(context), + context.target + ) ? EditorDropStatus.Accept() : EditorDropStatus.rejected; /// /// Validates and applies the current editor drag-and-drop interaction atomically. /// @@ -40,10 +40,16 @@ protected override EditorDropResult Drop(EditorDropContext SelectedSources(EditorDropContext context) + { + if (!context.interactions.selection.Contains(context.source)) + return[context.source]; + var values = new List(); + context.interactions.selection.CopyTargets(values); + return values.Count == context.interactions.selection.count ? values : []; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/OpenAssetCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/OpenAssetCommand.cs index 4732a5d59..98c547ef0 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/OpenAssetCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/OpenAssetCommand.cs @@ -1,11 +1,12 @@ +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Interactions; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_OPEN, FileBrowserInteractionIds.C_AREA, priority: 100)] -internal sealed class OpenAssetCommand(AssetEditorModule assets) : EditorAction +internal sealed class OpenAssetCommand(AssetBrowserModule assets) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -20,9 +21,7 @@ protected override EditorActionState Query(EditorActionContext c { if (!TryGetAssetContext(context, out AssetEditorContext? assetContext) || assetContext is null) return EditorActionState.hidden; - return assetContext.isDirectory - ? EditorActionState.hidden - : EditorActionState.enabled; + return assetContext.isDirectory ? EditorActionState.hidden : EditorActionState.enabled; } /// @@ -35,20 +34,27 @@ protected override void Execute(EditorActionContext context) { if (!TryGetAssetContext(context, out AssetEditorContext? assetContext) || assetContext is null) return; - if (assets.pipeline.TryLoad(AssetPath.Parse(assetContext.relativePath), out AssetObject? asset) && - asset is not null && - context.interactions.For(FileBrowserInteractionIds.C_AREA, asset).Execute( - FileBrowserInteractionIds.C_OPEN, - assetContext.relativePath)) + if (assets.edits.pipeline.TryLoad( + AssetPath.Parse(assetContext.relativePath), + out AssetObject? asset + ) + && asset is not null + && context.interactions.For( + FileBrowserInteractionIds.C_AREA, + asset + ).Execute( + FileBrowserInteractionIds.C_OPEN, + assetContext.relativePath + )) { return; } - _ = assets.Open(assetContext); + + _ = assets.edits.Open(assetContext); } private bool TryGetAssetContext( EditorActionContext context, out AssetEditorContext? assetContext - ) - => assets.TryCreateContext(context.editor, context.target.assetPath.ToString(), out assetContext); + ) => assets.edits.TryCreateContext(context.editor, context.target.assetPath.ToString(), out assetContext); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/RenameAssetCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/RenameAssetCommand.cs index 027a8c82f..0eeae9549 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/RenameAssetCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Interactions/RenameAssetCommand.cs @@ -1,31 +1,26 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System; - using Inno.Assets.Pipeline; using Inno.Core.Input; using Inno.Core.Logging; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Interactions; - -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using static Inno.Editor.Panel.FileBrowser.FileBrowserUtility; namespace Inno.Editor.Panel.FileBrowser; - [EditorAction(FileBrowserInteractionIds.C_RENAME, priority: 100)] [EditorMenu(FileBrowserInteractionIds.C_AREA, "Rename", order: 100)] [EditorShortcut(FileBrowserInteractionIds.C_AREA, KeyCode.F2)] internal sealed class RenameAssetCommand( - AssetEditorModule assets, + AssetBrowserModule assets, LogRouter logs -) : - EditorPresentationAction +) : EditorPresentationAction { - private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))) - .CreateLogger(); - private AssetEditorContext? m_asset; + private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))).CreateLogger(); private string m_buffer = string.Empty; private bool m_requestFocus; - /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -35,11 +30,11 @@ LogRouter logs /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => !context.target.isReadOnly && TryGetAssetContext(context, out _) - ? EditorActionState.enabled - : EditorActionState.hidden; - + protected override EditorActionState Query(EditorActionContext context) => !context.target.isReadOnly + && TryGetAssetContext( + context, + out _ + ) ? EditorActionState.enabled : EditorActionState.hidden; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -51,7 +46,6 @@ protected override void Execute(EditorActionContext context) if (!TryGetAssetContext(context, out AssetEditorContext? assetContext) || assetContext is null) return; Activate(context); - m_asset = assetContext; m_buffer = GetEditableName(assetContext.name, assetContext.isDirectory); m_requestFocus = true; } @@ -67,27 +61,29 @@ protected override void Execute(EditorActionContext context) /// protected override bool Present(EditorActionContext context) { - if (m_asset is null) + if (!TryResolveAsset(out AssetEditorContext? asset) || asset is null) return false; InlineRenamePresentation presentation = context.argument; - - InlineRenameResult result = EditorWidget.InlineRename( + InlineRenameResult result = EditorUI.current.widgets.InlineRename( presentation.id, ref m_buffer, ref m_requestFocus, presentation.rowHeight, presentation.bufferSize, - presentation.width); + presentation.width + ); if (result == InlineRenameResult.Cancel) { Cancel(); return true; } + if (result == InlineRenameResult.FocusLost) { _ = TryCommit(keepActiveWhenInvalid: false); return true; } + if (result != InlineRenameResult.Commit) return true; _ = TryCommit(keepActiveWhenInvalid: true); @@ -98,26 +94,23 @@ protected override bool Present(EditorActionContext protected override void OnCompleted() => ClearState(); - /// /// Cancels the interaction without committing its pending value. /// protected override void OnCancelled() => ClearState(); - /// /// Cancels pending presentation state when its editor surface disappears. /// protected override void OnPresentationLost() => _ = TryCommit(keepActiveWhenInvalid: false); - private bool TryCommit(bool keepActiveWhenInvalid) { - if (m_asset is null) + if (!TryResolveAsset(out AssetEditorContext? asset) || asset is null) { Cancel(); return false; } - EditorValidationResult validation = assets.ValidateRename(m_asset, m_buffer); + EditorValidationResult validation = assets.edits.ValidateRename(asset, m_buffer); if (!validation.isValid) { m_log.Write(LogLevel.Warn, "Asset rename was rejected: {0}", [validation.message]); @@ -128,16 +121,20 @@ private bool TryCommit(bool keepActiveWhenInvalid) return false; } - AssetEditorContext asset = m_asset; string name = m_buffer; + assets.edits.Rename(asset, name); Complete(); - assets.Rename(asset, name); return true; } + private bool TryResolveAsset(out AssetEditorContext? asset) + { + asset = null; + return activeTarget is AssetFileEntry entry && activeEditorContext is { } editor && assets.edits.TryCreateContext(editor, entry.assetPath.ToString(), out asset); + } + private void ClearState() { - m_asset = null; m_buffer = string.Empty; m_requestFocus = false; } @@ -145,6 +142,5 @@ private void ClearState() private bool TryGetAssetContext( EditorActionContext context, out AssetEditorContext? assetContext - ) - => assets.TryCreateContext(context.editor, context.target.assetPath.ToString(), out assetContext); + ) => assets.edits.TryCreateContext(context.editor, context.target.assetPath.ToString(), out assetContext); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Properties/ScriptingApi.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Properties/ScriptingApi.cs deleted file mode 100644 index d4fa16c72..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Properties/ScriptingApi.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Inno.Scripting.Api; -using Inno.Assets.Pipeline; -using Inno.Editor.Panel.FileBrowser; - -[assembly: ScriptingApiNamespace( - "InnoEditor.Assets", - "Inno.Editor.Panel.FileBrowser", - ScriptingApiScope.Editor)] - -[assembly: ScriptingApiExport(typeof(AssetEditor), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetFileEntry), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetEditorAttribute), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetEditorContext), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetOperationValidation), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetBrowserRoot), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetBrowserState), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetIconAttribute), ScriptingApiScope.Editor)] -[assembly: ScriptingApiExport(typeof(AssetImportSettingsEdits), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModal.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModal.cs index 30889751a..cdcae798d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModal.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Samples/AssetSampleImportModal.cs @@ -1,10 +1,10 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using System.Numerics; - using Inno.Editor.Core; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; namespace Inno.Editor.Panel.FileBrowser; - [EditorModal("asset-sample-import.progress", "Importing Sample", order: 110)] internal sealed class AssetSampleImportModal(AssetSampleImportModule imports) : EditorModal { @@ -12,7 +12,6 @@ internal sealed class AssetSampleImportModal(AssetSampleImportModule imports) : /// Shows progress while the import owns pending background work. /// public override bool isVisible => imports.isBusy; - /// /// Blocks unrelated interaction until import publication or cancellation completes. /// @@ -26,9 +25,9 @@ internal sealed class AssetSampleImportModal(AssetSampleImportModule imports) : /// protected override void OnDraw(EditorContext context) { - EditorWidget.WrappedText(imports.status); - EditorWidget.CenteredProgressBar(imports.progress, new Vector2(-1f, 0f), $"{imports.progress:P0}"); - if (EditorWidget.CenteredButton("Cancel", EditorWidget.style.itemSpacing.Y)) + EditorUI.current.widgets.WrappedText(imports.status); + EditorUI.current.widgets.CenteredProgressBar(imports.progress, new Vector2(-1f, 0f), $"{imports.progress:P0}"); + if (EditorUI.current.widgets.CenteredButton("Cancel", EditorUI.current.widgets.style.itemSpacing.Y)) imports.Cancel(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs index 58acca79e..4b65ea47e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs @@ -1,12 +1,10 @@ using System; - using Inno.Core.Settings; using Inno.Editor.Settings; using Inno.Runtime; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.GameView; - [ProjectSettingPath("Project/Player/Presentation")] internal sealed class GamePresentationSetting : ProjectSettingEditor { @@ -17,7 +15,6 @@ internal sealed class GamePresentationSetting : ProjectSettingEditor public override string section => "Display"; - /// /// Gets the explanation shared by Game View and deployed Player presentation. /// @@ -32,22 +29,21 @@ internal sealed class GamePresentationSetting : ProjectSettingEditor /// Gets a new value initialized to this setting's canonical default state. /// public override EditorSettingObject defaultValue => CreateDefault(); - /// /// Gets the presentation section that groups this setting. /// public override string section => "Viewport Backgrounds"; - /// /// Gets the user-facing explanation of this feature or setting. /// @@ -37,13 +32,12 @@ internal sealed class GameViewBackgroundSetting : EditorSetting protected override void OnDraw(EditorSettingObject setting) { Vector4 value = ReadVector(setting); - NativeImGui.SetNextItemWidth(-1f); + Ui.SetNextItemWidth(-1f); if (EditorImGui.ColorEditLinear4("##game_view_background", ref value)) setting.SetAsSingleArray("value", [value.X, value.Y, value.Z, value.W]); } internal static Vector4 Read(EditorSettings settings) => ReadVector(settings.Get(C_PATH)); - private static EditorSettingObject CreateDefault() { var result = new EditorSettingObject(); @@ -54,8 +48,6 @@ private static EditorSettingObject CreateDefault() private static Vector4 ReadVector(EditorSettingObject setting) { float[] values = setting.GetAsSingleArray("value", S_DEFAULT); - return values.Length == 4 - ? new Vector4(values[0], values[1], values[2], values[3]) - : new Vector4(S_DEFAULT[0], S_DEFAULT[1], S_DEFAULT[2], S_DEFAULT[3]); + return values.Length == 4 ? new Vector4(values[0], values[1], values[2], values[3]) : new Vector4(S_DEFAULT[0], S_DEFAULT[1], S_DEFAULT[2], S_DEFAULT[3]); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs index 3fd3884f0..4f262ac0d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs @@ -1,9 +1,10 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using EditorImGui = Inno.Editor.ImGui.ImGui; using Inno.References; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Core.Settings; using Inno.Core.Input; using Inno.Editor.Core; @@ -14,23 +15,20 @@ using Inno.Editor.Settings; using Inno.Rendering; using Inno.Runtime; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using Ui = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Runtime; namespace Inno.Editor.Panel.GameView; - /// /// Presents the active Plugin provider for the open Game viewport purpose. /// [EditorPanel("rendering.game-view", "Game", order: 220, menuPath: "Viewports")] -internal sealed unsafe class GameViewPanel : EditorPanel +internal sealed class GameViewPanel : EditorPanel { + private IDisposable? m_settingsSubscription; private const string C_VIEWPORT_ID = "game-view"; private static readonly EditorViewportKindId S_KIND = new("inno.editor.viewport.game"); private static readonly Vector2 S_UNAVAILABLE_PADDING = new(48f, 32f); - private readonly EditorRenderingModule m_rendering; private readonly IEditorGameScenePresentation m_scenePresentation; private readonly IEditorPlayMode m_playMode; @@ -42,7 +40,6 @@ internal sealed unsafe class GameViewPanel : EditorPanel private GamePresentationSettings m_presentation = new(); private long m_presentationRevision = -1; private bool m_leftPointerActive; - internal GameViewPanel( EditorRenderingModule rendering, IEditorGameScenePresentation scenePresentation, @@ -65,7 +62,6 @@ ProjectSettingsStore projectSettings /// Gets whether use window padding is enabled for this implementation. /// public override bool useWindowPadding => false; - /// /// Gets whether allow scrolling is enabled for this implementation. /// @@ -81,101 +77,85 @@ protected override void OnDraw(EditorContext context) { using IDisposable presentationScope = m_playModeLoop.EnterPresentationScope(); _ = context; - Vector2 available = NativeImGui.GetContentRegionAvail(); + Vector2 available = Ui.GetContentRegionAvailable(); if (available.X <= 0f || available.Y <= 0f) return; RefreshPresentationSettings(); - float framebufferScale = NativeImGui.Raw.GetIO()->DisplayFramebufferScale.X; + float framebufferScale = UiDrawing.GetInput().framebufferScale.X; if (!float.IsFinite(framebufferScale) || framebufferScale <= 0f) framebufferScale = 1f; GameViewportLayout layout = CalculateLayout(available, m_presentation, framebufferScale); m_rendering.SetPresentation( C_VIEWPORT_ID, - new EditorViewportPresentation(new Inno.Core.Mathematics.Color( - m_backgroundColor.X, - m_backgroundColor.Y, - m_backgroundColor.Z, - m_backgroundColor.W), framebufferScale)); + new EditorViewportPresentation(new Inno.Core.Mathematics.Color(m_backgroundColor.X, m_backgroundColor.Y, m_backgroundColor.Z, m_backgroundColor.W), framebufferScale) + ); m_rendering.SetContentScope(C_VIEWPORT_ID, CreateContentScope()); - Vector2 outputOrigin = NativeImGui.GetCursorScreenPos() + layout.offset; - Vector2 mouse = NativeImGui.GetMousePos(); + Vector2 outputOrigin = Ui.GetCursorScreenPosition() + layout.offset; + Vector2 mouse = Ui.GetMousePos(); Vector2 local = mouse - outputOrigin; - bool inside = local.X >= 0f && local.Y >= 0f - && local.X < layout.size.X && local.Y < layout.size.Y; - ImGuiViewport* viewport = NativeImGui.Raw.GetWindowViewport(); - bool gameViewFocused = m_playMode.isPlaying - && NativeImGui.IsWindowFocused((int)ImGuiFocusedFlags.RootAndChildWindows); - bool imageHovered = inside - && NativeImGui.IsWindowHovered((int)ImGuiHoveredFlags.RootAndChildWindows); - bool focused = m_inputCapture.Report( - viewport->ID, - outputOrigin - new Vector2(viewport->Pos.X, viewport->Pos.Y), - layout.size, - gameViewFocused, - imageHovered); + bool inside = local.X >= 0f && local.Y >= 0f && local.X < layout.size.X && local.Y < layout.size.Y; + var viewport = UiDrawing.GetViewport(); + bool gameViewFocused = m_playMode.isPlaying && Ui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows); + bool imageHovered = inside && Ui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows); + bool focused = m_inputCapture.Report(viewport.id, outputOrigin - viewport.position, layout.size, gameViewFocused, imageHovered); bool acceptsPointer = focused && imageHovered; - if (!NativeImGui.IsMouseDown((int)ImGuiMouseButton.Left) - && !NativeImGui.IsMouseReleased((int)ImGuiMouseButton.Left)) + if (!Ui.IsMouseButtonDown(ImGuiMouseButton.Left) && !Ui.IsMouseReleased(ImGuiMouseButton.Left)) { m_leftPointerActive = false; } - bool leftPressed = acceptsPointer && NativeImGui.IsMouseClicked((int)ImGuiMouseButton.Left); + + bool leftPressed = acceptsPointer && Ui.IsMouseClicked(ImGuiMouseButton.Left); if (leftPressed) m_leftPointerActive = true; - bool leftReleased = m_leftPointerActive && NativeImGui.IsMouseReleased((int)ImGuiMouseButton.Left); + bool leftReleased = m_leftPointerActive && Ui.IsMouseReleased(ImGuiMouseButton.Left); if (leftReleased || !focused) m_leftPointerActive = false; - var io = NativeImGui.Raw.GetIO(); + var io = UiDrawing.GetInput(); KeyModifier modifiers = KeyModifier.None; - if (io->KeyAlt != 0) + if (io.alt) modifiers |= KeyModifier.Alt; - if (io->KeyCtrl != 0) + if (io.control) modifiers |= KeyModifier.Control; - if (io->KeyShift != 0) + if (io.shift) modifiers |= KeyModifier.Shift; - if (io->KeySuper != 0) + if (io.super) modifiers |= KeyModifier.Super; - m_rendering.SetOutputInput(C_VIEWPORT_ID, focused ? new RenderOutputInput( - new Inno.Core.Mathematics.Vector2(local.X * layout.pixelWidth / layout.size.X, local.Y * layout.pixelHeight / layout.size.Y), - acceptsPointer, - acceptsPointer ? new Inno.Core.Mathematics.Vector2(0f, io->MouseWheel) : default, - modifiers, - [], [], - leftPressed ? [MouseButton.Left] : [], - leftReleased ? [MouseButton.Left] : [], - []) : RenderOutputInput.suspended); - if (!m_rendering.TrySubmit( - S_KIND, - C_VIEWPORT_ID, - layout.pixelWidth, - layout.pixelHeight, - out EditorViewportOutput output)) + m_rendering.SetOutputInput( + C_VIEWPORT_ID, + focused ? new RenderOutputInput( + new Inno.Core.Mathematics.Vector2(local.X * layout.pixelWidth / layout.size.X, local.Y * layout.pixelHeight / layout.size.Y), + acceptsPointer, + acceptsPointer ? new Inno.Core.Mathematics.Vector2(0f, io.wheel) : default, + modifiers, + [], + [], + leftPressed ? [MouseButton.Left] : [], + leftReleased ? [MouseButton.Left] : [], + [] + ) : RenderOutputInput.suspended + ); + if (!m_rendering.TrySubmit(S_KIND, C_VIEWPORT_ID, layout.pixelWidth, layout.pixelHeight, out EditorViewportOutput output)) { - DrawUnavailable( - available, - m_rendering.GetCompositionError(C_VIEWPORT_ID) ?? "No rendering model contributes to Game View."); + DrawUnavailable(available, m_rendering.GetCompositionError(C_VIEWPORT_ID) ?? "No rendering model contributes to Game View."); return; } + if (!output.isReady) { DrawUnavailable(available, "Preparing Game View GPU target..."); return; } - Vector2 origin = NativeImGui.GetCursorScreenPos(); + Vector2 origin = Ui.GetCursorScreenPosition(); if (m_presentation.preserveAspectRatio) { - EditorImGui.DrawFilledRectangle( - origin, - origin + available, - NativeImGui.ColorConvertFloat4ToU32(new Vector4(0f, 0f, 0f, EditorPalette.opacityOpaque)), - 0 - ); + EditorImGui.DrawFilledRectangle(origin, origin + available, Ui.ToPackedColor(new Vector4(0f, 0f, 0f, EditorPalette.opacityOpaque)), 0); } - NativeImGui.SetCursorScreenPos(origin + layout.offset); + + Ui.SetCursorScreenPosition(origin + layout.offset); m_rendering.Draw(output, layout.size); - NativeImGui.SetCursorScreenPos(origin); - NativeImGui.Dummy(available); + Ui.SetCursorScreenPosition(origin); + Ui.Dummy(available); } /// @@ -189,7 +169,7 @@ protected override void OnAttach(EditorContext context) _ = context; ApplyEditorSettings(m_editorSettings); RefreshPresentationSettings(); - m_editorSettings.changed += ApplyEditorSettings; + m_settingsSubscription = m_editorSettings.ListenChanges(ApplyEditorSettings); } /// @@ -201,14 +181,13 @@ protected override void OnAttach(EditorContext context) protected override void OnDetach(EditorContext context) { _ = context; - m_editorSettings.changed -= ApplyEditorSettings; + m_settingsSubscription?.Dispose(); m_rendering.Release(C_VIEWPORT_ID); m_presentationRevision = -1; m_leftPointerActive = false; } private void ApplyEditorSettings(EditorSettings settings) => m_backgroundColor = GameViewBackgroundSetting.Read(settings); - private void RefreshPresentationSettings() { if (m_presentationRevision == m_projectSettings.revision) @@ -224,37 +203,30 @@ float framebufferScale ) { int availableWidth = Math.Max(1, (int)MathF.Floor(available.X)); int availableHeight = Math.Max(1, (int)MathF.Floor(available.Y)); - GamePresentationViewport viewport = presentation.CalculateViewport( - availableWidth, - availableHeight); + GamePresentationViewport viewport = presentation.CalculateViewport(availableWidth, availableHeight); return new GameViewportLayout( new Vector2(viewport.x, viewport.y), new Vector2(viewport.width, viewport.height), Math.Max(1, (int)MathF.Ceiling(viewport.width * framebufferScale)), - Math.Max(1, (int)MathF.Ceiling(viewport.height * framebufferScale))); + Math.Max(1, (int)MathF.Ceiling(viewport.height * framebufferScale)) + ); } private ContentReadScope CreateContentScope() => m_scenePresentation.Capture(); - private void DrawUnavailable( Vector2 size, string message ) { - Vector2 minimum = NativeImGui.GetCursorScreenPos(); - EditorImGui.DrawFilledRectangle( - minimum, - minimum + size, - NativeImGui.ColorConvertFloat4ToU32(m_backgroundColor), - 0 - ); - NativeImGui.PushStyleColor((int)ImGuiCol.Text, EditorPalette.textDisabled); + Vector2 minimum = Ui.GetCursorScreenPosition(); + EditorImGui.DrawFilledRectangle(minimum, minimum + size, Ui.ToPackedColor(m_backgroundColor), 0); + Ui.PushStyleColor(ImGuiCol.Text, EditorPalette.textDisabled); try { - EditorWidget.CenteredWrappedText(message, size, S_UNAVAILABLE_PADDING); + EditorUI.current.widgets.CenteredWrappedText(message, size, S_UNAVAILABLE_PADDING); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj b/src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj index 0c8ceccf1..73db8d4b7 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/Inno.Editor.Panel.GameView.csproj @@ -4,38 +4,22 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - - + - - + + - - - + + + @@ -43,5 +27,4 @@ - diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj deleted file mode 100644 index 00c9aa675..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Inno.Editor.Panel.Global.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - net10.0 - 14.0 - disable - enable - true - true - $(WarningsAsErrors);CS1572;CS1573;CS1591 - Editor - - - - - - - - - - - - - - - - - diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/IconSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/IconSettings.cs deleted file mode 100644 index 4d95d8d33..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/IconSettings.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System.Runtime.CompilerServices; -using NumericsVector2 = System.Numerics.Vector2; -using NativeVector2 = Inno.Native.ImGui.ImVec2C; -using BGCS.Runtime; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Reflection; - -using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Editor.Settings; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Panel.Global; - -internal abstract unsafe class IconSetting : EditorSetting -{ - private static readonly IReadOnlyList<(string Name, string Glyph)> C_ICONS = - typeof(ImGuiIcon) - .GetFields(BindingFlags.Public | BindingFlags.Static) - .Where(static field => field.IsLiteral && field.FieldType == typeof(string)) - .Select(static field => (field.Name, (string)field.GetRawConstantValue()!)) - .OrderBy(static value => value.Name, StringComparer.Ordinal) - .ToArray(); - - private static int s_iconSlotFrame = -1; - private static float s_iconSlotWidth; - - private readonly string m_glyph; - - /// - /// Creates a validated icon setting instance. - /// - /// - /// The glyph text validated by the icon setting operation. - /// - protected IconSetting(string glyph) - { - ArgumentException.ThrowIfNullOrWhiteSpace(glyph); - m_glyph = glyph; - } - - /// - /// Gets a new value initialized to this setting's canonical default state. - /// - public override EditorSettingObject defaultValue => CreateDefault(m_glyph); - - /// - /// Gets the presentation section that groups this setting. - /// - public override string section => "Editor Icons"; - - /// - /// Renders this feature using the current editor presentation context. - /// - /// - /// The mutable editor setting value currently being presented. - /// - protected override void OnDraw(EditorSettingObject setting) - { - string value = setting.GetAsString("value", m_glyph) ?? m_glyph; - float iconSlotWidth = GetIconSlotWidth(); - ImDrawList* comboDrawList = NativeImGui.Raw.GetWindowDrawList(); - NativeImGui.SetNextItemWidth(-1f); - bool isOpen = EditorWidget.BeginBoundedCombo("##icon", string.Empty); - Vector2 comboMinimum = NativeImGui.GetItemRectMin(); - Vector2 comboMaximum = NativeImGui.GetItemRectMax(); - ImGuiStyle* comboStyle = NativeImGui.Raw.GetStyle(); - float comboLineHeight = NativeImGui.GetTextLineHeight(); - DrawIconTextAt( - comboDrawList, - new Vector2( - comboMinimum.X + comboStyle->FramePadding.X, - comboMinimum.Y + MathF.Max( - 0f, - (comboMaximum.Y - comboMinimum.Y - comboLineHeight) * 0.5f)), - value, - GetIconName(value), - iconSlotWidth); - if (isOpen) - { - try - { - for (int i = 0; i < C_ICONS.Count; i++) - { - (string name, string candidate) = C_ICONS[i]; - NativeImGui.PushID(name); - try - { - bool selected = string.Equals(candidate, value, StringComparison.Ordinal); - if (NativeImGui.Selectable( - "##icon_option", - selected, - (int)ImGuiSelectableFlags.None, - new Vector2(0f, NativeImGui.GetFrameHeight()))) - { - value = candidate; - setting.SetAsString("value", candidate); - } - Vector2 minimum = NativeImGui.GetItemRectMin(); - Vector2 maximum = NativeImGui.GetItemRectMax(); - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float lineHeight = NativeImGui.GetTextLineHeight(); - DrawIconTextAt( - NativeImGui.Raw.GetWindowDrawList(), - new Vector2( - minimum.X + style->FramePadding.X, - minimum.Y + MathF.Max(0f, (maximum.Y - minimum.Y - lineHeight) * 0.5f)), - candidate, - name, - iconSlotWidth); - if (selected) - NativeImGui.SetItemDefaultFocus(); - } - finally - { - NativeImGui.PopID(); - } - } - } - finally - { - EditorWidget.EndBoundedCombo(); - } - } - } - - private static EditorSettingObject CreateDefault(string glyph) - { - ArgumentException.ThrowIfNullOrWhiteSpace(glyph); - var result = new EditorSettingObject(); - result.SetAsString("value", glyph); - return result; - } - - private static float GetIconSlotWidth() - { - int frame = NativeImGui.GetFrameCount(); - if (s_iconSlotFrame == frame) - return s_iconSlotWidth; - - ImFont* font = NativeImGui.Raw.GetFont(); - float fontSize = NativeImGui.GetFontSize(); - float result = NativeImGui.GetTextLineHeight(); - for (int i = 0; i < C_ICONS.Count; i++) - { - Vector4 bounds = ImGuiWidget.GetGlyphVisualBounds(font, fontSize, C_ICONS[i].Glyph); - result = MathF.Max(result, bounds.Z - bounds.X); - } - s_iconSlotFrame = frame; - s_iconSlotWidth = result; - return s_iconSlotWidth; - } - - private static string GetIconName(string glyph) - { - for (int i = 0; i < C_ICONS.Count; i++) - { - if (string.Equals(C_ICONS[i].Glyph, glyph, StringComparison.Ordinal)) - return C_ICONS[i].Name; - } - return glyph; - } - - private static void DrawIconTextAt( - ImDrawList* drawList, - Vector2 origin, - string glyph, - string text, - float iconSlotWidth - ) { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - ImFont* font = NativeImGui.Raw.GetFont(); - float fontSize = NativeImGui.GetFontSize(); - float lineHeight = NativeImGui.GetTextLineHeight(); - uint color = NativeImGui.GetColorU32((int)ImGuiCol.Text); - ImGuiWidget.AddGlyphCentered( - drawList, - font, - fontSize, - glyph, - new Vector2(origin.X + iconSlotWidth * 0.5f, origin.Y + lineHeight * 0.5f), - color); - Inno.Editor.ImGui.ImGui.DrawText( - new Vector2(origin.X + iconSlotWidth + style->ItemInnerSpacing.X, origin.Y), - color, - text); - } -} - -[EditorSettingPath("Editor/Appearance/Icons/Scene")] -internal sealed unsafe class SceneIconSetting() : IconSetting(ImGuiIcon.Cubes) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a scene document or loaded scene."; -} - -[EditorSettingPath("Editor/Appearance/Icons/GameObject")] -internal sealed unsafe class GameObjectIconSetting() : IconSetting(ImGuiIcon.Cube) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a scene GameObject."; -} - -[EditorSettingPath("Editor/Appearance/Icons/Prefab")] -internal sealed unsafe class PrefabIconSetting() : IconSetting(ImGuiIcon.DiceD6) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a reusable prefab object."; -} - -[EditorSettingPath("Editor/Appearance/Icons/Layers")] -internal sealed unsafe class LayersIconSetting() : IconSetting(ImGuiIcon.LayerGroup) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a project layer configuration."; -} - -[EditorSettingPath("Editor/Appearance/Icons/Folder")] -internal sealed unsafe class FolderIconSetting() : IconSetting(ImGuiIcon.Folder) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a source directory."; -} - -[EditorSettingPath("Editor/Appearance/Icons/File")] -internal sealed unsafe class FileIconSetting() : IconSetting(ImGuiIcon.File) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// - public override string description => "Selects the icon used wherever the editor presents a generic source file."; -} - -[EditorSettingPath("Editor/Appearance/Icons/Shader")] -internal sealed unsafe class ShaderIconSetting() : IconSetting(ImGuiIcon.Shader) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// -public override string description => "Selects the icon used consistently for Shader graph assets and their Inspector headers."; -} - -[EditorSettingPath("Editor/Appearance/Icons/ShaderSource")] -internal sealed unsafe class ShaderSourceIconSetting() : IconSetting(ImGuiIcon.RupeeSign) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// -public override string description - => "Selects the icon used consistently for Shader source-function libraries. The default function symbol distinguishes reusable code from complete Shader graphs."; -} - -[EditorSettingPath("Editor/Appearance/Icons/Material")] -internal sealed unsafe class MaterialIconSetting() : IconSetting(ImGuiIcon.Palette) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// -public override string description => "Selects the icon used consistently for Material assets and their Inspector headers."; -} - -[EditorSettingPath("Editor/Appearance/Icons/RenderPipeline")] -internal sealed unsafe class RenderPipelineIconSetting() : IconSetting(ImGuiIcon.RupiahSign) -{ - /// - /// Gets the user-facing explanation of this feature or setting. - /// -public override string description => "Selects the icon used consistently for Render Pipeline assets and their Inspector headers."; -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj index 5432c5883..1c653775e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Inno.Editor.Panel.Hierarchy.csproj @@ -4,7 +4,6 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor @@ -13,38 +12,16 @@ - - + + - - - - - - - + + + + + - + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/DeleteGameObjectCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/DeleteGameObjectCommand.cs index 0e751a295..1f7a00942 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/DeleteGameObjectCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/DeleteGameObjectCommand.cs @@ -1,10 +1,11 @@ +using System.Collections.Generic; +using System.Linq; using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Core.Input; using Inno.Scene; namespace Inno.Editor.Panel.Hierarchy; - [EditorAction(HierarchyInteractionIds.C_DELETE_GAME_OBJECT, priority: 100)] [EditorMenu(HierarchyInteractionIds.C_AREA, "Delete", order: 300)] [EditorShortcut(HierarchyInteractionIds.C_AREA, KeyCode.Delete)] @@ -20,9 +21,17 @@ internal sealed class DeleteGameObjectCommand(SceneEdits edits) : EditorAction protected override EditorActionState Query(EditorActionContext context) - => context.target.isRuntimeValid - ? edits.CanEdit(context.target) ? EditorActionState.enabled : EditorActionState.disabled - : EditorActionState.hidden; + { + if (!context.target.isRuntimeValid) + return EditorActionState.hidden; + GameObject[] targets = Targets(context); + return targets.Length != 0 + && targets.All(edits.CanEdit) ? EditorActionState.enabled : new EditorActionState( + isVisible: true, + isEnabled: false, + unavailableReason: "Every selected object must belong to an editable scene." + ); + } /// /// Executes the prepared operation and publishes only a completed result. @@ -32,10 +41,16 @@ protected override EditorActionState Query(EditorActionContext conte /// protected override void Execute(EditorActionContext context) { - if (!context.target.isRuntimeValid) - return; - _ = edits.DeleteGameObject(context.target); - if (context.interactions.selection.TryGet(out GameObject? selected) && ReferenceEquals(selected, context.target)) - _ = context.interactions.For(context.area).Select(); + edits.DeleteGameObjects(Targets(context)); + context.interactions.SetSelection((object? )null); + } + + private static GameObject[] Targets(EditorActionContext context) + { + if (!context.interactions.selection.Contains(context.target)) + return[context.target]; + var values = new List(); + context.interactions.selection.CopyTargets(values); + return values.Count == context.interactions.selection.count ? values.ToArray() : []; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/MoveGameObjectToSceneDropHandler.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/MoveGameObjectToSceneDropHandler.cs index 3c7e30e60..fbb131719 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/MoveGameObjectToSceneDropHandler.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/MoveGameObjectToSceneDropHandler.cs @@ -1,15 +1,14 @@ using System; - +using System.Collections.Generic; +using System.Linq; using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Scene; namespace Inno.Editor.Panel.Hierarchy; - [EditorDrop(HierarchyInteractionIds.C_AREA)] -internal sealed class MoveGameObjectToSceneDropHandler(SceneEdits edits) - : EditorDrop +internal sealed class MoveGameObjectToSceneDropHandler(SceneEdits edits) : EditorDrop { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -22,12 +21,12 @@ internal sealed class MoveGameObjectToSceneDropHandler(SceneEdits edits) /// protected override EditorDropStatus Query(EditorDropContext context) { - GameObject source = context.source; + GameObject[] sources = SelectedSources(context); GameScene target = context.target.scene; - return source.isRuntimeValid && edits.CanEdit(source) && edits.CanEdit(target) - && target is { isLoaded: true, isDestroyed: false } - ? EditorDropStatus.Accept() - : EditorDropStatus.rejected; + return sources.Length > 0 + && sources.All(value => value.isRuntimeValid && edits.CanEdit(value)) + && edits.CanEdit(target) + && target is { isLoaded: true, isDestroyed: false } ? EditorDropStatus.Accept() : EditorDropStatus.rejected; } /// @@ -41,30 +40,18 @@ protected override EditorDropStatus Query(EditorDropContext protected override EditorDropResult Drop(EditorDropContext context) { - GameObject source = context.source; - GameScene target = context.target.scene; - _ = edits.ChangeHierarchy( - source, - hierarchy => - { - if (!ReferenceEquals(source.scene, target)) - hierarchy.MoveToScene(source, target); - else - source.transform.SetParent(null); - source.transform.SetSiblingIndex(GetRootCount(target) - 1); - }); - _ = context.interactions.For(context.area, source).Select(); - return EditorDropResult.Accepted(source, target); + if (!Query(context).canDrop) + return EditorDropResult.rejected; + edits.MoveGameObjects(SelectedSources(context), context.target.scene); + return EditorDropResult.Accepted(context.source, context.target); } - private static int GetRootCount(GameScene scene) + private static GameObject[] SelectedSources(EditorDropContext context) { - int count = 0; - foreach (GameObject gameObject in scene.GetObjects()) - { - if (gameObject.transform.parent is null) - count++; - } - return count; + if (!context.interactions.selection.Contains(context.source)) + return[context.source]; + var values = new List(); + context.interactions.selection.CopyTargets(values); + return values.Count == context.interactions.selection.count ? values.ToArray() : []; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/RenameHierarchyTargetCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/RenameHierarchyTargetCommand.cs index 6a21e0d37..5bae41e51 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/RenameHierarchyTargetCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/RenameHierarchyTargetCommand.cs @@ -1,25 +1,20 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - using Inno.Core.Input; using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Scene; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; namespace Inno.Editor.Panel.Hierarchy; - [EditorAction(HierarchyInteractionIds.C_RENAME, priority: 100)] [EditorMenu(HierarchyInteractionIds.C_AREA, "Rename", order: 200)] [EditorShortcut(HierarchyInteractionIds.C_AREA, KeyCode.F2)] -internal sealed class RenameHierarchyTargetCommand(SceneEdits edits) : - EditorPresentationAction +internal sealed class RenameHierarchyTargetCommand(SceneEdits edits) : EditorPresentationAction { - private EngineObject? m_target; private string m_buffer = string.Empty; private bool m_requestFocus; private string m_originalName = string.Empty; - /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -29,11 +24,7 @@ internal sealed class RenameHierarchyTargetCommand(SceneEdits edits) : /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => IsAvailable(context.target) - ? edits.CanEdit(context.target) ? EditorActionState.enabled : EditorActionState.disabled - : EditorActionState.hidden; - + protected override EditorActionState Query(EditorActionContext context) => IsAvailable(context.target) ? edits.CanEdit(context.target) ? EditorActionState.enabled : EditorActionState.disabled : EditorActionState.hidden; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -45,7 +36,6 @@ protected override void Execute(EditorActionContext context) if (!TryGetName(context.target, out string name)) return; Activate(context); - m_target = context.target; m_buffer = name; m_originalName = name; m_requestFocus = true; @@ -62,21 +52,23 @@ protected override void Execute(EditorActionContext context) /// protected override bool Present(EditorActionContext context) { - if (m_target is null) + if (activeTarget is not EngineObject target) return false; InlineRenamePresentation presentation = context.argument; - InlineRenameResult result = EditorWidget.InlineRename( + InlineRenameResult result = EditorUI.current.widgets.InlineRename( presentation.id, ref m_buffer, ref m_requestFocus, presentation.rowHeight, presentation.bufferSize, - presentation.width); + presentation.width + ); if (result == InlineRenameResult.Cancel) { Cancel(); return true; } + if (result is InlineRenameResult.FocusLost or InlineRenameResult.Commit) CommitName(); return true; @@ -86,34 +78,33 @@ protected override bool Present(EditorActionContext protected override void OnCompleted() => ClearState(); - /// /// Cancels the interaction without committing its pending value. /// protected override void OnCancelled() => ClearState(); - /// /// Cancels pending presentation state when its editor surface disappears. /// protected override void OnPresentationLost() { - if (m_target is null || !IsAvailable(m_target)) + if (activeTarget is not EngineObject target || !IsAvailable(target)) { Cancel(); return; } + CommitName(); } private void CommitName() { - if (m_target is null) + if (activeTarget is not EngineObject target) return; - string fallback = m_target is GameScene ? "Scene" : "GameObject"; + string fallback = target is GameScene ? "Scene" : "GameObject"; string name = string.IsNullOrWhiteSpace(m_buffer) ? fallback : m_buffer.Trim(); if (!string.Equals(m_originalName, name, StringComparison.Ordinal)) { - switch (m_target) + switch (target) { case GameScene scene: edits.RenameScene(scene, name); @@ -123,25 +114,23 @@ private void CommitName() break; } } + Complete(); } private void ClearState() { - m_target = null; m_buffer = string.Empty; m_requestFocus = false; m_originalName = string.Empty; } - private static bool IsAvailable(EngineObject target) - => target switch - { - GameScene scene => scene.isLoaded && !scene.isDestroyed, - GameObject gameObject => gameObject.isRuntimeValid, - _ => false - }; - + private static bool IsAvailable(EngineObject target) => target switch + { + GameScene scene => scene.isLoaded && !scene.isDestroyed, + GameObject gameObject => gameObject.isRuntimeValid, + _ => false + }; private static bool TryGetName( EngineObject target, out string name diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/ReparentGameObjectDropHandler.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/ReparentGameObjectDropHandler.cs index ee538f3c1..041664d6e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/ReparentGameObjectDropHandler.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/ReparentGameObjectDropHandler.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; - using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.Scene; @@ -9,10 +8,8 @@ using Inno.Scene.Components; namespace Inno.Editor.Panel.Hierarchy; - [EditorDrop(HierarchyInteractionIds.C_AREA)] -internal sealed class ReparentGameObjectDropHandler(SceneEdits edits) - : EditorDrop +internal sealed class ReparentGameObjectDropHandler(SceneEdits edits) : EditorDrop { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -25,10 +22,15 @@ internal sealed class ReparentGameObjectDropHandler(SceneEdits edits) /// protected override EditorDropStatus Query(EditorDropContext context) { - GameObject source = context.source; GameObject target = context.target.gameObject; - if (ReferenceEquals(source, target) || !source.isRuntimeValid || !target.isRuntimeValid - || !edits.CanEdit(source) || !edits.CanEdit(target)) + GameObject[] sources = SelectedSources(context); + if (sources.Length == 0 || !target.isRuntimeValid || !edits.CanEdit(target) || sources.Any(value => !value.isRuntimeValid || !edits.CanEdit(value))) + return EditorDropStatus.rejected; + Transform? parent = context.placement == EditorDropPlacement.Into ? target.transform : target.transform.parent; + for (Transform? ancestor = parent; ancestor is not null; ancestor = ancestor.parent) + if (sources.Any(value => ReferenceEquals(value.transform, ancestor))) + return EditorDropStatus.rejected; + if (sources.Contains(target)) return EditorDropStatus.rejected; return EditorDropStatus.Accept(context.placement switch { @@ -49,73 +51,30 @@ protected override EditorDropStatus Query(EditorDropContext protected override EditorDropResult Drop(EditorDropContext context) { - GameObject source = context.source; + if (!Query(context).canDrop) + return EditorDropResult.rejected; GameObject target = context.target.gameObject; - Transform sourceTransform = source.transform; - Transform targetTransform = target.transform; - GameObject[] relatedObjects = sourceTransform.children - .Select(static child => child.gameObject) - .ToArray(); - _ = edits.ChangeHierarchy( - source, - hierarchy => - { - if (!ReferenceEquals(source.scene, target.scene)) - hierarchy.MoveToScene(source, target.scene); - ApplyDrop(context, sourceTransform, targetTransform); - }, - "Reparent GameObject", - relatedObjects); - _ = context.interactions.For(context.area, source).Select(); - return EditorDropResult.Accepted(source, target); - } - - private static void ApplyDrop( - EditorDropContext context, - Transform sourceTransform, - Transform targetTransform - ) { - if (context.placement == EditorDropPlacement.Into) + GameObject[] sources = SelectedSources(context); + Transform? parent = context.placement == EditorDropPlacement.Into ? target.transform : target.transform.parent; + int insertionIndex = int.MaxValue; + if (context.placement != EditorDropPlacement.Into) { - if (IsDescendantOf(targetTransform, sourceTransform)) - PromoteDirectChildren(sourceTransform); - sourceTransform.SetParent(targetTransform); - sourceTransform.SetSiblingIndex(targetTransform.children.Count - 1); - return; + insertionIndex = target.transform.siblingIndex; + insertionIndex -= sources.Count(value => ReferenceEquals(value.transform.parent, parent) && ReferenceEquals(value.scene, target.scene) && value.transform.siblingIndex < target.transform.siblingIndex); + if (context.placement == EditorDropPlacement.After) + insertionIndex++; } - Transform? targetParent = targetTransform.parent; - sourceTransform.SetParent(targetParent); - int sourceIndex = sourceTransform.siblingIndex; - int targetIndex = targetTransform.siblingIndex; - if (sourceIndex < targetIndex) - targetIndex--; - sourceTransform.SetSiblingIndex( - targetIndex + (context.placement == EditorDropPlacement.After ? 1 : 0)); + edits.MoveGameObjects(sources, target.scene, parent?.gameObject, insertionIndex); + return EditorDropResult.Accepted(context.source, context.target); } - private static bool IsDescendantOf( - Transform transform, - Transform possibleAncestor - ) { - for (Transform? current = transform.parent; current is not null; current = current.parent) - { - if (ReferenceEquals(current, possibleAncestor)) - return true; - } - return false; - } - - private static void PromoteDirectChildren(Transform transform) + private static GameObject[] SelectedSources(EditorDropContext context) { - Transform? previousParent = transform.parent; - int insertionIndex = transform.siblingIndex; - List children = new(transform.children); - for (int i = 0; i < children.Count; i++) - { - Transform child = children[i]; - child.SetParent(previousParent); - child.SetSiblingIndex(insertionIndex + i); - } + if (!context.interactions.selection.Contains(context.source)) + return[context.source]; + var values = new List(); + context.interactions.selection.CopyTargets(values); + return values.Count == context.interactions.selection.count ? values.ToArray() : []; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/SaveCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/SaveCommand.cs index 285f94344..139fef8ab 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/SaveCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Interactions/SaveCommand.cs @@ -1,27 +1,21 @@ using System; using System.Collections.Generic; - using Inno.Core.Logging; using Inno.Core.Input; using Inno.Editor.Interactions; -using Inno.Editor.Panel.FileBrowser; using Inno.Editor.Scene; using Inno.Scene; namespace Inno.Editor.Panel.Hierarchy; - [EditorAction(HierarchyInteractionIds.C_SAVE)] [EditorMenu(HierarchyInteractionIds.C_MAIN_MENU_AREA, "File/Save", order: 100)] [EditorShortcut(KeyCode.S, primary: true)] internal sealed class SaveCommand( IEditorSceneWorkspace workspace, - AssetEditorModule assets, LogRouter logs ) : EditorAction { - private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))) - .CreateLogger(); - + private readonly Logger m_log = (logs ?? throw new ArgumentNullException(nameof(logs))).CreateLogger(); /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -31,11 +25,8 @@ LogRouter logs /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => workspace.canPersist && workspace.activeScene is not null - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => workspace.canPersist + && workspace.activeScene is not null ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -49,9 +40,7 @@ protected override void Execute(EditorActionContext context) IReadOnlyList scenes = workspace.scenes; for (int i = 0; i < scenes.Count; i++) { - _ = workspace.Save( - scenes[i], - assets.browser.projectDirectory); + _ = workspace.Save(scenes[i], context.editor.projectDirectory); } } catch (Exception exception) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyDropVisual.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyDropVisual.cs index 36adfca23..2c9913245 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyDropVisual.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyDropVisual.cs @@ -1,25 +1,19 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; - using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.Panel.Hierarchy; - -internal sealed unsafe class HierarchyDropVisual +internal sealed class HierarchyDropVisual { internal EditorDropPlacement GetScenePlacement( in TreeNodeResult result, float mouseY - ) - => mouseY >= (result.min.Y + result.max.Y) * 0.5f - ? EditorDropPlacement.After - : EditorDropPlacement.Before; - + ) => mouseY >= (result.min.Y + result.max.Y) * 0.5f ? EditorDropPlacement.After : EditorDropPlacement.Before; internal EditorDropPlacement GetObjectPlacement( in TreeNodeResult result, float mouseY @@ -38,18 +32,16 @@ EditorDropVisual visual switch (visual) { case EditorDropVisual.InsertBefore: - EditorWidget.InsertionLine(result.min.X, result.max.X, result.min.Y); + EditorUI.current.widgets.InsertionLine(result.min.X, result.max.X, result.min.Y); break; case EditorDropVisual.InsertAfter: - EditorWidget.InsertionLine(result.min.X, result.max.X, result.max.Y); + EditorUI.current.widgets.InsertionLine(result.min.X, result.max.X, result.max.Y); break; case EditorDropVisual.Highlight: { Vector2 highlightMin = result.contentMin; - highlightMin.X = MathF.Max( - result.min.X, - highlightMin.X - NativeImGui.Raw.GetStyle()->ItemInnerSpacing.X); - EditorWidget.DropTargetHighlight(highlightMin, result.max); + highlightMin.X = MathF.Max(result.min.X, highlightMin.X - UiDrawing.GetLayout().itemInnerSpacing.X); + EditorUI.current.widgets.DropTargetHighlight(highlightMin, result.max); break; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs index d2190d8da..6b26e4fab 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs @@ -1,7 +1,8 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Core.Identity; using Inno.Editor.Core; using Inno.Editor.Interactions; @@ -9,23 +10,20 @@ using Inno.Editor.Settings; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Scene; using Inno.Scene.Components; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Hierarchy; - /// /// Edits the active scene object hierarchy. /// [EditorPanel("scene.hierarchy", "Hierarchy", order: 100, menuPath: "Workspace")] internal sealed class HierarchyPanel : EditorPanel { + private readonly List m_visibleSelection = []; + private RuntimeIdentity? m_pendingSelection; private const nuint C_NAME_BUFFER_SIZE = 512; - private readonly IEditorSceneWorkspace m_workspace; private readonly EditorInteractions m_interactions; private readonly SceneEdits m_edits; @@ -38,12 +36,10 @@ internal sealed class HierarchyPanel : EditorPanel private readonly HashSet m_initializedSceneIds = []; private ulong m_lastRevealedSelectionVersion = ulong.MaxValue; private int m_visibleRowIndex; - /// /// Gets whether use window padding is enabled for this implementation. /// public override bool useWindowPadding => false; - /// /// Lets the hierarchy content child own both scroll directions. /// @@ -73,12 +69,7 @@ EditorSettings settings /// protected override void OnDraw(EditorContext context) { - bool visible = NativeImGui.BeginChild( - "##HierarchyContent", - Vector2.Zero, - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.NoSavedSettings | - ImGuiWindowFlags.HorizontalScrollbar)); + bool visible = Ui.BeginChild("##HierarchyContent", Vector2.Zero, (ImGuiChildFlags.None), (ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.HorizontalScrollbar)); try { if (visible) @@ -86,39 +77,43 @@ protected override void OnDraw(EditorContext context) } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } - private unsafe void DrawContent(EditorContext context) + private void DrawContent(EditorContext context) { m_drawnIds.Clear(); + m_visibleSelection.Clear(); + m_pendingSelection = null; m_visibleRowIndex = 0; m_selection.Prune(context); RevealSelection(); - - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.ItemSpacing), EditorWidget.style.hierarchyItemSpacing); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.compactFramePadding); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.hierarchyItemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.compactFramePadding); try { IReadOnlyList scenes = m_workspace.scenes; if (scenes.Count > 0) { - Vector2 contentOrigin = NativeImGui.GetCursorScreenPos(); - NativeImGui.SetCursorScreenPos(new Vector2( - contentOrigin.X, - contentOrigin.Y - NativeImGui.Raw.GetCurrentWindow()->WindowPadding.Y)); + Vector2 contentOrigin = Ui.GetCursorScreenPosition(); + Ui.SetCursorScreenPosition(new Vector2(contentOrigin.X, contentOrigin.Y - UiDrawing.GetWindowBounds().padding.Y)); } + for (int i = 0; i < scenes.Count; i++) { DrawScene(context, scenes[i]); } DrawBlankArea(context); + if (m_pendingSelection is RuntimeIdentity pending && m_interactions.TryResolveIdentity(pending, out IdentityObject? target) && target is not null) + EditorUI.current.widgets.SelectTarget(m_interactions, target, m_visibleSelection); } finally { - NativeImGui.PopStyleVar(2); + m_pendingSelection = null; + m_visibleSelection.Clear(); + Ui.PopStyleVar(2); } } @@ -132,64 +127,50 @@ GameScene scene shouldOpen |= m_forceOpenSceneIds.Remove(sceneId); if (shouldOpen) { - EditorWidget.SetNextTreeNodeOpen(true); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); } - bool selected = m_interactions.selection.TryGet(out GameScene? selectedScene) && - ReferenceEquals(selectedScene, scene); - TreeNodeResult result = EditorWidget.TreeNode( + m_visibleSelection.Add(scene); + bool selected = m_interactions.selection.Contains(scene); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( $"scene_{id}", drawContext => DrawSceneRowContent(context, scene, id, drawContext.rowHeight), - new TreeNodeOptions - { - isLeaf = false, - selected = selected, - showBackground = true, - backgroundColor = EditorPalette.hierarchySceneRow, - suppressHoverHighlight = false - }); + new TreeNodeOptions { isLeaf = false, selected = selected, showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow, suppressHoverHighlight = false } + ); try { + if (result.isClicked || result.isDoubleClicked) + m_pendingSelection = scene.identity.runtimeIdentity; + if (result.isDoubleClicked) + { + _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).Execute(HierarchyInteractionIds.C_RENAME); + } - if (result.isClicked || result.isDoubleClicked) - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).Select(); - - if (result.isDoubleClicked) - { - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene) - .Execute(HierarchyInteractionIds.C_RENAME); - } - - _ = EditorDragDropRenderer.Source( - m_interactions.For(HierarchyInteractionIds.C_AREA, scene), - new EditorDragData(scene, scene.name), - () => NativeImGui.TextUnformatted(scene.name)); - - EditorDropPlacement scenePlacement = m_dropVisual.GetScenePlacement(result, NativeImGui.GetMousePos().Y); - EditorDropWidgetResult sceneDrop = EditorDragDropRenderer.Target( - m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchySceneDropTarget(scene)), - scenePlacement); - if (sceneDrop.isPreviewing && sceneDrop.status.canDrop) - m_dropVisual.Draw(result, sceneDrop.status.visual); - ApplyDropResult(sceneDrop.result); - - DrawSceneContextMenu(context, scene, id); - if (!result.isOpen) - { - return; - } - - IReadOnlyList roots = m_selection.GetRootObjects(scene); - for (int i = 0; i < roots.Count; i++) - { - DrawObject(context, scene, roots[i]); - } + _ = EditorDragDropRenderer.Source(m_interactions.For(HierarchyInteractionIds.C_AREA, scene), new EditorDragData(scene, scene.name), () => Ui.Text(scene.name)); + EditorDropPlacement scenePlacement = m_dropVisual.GetScenePlacement(result, Ui.GetMousePos().Y); + EditorDropWidgetResult sceneDrop = EditorDragDropRenderer.Target( + m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchySceneDropTarget(scene)), + scenePlacement + ); + if (sceneDrop.isPreviewing && sceneDrop.status.canDrop) + m_dropVisual.Draw(result, sceneDrop.status.visual); + ApplyDropResult(sceneDrop.result); + DrawSceneContextMenu(context, scene, id); + if (!result.isOpen) + { + return; + } + IReadOnlyList roots = m_selection.GetRootObjects(scene); + for (int i = 0; i < roots.Count; i++) + { + DrawObject(context, scene, roots[i]); + } } finally { if (result.isOpen) - NativeImGui.TreePop(); + Ui.TreePop(); } } @@ -204,76 +185,71 @@ GameObject gameObject } Transform transform = gameObject.GetComponent(); - bool selected = m_interactions.selection.TryGet(out GameObject? selectedObject) - && ReferenceEquals(selectedObject, gameObject); + m_visibleSelection.Add(gameObject); + bool selected = m_interactions.selection.Contains(gameObject); string id = gameObject.identity.persistentId.ToString("N"); if (m_forceOpenIds.Remove(gameObject.identity.persistentId)) { - EditorWidget.SetNextTreeNodeOpen(true); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); } - TreeNodeResult result = EditorWidget.TreeNode( + TreeNodeResult result = EditorUI.current.widgets.TreeNode( $"hierarchy_{id}", drawContext => DrawRowContent(context, gameObject, drawContext.rowHeight), - new TreeNodeOptions - { + new TreeNodeOptions { selected = selected, isLeaf = transform.children.Count == 0, showBackground = true, - backgroundColor = m_visibleRowIndex % 2 == 0 - ? EditorPalette.collectionRow - : EditorPalette.collectionRowAlternate, + backgroundColor = m_visibleRowIndex % 2 == 0 ? EditorPalette.collectionRow : EditorPalette.collectionRowAlternate, drawViewportOverlay = () => DrawVisibilityButton(gameObject, id) - }); + } + ); m_visibleRowIndex++; try { + if (result.isClicked || result.isDoubleClicked) + { + m_pendingSelection = gameObject.identity.runtimeIdentity; + } - if (result.isClicked || result.isDoubleClicked) - { - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).Select(); - } - - if (result.isDoubleClicked) - { - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject) - .Execute(HierarchyInteractionIds.C_RENAME); - } - - _ = EditorDragDropRenderer.Source( - m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject), - new EditorDragData(gameObject, gameObject.name), - () => NativeImGui.TextUnformatted(gameObject.name)); - EditorDropPlacement placement = m_dropVisual.GetObjectPlacement(result, NativeImGui.GetMousePos().Y); - EditorDropWidgetResult objectDrop = EditorDragDropRenderer.Target( - m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchyObjectDropTarget(gameObject)), - placement); - if (objectDrop.isPreviewing && objectDrop.status.canDrop) - m_dropVisual.Draw(result, objectDrop.status.visual); - ApplyDropResult(objectDrop.result); - - DrawObjectContextMenu(context, gameObject, id); - - if (!result.isOpen) - { - return; - } + if (result.isDoubleClicked) + { + _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).Execute(HierarchyInteractionIds.C_RENAME); + } - IReadOnlyList children = transform.children; - for (int i = 0; i < children.Count; i++) - { - GameObject? child = children[i].gameObject; - if (child is not null) + _ = EditorDragDropRenderer.Source( + m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject), + new EditorDragData(gameObject, gameObject.name), + () => Ui.Text(gameObject.name) + ); + EditorDropPlacement placement = m_dropVisual.GetObjectPlacement(result, Ui.GetMousePos().Y); + EditorDropWidgetResult objectDrop = EditorDragDropRenderer.Target( + m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchyObjectDropTarget(gameObject)), + placement + ); + if (objectDrop.isPreviewing && objectDrop.status.canDrop) + m_dropVisual.Draw(result, objectDrop.status.visual); + ApplyDropResult(objectDrop.result); + DrawObjectContextMenu(context, gameObject, id); + if (!result.isOpen) { - DrawObject(context, scene, child); + return; } - } + IReadOnlyList children = transform.children; + for (int i = 0; i < children.Count; i++) + { + GameObject? child = children[i].gameObject; + if (child is not null) + { + DrawObject(context, scene, child); + } + } } finally { if (result.isOpen) - NativeImGui.TreePop(); + Ui.TreePop(); } } @@ -286,51 +262,34 @@ float rowHeight bool dimmed = !gameObject.activeInHierarchy; if (dimmed) { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.hierarchyInactiveText); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.hierarchyInactiveText); } + try { EditorInteraction interaction = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject); bool isRenaming = interaction.IsActive(HierarchyInteractionIds.C_RENAME); if (isRenaming) { - EditorWidget.IconText( - m_settings - .Get("Editor/Appearance/Icons/GameObject") - .GetAsString("value", ImGuiIcon.Cube)!, - string.Empty, - false); - NativeImGui.SameLine(0f, 0f); + EditorUI.current.widgets.IconText(m_settings.Get("Editor/Appearance/Icons/GameObject").GetAsString("value", ImGuiIcon.Cube)!, string.Empty, false); + Ui.SameLine(0f, 0f); float visibilityWidth = GetVisibilityButtonWidth(); float renameWidth = MathF.Max( - EditorWidget.style.hierarchyRenameMinimumWidth, - NativeImGui.GetContentRegionAvail().X - - visibilityWidth - - EditorWidget.style.hierarchyRenameTrailingGap); - _ = interaction.Present( - HierarchyInteractionIds.C_RENAME, - new InlineRenamePresentation( - $"hierarchy_{id}", - renameWidth, - rowHeight, - C_NAME_BUFFER_SIZE)); + EditorUI.current.widgets.style.hierarchyRenameMinimumWidth, + Ui.GetContentRegionAvailable().X - visibilityWidth - EditorUI.current.widgets.style.hierarchyRenameTrailingGap + ); + _ = interaction.Present(HierarchyInteractionIds.C_RENAME, new InlineRenamePresentation($"hierarchy_{id}", renameWidth, rowHeight, C_NAME_BUFFER_SIZE)); } else { - EditorWidget.IconText( - m_settings - .Get("Editor/Appearance/Icons/GameObject") - .GetAsString("value", ImGuiIcon.Cube)!, - gameObject.name, - false); + EditorUI.current.widgets.IconText(m_settings.Get("Editor/Appearance/Icons/GameObject").GetAsString("value", ImGuiIcon.Cube)!, gameObject.name, false); } } finally { if (dimmed) - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } - } private void DrawSceneRowContent( @@ -346,57 +305,44 @@ float rowHeight return; } - EditorWidget.IconText( - m_settings - .Get("Editor/Appearance/Icons/Scene") - .GetAsString("value", ImGuiIcon.Cubes)!, + EditorUI.current.widgets.IconText( + m_settings.Get("Editor/Appearance/Icons/Scene").GetAsString("value", ImGuiIcon.Cubes)!, string.Empty, - ReferenceEquals(scene, m_workspace.activeScene)); - NativeImGui.SameLine(0f, 0f); + ReferenceEquals(scene, m_workspace.activeScene) + ); + Ui.SameLine(0f, 0f); float renameWidth = MathF.Max( - EditorWidget.style.hierarchyRenameMinimumWidth, - NativeImGui.GetContentRegionAvail().X - - EditorWidget.style.hierarchyRenameTrailingGap); - _ = interaction.Present( - HierarchyInteractionIds.C_RENAME, - new InlineRenamePresentation( - $"scene_{id}", - renameWidth, - rowHeight, - C_NAME_BUFFER_SIZE)); + EditorUI.current.widgets.style.hierarchyRenameMinimumWidth, + Ui.GetContentRegionAvailable().X - EditorUI.current.widgets.style.hierarchyRenameTrailingGap + ); + _ = interaction.Present(HierarchyInteractionIds.C_RENAME, new InlineRenamePresentation($"scene_{id}", renameWidth, rowHeight, C_NAME_BUFFER_SIZE)); } - private unsafe void DrawVisibilityButton( + private void DrawVisibilityButton( GameObject gameObject, string id ) { string icon = gameObject.activeSelf ? ImGuiIcon.Eye : ImGuiIcon.EyeSlash; float buttonWidth = GetVisibilityButtonWidth(); - float right = NativeImGui.Raw.GetCurrentWindow()->WorkRect.Max.X - - EditorWidget.style.windowPadding.X - - buttonWidth; - NativeImGui.SameLine(0f, -1f); - Vector2 cursor = NativeImGui.GetCursorScreenPos(); - NativeImGui.SetCursorScreenPos(new Vector2(MathF.Max(cursor.X, right), cursor.Y)); - if (EditorWidget.ClickableIcon($"hierarchy_visibility_{id}", icon, - gameObject.activeSelf ? "Deactivate" : "Activate")) + float right = UiDrawing.GetWindowBounds().workMaximum.X - EditorUI.current.widgets.style.windowPadding.X - buttonWidth; + Ui.SameLine(0f, -1f); + Vector2 cursor = Ui.GetCursorScreenPosition(); + Ui.SetCursorScreenPosition(new Vector2(MathF.Max(cursor.X, right), cursor.Y)); + if (EditorUI.current.widgets.ClickableIcon($"hierarchy_visibility_{id}", icon, gameObject.activeSelf ? "Deactivate" : "Activate")) { m_edits.SetGameObjectActive(gameObject, !gameObject.activeSelf); } } - private static float GetVisibilityButtonWidth() => EditorWidget.GetCompactIconSize().X; - + private static float GetVisibilityButtonWidth() => EditorUI.current.widgets.GetCompactIconSize().X; private void DrawSceneContextMenu( EditorContext context, GameScene scene, string id ) { - if (NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Right))) - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).Select(); - _ = EditorMenuRenderer.ContextMenu( - $"##scene_context_{id}", - m_interactions.For(HierarchyInteractionIds.C_AREA, scene)); + if (Ui.IsItemClicked(ImGuiMouseButton.Right) && !m_interactions.selection.Contains(scene)) + m_interactions.SetSelection(scene); + _ = EditorUI.current.menus.ContextMenu($"##scene_context_{id}", m_interactions.For(HierarchyInteractionIds.C_AREA, scene)); } private void DrawObjectContextMenu( @@ -404,21 +350,17 @@ private void DrawObjectContextMenu( GameObject gameObject, string id ) { - if (NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Right))) - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).Select(); - _ = EditorMenuRenderer.ContextMenu( - $"##hierarchy_context_{id}", - m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject)); + if (Ui.IsItemClicked(ImGuiMouseButton.Right) && !m_interactions.selection.Contains(gameObject)) + m_interactions.SetSelection(gameObject); + _ = EditorUI.current.menus.ContextMenu($"##hierarchy_context_{id}", m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject)); } private void DrawBlankArea(EditorContext context) { - Vector2 available = NativeImGui.GetContentRegionAvail(); - Vector2 size = new( - MathF.Max(1f, available.X), - MathF.Max(EditorWidget.style.hierarchyBlankMinimumHeight, available.Y)); - _ = NativeImGui.InvisibleButton("##hierarchy_blank", size); - if (NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Left))) + Vector2 available = Ui.GetContentRegionAvailable(); + Vector2 size = new(MathF.Max(1f, available.X), MathF.Max(EditorUI.current.widgets.style.hierarchyBlankMinimumHeight, available.Y)); + _ = Ui.InvisibleButton("##hierarchy_blank", size); + if (Ui.IsItemClicked((ImGuiMouseButton.Left))) { _ = m_interactions.For(HierarchyInteractionIds.C_AREA).Select(); } @@ -427,18 +369,16 @@ private void DrawBlankArea(EditorContext context) if (activeScene is not null) { EditorDropWidgetResult drop = EditorDragDropRenderer.Target( - m_interactions.For( - HierarchyInteractionIds.C_AREA, - new HierarchySceneDropTarget(activeScene)), - EditorDropPlacement.Into); + m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchySceneDropTarget(activeScene)), + EditorDropPlacement.Into + ); ApplyDropResult(drop.result); } - _ = EditorMenuRenderer.ContextMenu( + _ = EditorUI.current.menus.ContextMenu( "##hierarchy_blank_context", - activeScene is null - ? m_interactions.For(HierarchyInteractionIds.C_AREA) - : m_interactions.For(HierarchyInteractionIds.C_AREA, activeScene)); + activeScene is null ? m_interactions.For(HierarchyInteractionIds.C_AREA) : m_interactions.For(HierarchyInteractionIds.C_AREA, activeScene) + ); } private void RevealSelection() @@ -447,7 +387,6 @@ private void RevealSelection() if (m_lastRevealedSelectionVersion == selectionVersion) return; m_lastRevealedSelectionVersion = selectionVersion; - if (!m_interactions.selection.TryGet(out GameObject? gameObject) || !gameObject.isRuntimeValid) return; m_forceOpenSceneIds.Add(gameObject.scene.identity.persistentId); @@ -471,5 +410,4 @@ private void ApplyDropResult(EditorDropResult result) m_forceOpenIds.Add(gameObject.identity.persistentId); } } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchySelection.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchySelection.cs index 9ed4bced2..374d203a9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchySelection.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchySelection.cs @@ -1,7 +1,6 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Core.Identity; using Inno.Editor.Core; using Inno.Editor.Interactions; @@ -9,28 +8,25 @@ using Inno.Editor.Settings; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Scene; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Hierarchy; - internal sealed class HierarchySelection( IEditorSceneWorkspace workspace, EditorInteractions interactions, EditorSettings settings -) { +) +{ internal void Prune(EditorContext context) { - if (interactions.selection.TryGet(out GameScene? selectedScene) && - (!selectedScene.isLoaded || !ContainsScene(workspace.scenes, selectedScene))) + if (interactions.selection.TryGet(out GameScene? selectedScene) && (!selectedScene.isLoaded || !ContainsScene(workspace.scenes, selectedScene))) { _ = interactions.For(HierarchyInteractionIds.C_AREA).Select(); return; } - if (interactions.selection.TryGet(out GameObject? gameObject) && - (!gameObject.isRuntimeValid || !ContainsScene(workspace.scenes, gameObject.scene))) + + if (interactions.selection.TryGet(out GameObject? gameObject) && (!gameObject.isRuntimeValid || !ContainsScene(workspace.scenes, gameObject.scene))) _ = interactions.For(HierarchyInteractionIds.C_AREA).Select(); } @@ -41,7 +37,6 @@ Guid persistentId GameObject? gameObject = IdentityAllocator.current.Get(persistentId); if (gameObject is null || !gameObject.isRuntimeValid || !ContainsScene(workspace.scenes, gameObject.scene)) return false; - _ = gameObject.scene.DestroyObject(gameObject); if (interactions.selection.TryGet(out GameObject? selected) && ReferenceEquals(selected, gameObject)) _ = interactions.For(HierarchyInteractionIds.C_AREA).Select(); @@ -57,6 +52,7 @@ GameScene scene if (ReferenceEquals(scenes[i], scene)) return true; } + return false; } @@ -69,6 +65,7 @@ internal IReadOnlyList GetRootObjects(GameScene scene) if (objects[i].transform.parent is null) roots.Add(objects[i]); } + roots.Sort(static ( left, right @@ -80,16 +77,15 @@ internal void DrawSceneRowContent( EditorContext context, GameScene scene ) { - EditorWidget.IconText( - settings - .Get("Editor/Appearance/Icons/Scene") - .GetAsString("value", ImGuiIcon.Cubes)!, + EditorUI.current.widgets.IconText( + settings.Get("Editor/Appearance/Icons/Scene").GetAsString("value", ImGuiIcon.Cubes)!, scene.name, - ReferenceEquals(scene, workspace.activeScene)); + ReferenceEquals(scene, workspace.activeScene) + ); if (!workspace.IsDirty(scene)) return; - NativeImGui.SameLine(0f, 0f); + Ui.SameLine(0f, 0f); using ImGuiFontScope font = ImGuiFont.PushStyle(ImGuiFontStyle.Italic); - NativeImGui.TextUnformatted(" *"); + Ui.Text(" *"); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj index 67876fa28..ae4a0bb24 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Inno.Editor.Panel.Inspector.csproj @@ -4,62 +4,32 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - + + + + - + - + - - - - - - - - - - - + + + + + + + + + - + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/AddComponentCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/AddComponentCommand.cs index d23ea7452..ce240b94a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/AddComponentCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/AddComponentCommand.cs @@ -1,16 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Inno.Extensibility.Types; using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Scene; namespace Inno.Editor.Panel.Inspector; - [EditorAction(InspectorInteractionIds.C_ADD_COMPONENT, InspectorInteractionIds.C_COMPONENT_AREA)] internal sealed class AddComponentCommand( SceneEdits edits, TypeCatalog types -) - : EditorAction +) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -21,11 +21,9 @@ TypeCatalog types /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => context.target.isRuntimeValid && context.argument.IsValid(types) - ? EditorActionState.enabled - : EditorActionState.hidden; - + protected override EditorActionState Query(EditorActionContext context) => Targets(context).Count > 0 + && Targets(context).All(value => value.isRuntimeValid && edits.CanEdit(value)) + && context.argument.IsValid(types) ? EditorActionState.enabled : EditorActionState.hidden; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -33,5 +31,19 @@ protected override EditorActionState Query(EditorActionContext protected override void Execute(EditorActionContext context) - => _ = edits.AddComponent(context.target, context.argument.Resolve(types)); + { + using EditorHistoryTransaction transaction = context.interactions.history.BeginTransaction("Add Components"); + foreach (GameObject target in Targets(context)) + _ = edits.AddComponent(target, context.argument.Resolve(types)); + transaction.Commit(); + } + + private static IReadOnlyList Targets(EditorActionContext context) + { + if (!context.interactions.selection.Contains(context.target)) + return[context.target]; + var targets = new List(); + context.interactions.selection.CopyTargets(targets); + return targets.Count == context.interactions.selection.count ? targets : []; + } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveComponentCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveComponentCommand.cs index 2b5926c7a..f3ce5e32a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveComponentCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveComponentCommand.cs @@ -1,12 +1,12 @@ +using Inno.Scene; using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Scene.Components; namespace Inno.Editor.Panel.Inspector; - [EditorAction(InspectorInteractionIds.C_REMOVE_COMPONENT, priority: 100)] [EditorMenu(InspectorInteractionIds.C_COMPONENT_AREA, "Remove Component", order: 200)] -internal sealed class RemoveComponentCommand(SceneEdits edits) : EditorAction +internal sealed class RemoveComponentCommand(SceneEdits edits) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -17,20 +17,16 @@ internal sealed class RemoveComponentCommand(SceneEdits edits) : EditorAction /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => context.target.component is not Transform - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => context.target is not Transform ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { - if (context.target.component is not Transform) - _ = edits.RemoveComponent(context.target.component); + if (context.target is not Transform) + _ = edits.RemoveComponent(context.target); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveSystemCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveSystemCommand.cs index e1e0ec380..57ec6d7ce 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveSystemCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/RemoveSystemCommand.cs @@ -1,11 +1,11 @@ +using Inno.Scene; using Inno.Editor.Interactions; using Inno.Editor.Scene; namespace Inno.Editor.Panel.Inspector; - [EditorAction(InspectorInteractionIds.C_REMOVE_SYSTEM, priority: 100)] [EditorMenu(InspectorInteractionIds.C_SYSTEM_AREA, "Remove System", order: 200)] -internal sealed class RemoveSystemCommand(SceneEdits edits) : EditorAction +internal sealed class RemoveSystemCommand(SceneEdits edits) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -16,17 +16,12 @@ internal sealed class RemoveSystemCommand(SceneEdits edits) : EditorAction /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => !context.target.system.isDestroyed - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => !context.target.isDestroyed ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) - => _ = edits.RemoveSystem(context.target.scene, context.target.system); + protected override void Execute(EditorActionContext context) => _ = edits.RemoveSystem(context.target); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetComponentCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetComponentCommand.cs index 0fb6a052a..4c900abca 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetComponentCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetComponentCommand.cs @@ -3,10 +3,9 @@ using Inno.Scene; namespace Inno.Editor.Panel.Inspector; - [EditorAction(InspectorInteractionIds.C_RESET_COMPONENT, priority: 100)] [EditorMenu(InspectorInteractionIds.C_COMPONENT_AREA, "Reset Component", order: 100)] -internal sealed class ResetComponentCommand(SceneEdits edits) : EditorAction +internal sealed class ResetComponentCommand(SceneEdits edits) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -17,16 +16,13 @@ internal sealed class ResetComponentCommand(SceneEdits edits) : EditorAction /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => !context.target.component.isDestroyed && context.target.component is not MissingGameComponent - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => !context.target.isDestroyed + && context.target is not MissingGameComponent ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) => edits.ResetComponent(context.target.component); + protected override void Execute(EditorActionContext context) => edits.ResetComponent(context.target); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetSystemCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetSystemCommand.cs index 4c058124d..0f7b5c51d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetSystemCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Interactions/ResetSystemCommand.cs @@ -3,10 +3,9 @@ using Inno.Scene; namespace Inno.Editor.Panel.Inspector; - [EditorAction(InspectorInteractionIds.C_RESET_SYSTEM, priority: 100)] [EditorMenu(InspectorInteractionIds.C_SYSTEM_AREA, "Reset System", order: 100)] -internal sealed class ResetSystemCommand(SceneEdits edits) : EditorAction +internal sealed class ResetSystemCommand(SceneEdits edits) : EditorAction { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -17,17 +16,13 @@ internal sealed class ResetSystemCommand(SceneEdits edits) : EditorAction /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => !context.target.system.isDestroyed && context.target.system is not MissingGameSystem - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => !context.target.isDestroyed + && context.target is not MissingGameSystem ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) - => edits.ResetSystem(context.target.scene, context.target.system); + protected override void Execute(EditorActionContext context) => edits.ResetSystem(context.target); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs index 913ef6807..ae5d6a0b6 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs @@ -1,29 +1,26 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Numerics; - using Inno.Core.Settings; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; using Inno.Scene.Layers; -using Inno.Native.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [ProjectSettingPath("Project/Scene/Layers")] -internal sealed unsafe class GameLayersSetting : ProjectSettingEditor +internal sealed class GameLayersSetting : ProjectSettingEditor { private const int C_LAYER_NAME_BUFFER_SIZE = 128; - private readonly string[] m_nameBuffers = new string[GameLayer.C_MAX_COUNT]; private string m_error = string.Empty; - private string?[]? m_observedNames; - + private string? []? m_observedNames; /// /// Gets the stable project-setting identity used for discovery and persistence. /// @@ -31,12 +28,10 @@ internal sealed unsafe class GameLayersSetting : ProjectSettingEditor public override string section => "Definitions"; - /// /// Gets the user-facing explanation of this feature or setting. /// - public override string description - => "Define layer names and slots. Stable IDs are generated as projectId.name and are never authored manually."; + public override string description => "Define layer names and slots. Stable IDs are generated as projectId.name and are never authored manually."; /// /// Draws this feature using the current editor presentation context. @@ -46,46 +41,33 @@ public override string description /// protected override void OnDraw(GameLayerCatalog setting) { - string?[] names = CaptureNames(setting); + string? [] names = CaptureNames(setting); SynchronizeBuffers(names); DrawLayerRows(setting, names); if (!string.IsNullOrEmpty(m_error)) - ImGuiWidget.ColoredText(EditorPalette.error, m_error); + EditorUI.current.widgets.ColoredText(EditorPalette.error, m_error); } private void DrawLayerRows( GameLayerCatalog setting, - string?[] names + string? [] names ) { int definedCount = names.Count(static name => !string.IsNullOrWhiteSpace(name)); DrawLayerToolbar(setting, names, definedCount); - NativeImGui.Spacing(); - - ImGuiTableFlags flags = ImGuiTableFlags.RowBg | - ImGuiTableFlags.BordersInnerH | - ImGuiTableFlags.BordersInnerV | - ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoSavedSettings; - NativeImGui.PushStyleVar((int)ImGuiStyleVar.CellPadding, ImGuiWidget.style.cellPadding); - if (!NativeImGui.BeginTable("##game_layer_definitions", 3, (int)flags, default(System.Numerics.Vector2), 0f)) + Ui.Spacing(); + ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerH | ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoSavedSettings; + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, EditorUI.current.widgets.style.cellPadding); + if (!Ui.BeginTable("##game_layer_definitions", 3, flags, default(System.Numerics.Vector2), 0f)) { - NativeImGui.PopStyleVar(); + Ui.PopStyleVar(); return; } + try { - NativeImGui.TableSetupColumn( - "Slot", - (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), - 48f * ImGuiWidget.style.zoom); - NativeImGui.TableSetupColumn( - "Name", - (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize)); - NativeImGui.TableSetupColumn( - "Action", - (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), - 72f * ImGuiWidget.style.zoom); + Ui.TableSetupColumn("Slot", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), 48f * EditorUI.current.widgets.style.zoom); + Ui.TableSetupColumn("Name", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize)); + Ui.TableSetupColumn("Action", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), 72f * EditorUI.current.widgets.style.zoom); DrawLayerTableHeader(); for (int index = 0; index < GameLayer.C_MAX_COUNT; index++) { @@ -95,8 +77,8 @@ private void DrawLayerRows( } finally { - NativeImGui.EndTable(); - NativeImGui.PopStyleVar(); + Ui.EndTable(); + Ui.PopStyleVar(); } } @@ -106,23 +88,23 @@ private void DrawLayerRow( int index ) { var layer = new GameLayer(index); - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); - NativeImGui.AlignTextToFramePadding(); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); + Ui.AlignTextToFramePadding(); InsetPlainCell(); - NativeImGui.TextUnformatted(index.ToString("00", CultureInfo.InvariantCulture)); - _ = NativeImGui.TableSetColumnIndex(1); + Ui.Text(index.ToString("00", CultureInfo.InvariantCulture)); + _ = Ui.TableSetColumnIndex(1); if (layer == GameLayer.defaultLayer) { InsetPlainCell(); - NativeImGui.TextUnformatted(names[index] ?? "Default"); - _ = NativeImGui.TableSetColumnIndex(2); + Ui.Text(names[index] ?? "Default"); + _ = Ui.TableSetColumnIndex(2); InsetPlainCell(); - ImGuiWidget.ColoredText(EditorPalette.textDisabled, "Fixed"); + EditorUI.current.widgets.ColoredText(EditorPalette.textDisabled, "Fixed"); return; } - NativeImGui.PushID(index); + Ui.PushId(index); try { string name = m_nameBuffers[index]; @@ -130,17 +112,10 @@ int index m_nameBuffers[index] = name; if (committed) CommitDefinition(setting, layer, name); - - _ = NativeImGui.TableSetColumnIndex(2); + _ = Ui.TableSetColumnIndex(2); InsetPlainCell(); - Vector2 removeSize = new( - NativeImGui.CalcTextSize("Remove").X, - NativeImGui.GetFrameHeight()); - if (ImGuiWidget.ClickableText( - $"remove_layer_{index}", - "Remove", - removeSize, - "Remove this definition while preserving Scene slot assignments.")) + Vector2 removeSize = new(Ui.MeasureText("Remove").X, Ui.GetFrameHeight()); + if (EditorUI.current.widgets.ClickableText($"remove_layer_{index}", "Remove", removeSize, "Remove this definition while preserving Scene slot assignments.")) { _ = setting.Remove(layer); SynchronizeSlot(setting, layer); @@ -149,7 +124,7 @@ int index } finally { - NativeImGui.PopID(); + Ui.PopId(); } } @@ -158,36 +133,31 @@ private static bool DrawTextField( string hint, ref string value ) { - NativeImGui.SetNextItemWidth(-1f); - NativeImGui.PushStyleColor((int)ImGuiCol.FrameBg, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.FrameBgHovered, EditorPalette.transparent); - NativeImGui.PushStyleColor((int)ImGuiCol.FrameBgActive, EditorPalette.transparent); + Ui.SetNextItemWidth(-1f); + Ui.PushStyleColor(ImGuiCol.FrameBg, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.FrameBgHovered, EditorPalette.transparent); + Ui.PushStyleColor(ImGuiCol.FrameBgActive, EditorPalette.transparent); try { - bool submitted = EditorImGui.InputTextWithHint( - id, - hint, - ref value, - C_LAYER_NAME_BUFFER_SIZE, - ImGuiInputTextFlags.EnterReturnsTrue); - return submitted || NativeImGui.IsItemDeactivatedAfterEdit(); + bool submitted = EditorImGui.InputTextWithHint(id, hint, ref value, C_LAYER_NAME_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); + return submitted || Ui.IsItemDeactivatedAfterEdit(); } finally { - NativeImGui.PopStyleColor(3); + Ui.PopStyleColor(3); } } private void DrawLayerToolbar( GameLayerCatalog setting, - string?[] names, + string? [] names, int definedCount ) { - float spacing = NativeImGui.Raw.GetStyle()->ItemSpacing.X; - ImGuiWidget.LabelChip("Defined", EditorPalette.collectionRowAlternate); - NativeImGui.SameLine(0f, 0f); - DrawReadOnlyCount($"{definedCount} / {GameLayer.C_MAX_COUNT}", 78f * ImGuiWidget.style.zoom); - NativeImGui.SameLine(0f, spacing); + float spacing = UiDrawing.GetLayout().itemSpacing.X; + EditorUI.current.widgets.LabelChip("Defined", EditorPalette.collectionRowAlternate); + Ui.SameLine(0f, 0f); + DrawReadOnlyCount($"{definedCount} / {GameLayer.C_MAX_COUNT}", 78f * EditorUI.current.widgets.style.zoom); + Ui.SameLine(0f, spacing); DrawAddLayer(setting, names, definedCount); } @@ -195,35 +165,23 @@ private static void DrawReadOnlyCount( string value, float width ) { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - Vector2 minimum = NativeImGui.GetCursorScreenPos(); - Vector2 size = new(MathF.Max(1f, width), NativeImGui.GetFrameHeight()); - NativeImGui.Dummy(size); + EditorUiLayout style = UiDrawing.GetLayout(); + Vector2 minimum = Ui.GetCursorScreenPosition(); + Vector2 size = new(MathF.Max(1f, width), Ui.GetFrameHeight()); + Ui.Dummy(size); Vector2 maximum = minimum + size; - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - EditorImGui.DrawFilledRectangle( - minimum, - maximum, - NativeImGui.GetColorU32((int)ImGuiCol.FrameBg), - style->FrameRounding - ); - EditorImGui.DrawRectangle( - minimum, - maximum, - NativeImGui.GetColorU32((int)ImGuiCol.Border), - style->FrameRounding, - ImGuiWidget.style.borderSize - ); - Vector2 textSize = NativeImGui.CalcTextSize(value); - EditorImGui.DrawText(minimum + (size - textSize) * 0.5f, NativeImGui.GetColorU32((int)ImGuiCol.Text), value); + EditorImGui.DrawFilledRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.FrameBg), style.frameRounding); + EditorImGui.DrawRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.Border), style.frameRounding, EditorUI.current.widgets.style.borderSize); + Vector2 textSize = Ui.MeasureText(value); + EditorImGui.DrawText(minimum + (size - textSize) * 0.5f, Ui.GetStyleColor(ImGuiCol.Text), value); } private static void DrawLayerTableHeader() { - uint background = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionHeader); - NativeImGui.TableNextRow(); - NativeImGui.TableSetBgColor((int)ImGuiTableBgTarget.RowBg0, background); - NativeImGui.TableSetBgColor((int)ImGuiTableBgTarget.RowBg1, background); + uint background = Ui.ToPackedColor(EditorPalette.collectionHeader); + Ui.TableNextRow(); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, background); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, background); DrawHeaderCell(0, "Slot"); DrawHeaderCell(1, "Name"); DrawHeaderCell(2, "Action"); @@ -233,32 +191,26 @@ private static void DrawHeaderCell( int column, string label ) { - _ = NativeImGui.TableSetColumnIndex(column); + _ = Ui.TableSetColumnIndex(column); InsetPlainCell(); - NativeImGui.TextUnformatted(label); + Ui.Text(label); } - private static void InsetPlainCell() - => NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + NativeImGui.Raw.GetStyle()->FramePadding.X); - + private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().framePadding.X); private void DrawAddLayer( GameLayerCatalog setting, - string?[] names, + string? [] names, int definedCount ) { - NativeImGui.BeginDisabled(definedCount >= GameLayer.C_MAX_COUNT); + Ui.BeginDisabled(definedCount >= GameLayer.C_MAX_COUNT); try { - float selectorWidth = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); - if (!ImGuiWidget.BeginMenuSelector( - "add_game_layer", - "Add layer...", - selectorWidth, - selectorWidth)) + float selectorWidth = MathF.Max(1f, Ui.GetContentRegionAvailable().X); + if (!EditorUI.current.widgets.BeginMenuSelector("add_game_layer", "Add layer...", selectorWidth, selectorWidth)) { return; } + try { for (int index = 1; index < GameLayer.C_MAX_COUNT; index++) @@ -266,18 +218,18 @@ int definedCount if (!string.IsNullOrWhiteSpace(names[index])) continue; string slot = index.ToString("00", CultureInfo.InvariantCulture); - if (NativeImGui.Selectable($"{slot} Layer {index}")) + if (Ui.Selectable($"{slot} Layer {index}")) CommitDefinition(setting, new GameLayer(index), $"Layer {index}"); } } finally { - ImGuiWidget.EndMenuSelector(); + EditorUI.current.widgets.EndMenuSelector(); } } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } } @@ -301,11 +253,11 @@ string name private void SynchronizeBuffers(IReadOnlyList names) { - if (m_observedNames is not null - && m_observedNames.SequenceEqual(names, StringComparer.Ordinal)) + if (m_observedNames is not null && m_observedNames.SequenceEqual(names, StringComparer.Ordinal)) { return; } + for (int index = 0; index < GameLayer.C_MAX_COUNT; index++) m_nameBuffers[index] = names[index] ?? string.Empty; m_observedNames = names.ToArray(); @@ -319,9 +271,9 @@ GameLayer layer m_observedNames = CaptureNames(setting); } - private static string?[] CaptureNames(GameLayerCatalog stack) + private static string? [] CaptureNames(GameLayerCatalog stack) { - var result = new string?[GameLayer.C_MAX_COUNT]; + var result = new string? [GameLayer.C_MAX_COUNT]; for (int index = 0; index < GameLayer.C_MAX_COUNT; index++) result[index] = stack.GetName(new GameLayer(index)); return result; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs index fe2149682..d9c672f74 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs @@ -1,16 +1,16 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - +using System.Linq; +using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Scene; using Inno.Scene; using Inno.Scene.Layers; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - /// /// Draws the project layer picker used by GameObject target headers. /// @@ -18,7 +18,6 @@ internal sealed class GameObjectLayerSelector { private readonly SceneProjectSettingsModule m_settings; private readonly SceneEdits m_edits; - /// /// Creates a layer selector backed by the project Settings layer catalog. /// @@ -29,7 +28,7 @@ internal sealed class GameObjectLayerSelector /// The Scene editing service used to record GameObject layer changes. /// /// - /// Thrown when or is . + /// Thrown when or is . /// internal GameObjectLayerSelector( SceneProjectSettingsModule settings, @@ -42,27 +41,33 @@ SceneEdits edits /// /// Draws a compact layer selector with an explicit width. /// - /// + /// /// The GameObject whose layer should be displayed and edited. /// + /// + /// The authoritative shared History owner. + /// /// /// The width reserved for the combo control. /// internal void Draw( - GameObject target, + IReadOnlyList targets, + EditorInteractions interactions, float width ) { - ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(targets); + if (targets.Count == 0) + return; + GameObject target = targets[0]; + bool mixed = targets.Any(value => value.layer != target.layer); GameLayerCatalog stack = m_settings.layerStack; string preview = FormatLayerPreview(stack.GetName(target.layer)); - ImGuiWidget.LabelChip("Layer", EditorPalette.inspectorLayerLabel); - NativeImGui.SameLine(0f, 0f); + if (mixed) + preview = "Mixed values"; + EditorUI.current.widgets.LabelChip("Layer", EditorPalette.inspectorLayerLabel); + Ui.SameLine(0f, 0f); float selectorWidth = MathF.Max(1f, width); - if (!ImGuiWidget.BeginMenuSelector( - $"game_object_layer_{target.identity.persistentId:N}", - preview, - selectorWidth, - selectorWidth)) + if (!EditorUI.current.widgets.BeginMenuSelector($"game_object_layer_{target.identity.persistentId:N}", preview, selectorWidth, selectorWidth)) { return; } @@ -74,13 +79,18 @@ float width { GameLayerDefinition definition = definitions[i]; string label = FormatLayerLabel(definition.layer, definition.name); - if (NativeImGui.Selectable(label, definition.layer == target.layer)) - m_edits.SetGameObjectLayer(target, definition.layer); + if (Ui.Selectable(label, definition.layer == target.layer)) + { + using EditorHistoryTransaction transaction = interactions.history.BeginTransaction("Set Selection Layer"); + foreach (GameObject selected in targets) + m_edits.SetGameObjectLayer(selected, definition.layer); + transaction.Commit(); + } } } finally { - ImGuiWidget.EndMenuSelector(); + EditorUI.current.widgets.EndMenuSelector(); } } @@ -94,11 +104,9 @@ float width /// when the layer can be selected. /// internal bool IsLayerDefined(GameLayer layer) => m_settings.layerStack.IsDefined(layer); - private static string FormatLayerLabel( GameLayer layer, string name ) => $"({layer.index}) {name}"; - private static string FormatLayerPreview(string? name) => name ?? "Undefined"; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs index 15a7c364a..2723a735f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs @@ -1,38 +1,30 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Numerics; -using BGCS.Runtime; - -using Inno.Adapter.Presentation.ImGui; using Inno.Core.Identity; using Inno.Core.Logging; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Interactions; using Inno.Editor.Scene; -using Inno.Native.ImGui; using Inno.Scene; using Inno.Scene.Components; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - -internal sealed unsafe class InspectorCardControls +internal sealed class InspectorCardControls { private const string C_COMPONENT_PAYLOAD = "INNO_INSPECTOR_COMPONENT"; private const string C_SYSTEM_PAYLOAD = "INNO_INSPECTOR_SYSTEM"; - private readonly Logger m_log; - internal InspectorCardControls(LogRouter logs) { ArgumentNullException.ThrowIfNull(logs); m_log = logs.CreateLogger(); } - internal float GetWidth(bool canRemove) => EditorWidget.GetCompactClickableTextSize().X * (canRemove ? 2f : 1f); - + internal float GetWidth(bool canRemove) => EditorUI.current.widgets.GetCompactClickableTextSize().X * (canRemove ? 2f : 1f); internal void DrawComponent( SceneEdits edits, GameComponent component, @@ -43,21 +35,15 @@ Action requestRemove ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(requestRemove); Guid componentId = component.identity.persistentId; - if (EditorWidget.ClickableText( - $"reset_Component_{componentId:N}", - ImGuiIcon.ArrowRotateLeft, - "Reset Component")) + if (EditorUI.current.widgets.ClickableText($"reset_Component_{componentId:N}", ImGuiIcon.ArrowRotateLeft, "Reset Component")) { TryEdit(() => edits.ResetComponent(component), "Component reset"); } + if (!canRemove) return; - - NativeImGui.SameLine(0f, 0f); - if (EditorWidget.ClickableText( - $"remove_Component_{componentId:N}", - ImGuiIcon.Xmark, - "Remove Component")) + Ui.SameLine(0f, 0f); + if (EditorUI.current.widgets.ClickableText($"remove_Component_{componentId:N}", ImGuiIcon.Xmark, "Remove Component")) { requestRemove(); } @@ -74,18 +60,13 @@ Action requestRemove ArgumentNullException.ThrowIfNull(system); ArgumentNullException.ThrowIfNull(requestRemove); Guid systemId = system.identity.persistentId; - if (EditorWidget.ClickableText( - $"reset_System_{systemId:N}", - ImGuiIcon.ArrowRotateLeft, - "Reset System")) + if (EditorUI.current.widgets.ClickableText($"reset_System_{systemId:N}", ImGuiIcon.ArrowRotateLeft, "Reset System")) { - TryEdit(() => edits.ResetSystem(owner, system), "System reset"); + TryEdit(() => edits.ResetSystem(system), "System reset"); } - NativeImGui.SameLine(0f, 0f); - if (EditorWidget.ClickableText( - $"remove_System_{systemId:N}", - ImGuiIcon.Xmark, - "Remove System")) + + Ui.SameLine(0f, 0f); + if (EditorUI.current.widgets.ClickableText($"remove_System_{systemId:N}", ImGuiIcon.Xmark, "Remove System")) { requestRemove(); } @@ -100,11 +81,7 @@ bool dimmed ArgumentNullException.ThrowIfNull(title); if (component.identity.runtimeIdentity is not RuntimeIdentity identity) return; - _ = EditorWidget.DragDropSource( - C_COMPONENT_PAYLOAD, - identity, - () => DrawDragPreview(title, dimmed), - allowHoldToOpenOthers: false); + _ = EditorUI.current.widgets.DragDropSource(C_COMPONENT_PAYLOAD, identity, () => DrawDragPreview(title, dimmed), allowHoldToOpenOthers: false); } internal void DrawSystemDragSource( @@ -116,11 +93,7 @@ bool dimmed ArgumentNullException.ThrowIfNull(title); if (system.identity.runtimeIdentity is not RuntimeIdentity identity) return; - _ = EditorWidget.DragDropSource( - C_SYSTEM_PAYLOAD, - identity, - () => DrawDragPreview(title, dimmed), - allowHoldToOpenOthers: false); + _ = EditorUI.current.widgets.DragDropSource(C_SYSTEM_PAYLOAD, identity, () => DrawDragPreview(title, dimmed), allowHoldToOpenOthers: false); } internal void DrawComponentDropTarget( @@ -134,29 +107,25 @@ Vector2 cardMaximum ArgumentNullException.ThrowIfNull(interactions); ArgumentNullException.ThrowIfNull(edits); ArgumentNullException.ThrowIfNull(target); - DrawDropTarget( - C_COMPONENT_PAYLOAD, - $"component_{target.identity.persistentId:N}", - interactions, - cardMinimum, - cardMaximum, - source => source is GameComponent component && - ReferenceEquals(component.gameObject, target.gameObject) && - !ReferenceEquals(component, target), - ( - source, - insertAfter - ) => - { - var component = (GameComponent)source; - int sourceIndex = component.gameObject.GetComponentIndex(component); - int insertionBoundary = targetIndex + (insertAfter ? 1 : 0); - int destinationIndex = insertionBoundary > sourceIndex - ? insertionBoundary - 1 - : insertionBoundary; - edits.SetComponentIndex(component, destinationIndex); - }, - "Component reorder"); + DrawDropTarget(C_COMPONENT_PAYLOAD, $"component_{target.identity.persistentId:N}", interactions, cardMinimum, cardMaximum, source => source is GameComponent component + && ReferenceEquals( + component.gameObject, + target.gameObject + ) + && !ReferenceEquals( + component, + target + ), ( + source, + insertAfter + ) => + { + var component = (GameComponent)source; + int sourceIndex = component.gameObject.GetComponentIndex(component); + int insertionBoundary = targetIndex + (insertAfter ? 1 : 0); + int destinationIndex = insertionBoundary > sourceIndex ? insertionBoundary - 1 : insertionBoundary; + edits.SetComponentIndex(component, destinationIndex); + }, "Component reorder"); } internal void DrawSystemDropTarget( @@ -172,29 +141,25 @@ Vector2 cardMaximum ArgumentNullException.ThrowIfNull(edits); ArgumentNullException.ThrowIfNull(scene); ArgumentNullException.ThrowIfNull(target); - DrawDropTarget( - C_SYSTEM_PAYLOAD, - $"system_{target.identity.persistentId:N}", - interactions, - cardMinimum, - cardMaximum, - source => source is GameSystem system && - !ReferenceEquals(system, target) && - IsOwnedBy(scene, system), - ( - source, - insertAfter - ) => - { - var system = (GameSystem)source; - int sourceIndex = scene.GetSystemIndex(system); - int insertionBoundary = targetIndex + (insertAfter ? 1 : 0); - int destinationIndex = insertionBoundary > sourceIndex - ? insertionBoundary - 1 - : insertionBoundary; - edits.SetSystemIndex(scene, system, destinationIndex); - }, - "System reorder"); + DrawDropTarget(C_SYSTEM_PAYLOAD, $"system_{target.identity.persistentId:N}", interactions, cardMinimum, cardMaximum, source => source is GameSystem system + && !ReferenceEquals( + system, + target + ) + && IsOwnedBy( + scene, + system + ), ( + source, + insertAfter + ) => + { + var system = (GameSystem)source; + int sourceIndex = scene.GetSystemIndex(system); + int insertionBoundary = targetIndex + (insertAfter ? 1 : 0); + int destinationIndex = insertionBoundary > sourceIndex ? insertionBoundary - 1 : insertionBoundary; + edits.SetSystemIndex(scene, system, destinationIndex); + }, "System reorder"); } private void DrawDropTarget( @@ -207,38 +172,32 @@ private void DrawDropTarget( Action move, string operation ) { - ImGuiPayload* activePayload = NativeImGui.Raw.GetDragDropPayload(); - using NativeStringLease nativeType = Utf8Codec.EncodeNullTerminated(payloadType, allowsNull: false); - if ((activePayload == null) || - NativeImGui.Raw.IsDataType(activePayload, (sbyte*)nativeType.pointer) == 0 || - cardMaximum.X <= cardMinimum.X || - cardMaximum.Y <= cardMinimum.Y) + if (!Inno.Editor.ImGui.ImGui.HasDragPayload(payloadType) || cardMaximum.X <= cardMinimum.X || cardMaximum.Y <= cardMinimum.Y) { return; } - uint targetId = NativeImGui.GetID($"##inspector_card_drop_{id}"); - bool delivered = EditorWidget.DragDropTarget( + uint targetId = Ui.GetControlId($"##inspector_card_drop_{id}"); + bool delivered = EditorUI.current.widgets.DragDropTarget( payloadType, cardMinimum, cardMaximum, targetId, out RuntimeIdentity identity, out bool isPreviewing, - drawDefaultHighlight: false); + drawDefaultHighlight: false + ); if (!isPreviewing && !delivered) return; - if (!interactions.TryResolveIdentity(identity, out IdentityObject? source) || - source is null || - !accepts(source)) + if (!interactions.TryResolveIdentity(identity, out IdentityObject? source) || source is null || !accepts(source)) { return; } - bool insertAfter = NativeImGui.GetMousePos().Y >= (cardMinimum.Y + cardMaximum.Y) * 0.5f; + bool insertAfter = Ui.GetMousePos().Y >= (cardMinimum.Y + cardMaximum.Y) * 0.5f; float markerY = insertAfter ? cardMaximum.Y : cardMinimum.Y; if (isPreviewing) - EditorWidget.InsertionLine(cardMinimum.X, cardMaximum.X, markerY); + EditorUI.current.widgets.InsertionLine(cardMinimum.X, cardMaximum.X, markerY); if (delivered) TryEdit(() => move(source, insertAfter), operation); } @@ -247,29 +206,20 @@ private static void DrawDragPreview( string title, bool dimmed ) { - Vector2 origin = NativeImGui.GetCursorScreenPos(); - Vector2 padding = EditorWidget.style.inspectorCardHeaderPadding; - Vector2 gripSize = NativeImGui.CalcTextSize(ImGuiIcon.GripVertical); - Vector2 titleSize = NativeImGui.CalcTextSize(title); + Vector2 origin = Ui.GetCursorScreenPosition(); + Vector2 padding = EditorUI.current.widgets.style.inspectorCardHeaderPadding; + Vector2 gripSize = Ui.MeasureText(ImGuiIcon.GripVertical); + Vector2 titleSize = Ui.MeasureText(title); Vector2 size = new( - padding.X * 2f + gripSize.X + EditorWidget.style.inspectorHeaderControlSpacing + titleSize.X, - MathF.Max(gripSize.Y, titleSize.Y) + padding.Y * 2f); - ImDrawList* draw = NativeImGui.Raw.GetWindowDrawList(); - EditorImGui.DrawFilledRectangle( - origin, - origin + size, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardHeader), - EditorWidget.style.frameRounding + padding.X * 2f + gripSize.X + EditorUI.current.widgets.style.inspectorHeaderControlSpacing + titleSize.X, + MathF.Max(gripSize.Y, titleSize.Y) + padding.Y * 2f ); - uint color = NativeImGui.ColorConvertFloat4ToU32( - dimmed ? EditorPalette.inspectorCardDisabledText : EditorPalette.text); + EditorImGui.DrawFilledRectangle(origin, origin + size, Ui.ToPackedColor(EditorPalette.inspectorCardHeader), EditorUI.current.widgets.style.frameRounding); + uint color = Ui.ToPackedColor(dimmed ? EditorPalette.inspectorCardDisabledText : EditorPalette.text); Vector2 textOrigin = origin + padding; EditorImGui.DrawText(textOrigin, color, ImGuiIcon.GripVertical); - EditorImGui.DrawText( - new Vector2(textOrigin.X + gripSize.X + EditorWidget.style.inspectorHeaderControlSpacing, textOrigin.Y), - color, - title); - NativeImGui.Dummy(size); + EditorImGui.DrawText(new Vector2(textOrigin.X + gripSize.X + EditorUI.current.widgets.style.inspectorHeaderControlSpacing, textOrigin.Y), color, title); + Ui.Dummy(size); } private void TryEdit( diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorPanel.cs index 643702398..aa7077e08 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorPanel.cs @@ -1,21 +1,18 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; - using Inno.Core.Logging; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Inspection; using Inno.Editor.Interactions; using Inno.Editor.PlayMode; using Inno.Editor.Scene; using Inno.Scene; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - /// /// Draws the registered inspector for the current editor selection. /// @@ -29,12 +26,10 @@ internal sealed class InspectorPanel : EditorPanel private readonly InspectorTargetHeader m_targetHeader; private readonly Logger m_log; private string m_failureState = string.Empty; - /// /// Gets whether use window padding is enabled for this implementation. /// public override bool useWindowPadding => false; - /// /// Lets the inspector content child own vertical scrolling. /// @@ -59,7 +54,7 @@ internal sealed class InspectorPanel : EditorPanel /// The application log router used for inspector presentation failures. /// /// - /// Thrown when or is . + /// Thrown when or is . /// internal InspectorPanel( SceneInspectionModule inspection, @@ -85,82 +80,63 @@ LogRouter logs /// protected override void OnDraw(EditorContext context) { - bool visible = NativeImGui.BeginChild( - "##InspectorScrollRegion", - Vector2.Zero, - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.NoSavedSettings)); + bool visible = Ui.BeginChild("##InspectorScrollRegion", Vector2.Zero, (ImGuiChildFlags.None), (ImGuiWindowFlags.NoSavedSettings)); try { if (visible) { - EditorWidget.ConstrainedContent( - "##InspectorContent", - () => DrawContent(context)); + EditorUI.current.widgets.ConstrainedContent("##InspectorContent", () => DrawContent(context)); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } private void DrawContent(EditorContext context) { - object? target = m_targetHeader.Resolve(m_interactions, m_interactions.selection.selectedTarget); + object? target = m_targetHeader.Resolve(m_interactions, m_interactions.selection.count > 1 ? m_interactions.selection.Capture() : m_interactions.selection.selectedTarget); if (target is null) { m_failureState = string.Empty; - EditorWidget.Hint("Select an asset or scene object."); + EditorUI.current.widgets.Hint("Select an asset or scene object."); return; } - if (!m_inspection.TryResolve( - context, - target, - out IInspectionDrawer? drawer, - out InspectionDrawContext? drawContext) || - drawer is null || - drawContext is null) + if (!m_inspection.TryResolve(context, target, out IInspectionDrawer? drawer, out InspectionDrawContext? drawContext) || drawer is null || drawContext is null) { m_failureState = string.Empty; - EditorWidget.Hint($"No inspector drawer is registered for {target.GetType().Name}."); + EditorUI.current.widgets.Hint($"No inspector drawer is registered for {target.GetType().Name}."); return; } bool sceneTarget = target is EngineObject; bool readOnlyScene = target is EngineObject sceneObject && !m_sceneEdits.CanEdit(sceneObject); - string? notice = readOnlyScene - ? "Read-only scene source. Import it into Project Assets to edit." - : sceneTarget && m_playMode.isPlaying - ? "Play Mode edits are temporary. Stopping Play restores the Edit scene." - : null; + string? notice = readOnlyScene ? "Read-only scene source. Import it into Project Assets to edit." : sceneTarget + && m_playMode.isPlaying ? "Play Mode edits are temporary. Stopping Play restores the Edit scene." : null; try { m_targetHeader.Draw(drawer, drawContext, readOnlyScene, notice); - NativeImGui.BeginDisabled(readOnlyScene); + Ui.BeginDisabled(readOnlyScene); try { - EditorWidget.SectionLayout(() => drawer.Draw(drawContext)); + EditorUI.current.widgets.SectionLayout(() => drawer.Draw(drawContext)); } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } + m_failureState = string.Empty; } catch (Exception exception) { - EditorWidget.ColoredText( - EditorPalette.error, - $"Inspector failed: {exception.Message}"); + EditorUI.current.widgets.ColoredText(EditorPalette.error, $"Inspector failed: {exception.Message}"); string failureState = $"{target.GetType().FullName}:{exception}"; if (!string.Equals(m_failureState, failureState, StringComparison.Ordinal)) { - m_log.Write( - LogLevel.Error, - "Inspector failed for target '{0}': {1}", - [target.GetType().FullName ?? target.GetType().Name, exception]); + m_log.Write(LogLevel.Error, "Inspector failed for target '{0}': {1}", [target.GetType().FullName ?? target.GetType().Name, exception]); m_failureState = failureState; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs index a25e2e9e7..b41e2cda6 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs @@ -1,30 +1,22 @@ -using System.Runtime.CompilerServices; -using NumericsVector2 = System.Numerics.Vector2; -using NativeVector2 = Inno.Native.ImGui.ImVec2C; +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; - using Inno.Editor.Inspection; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Interactions; using EditorImGui = Inno.Editor.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - /// /// Draws the persistent identity header shared by every Inspector target type. /// -internal sealed unsafe class InspectorTargetHeader +internal sealed class InspectorTargetHeader { private const int C_NAME_BUFFER_SIZE = 512; - private readonly InspectorLockControl m_lock = new(); - /// /// Resolves the target that should be presented after applying Inspector lock state. /// @@ -41,7 +33,6 @@ internal sealed unsafe class InspectorTargetHeader EditorInteractions interactions, object? selectedTarget ) => m_lock.Resolve(interactions, selectedTarget); - /// /// Draws the common framed header for a resolved Inspector target. /// @@ -65,59 +56,53 @@ internal void Draw( ) { ArgumentNullException.ThrowIfNull(drawer); ArgumentNullException.ThrowIfNull(context); - EditorWidget.HeaderSurface( - "##inspector_target_header", - () => + EditorUI.current.widgets.HeaderSurface("##inspector_target_header", () => + { + Ui.BeginDisabled(readOnly); + try { - NativeImGui.BeginDisabled(readOnly); - try - { - DrawContent(drawer, context); - } - finally - { - NativeImGui.EndDisabled(); - } - if (notice is null) - return; - NativeImGui.Spacing(); - EditorWidget.HelpBox( - notice, - ImGuiIcon.CircleInfo, - new Vector4(0.42f, 0.66f, 0.88f, EditorPalette.opacityOpaque)); - }, - spanWindowPadding: true); + DrawContent(drawer, context); + } + finally + { + Ui.EndDisabled(); + } + + if (notice is null) + return; + Ui.Spacing(); + EditorUI.current.widgets.HelpBox(notice, ImGuiIcon.CircleInfo, new Vector4(0.42f, 0.66f, 0.88f, EditorPalette.opacityOpaque)); + }, spanWindowPadding: true); } private void DrawContent( IInspectionDrawer drawer, InspectionDrawContext context ) { - NativeImGui.PushStyleVar((int)ImGuiStyleVar.ItemSpacing, EditorWidget.style.compactItemSpacing); - NativeImGui.PushStyleVar((int)ImGuiStyleVar.FramePadding, EditorWidget.style.compactFramePadding); + Ui.PushStyleVar(ImGuiStyleVar.ItemSpacing, EditorUI.current.widgets.style.compactItemSpacing); + Ui.PushStyleVar(ImGuiStyleVar.FramePadding, EditorUI.current.widgets.style.compactFramePadding); try { - float rowHeight = NativeImGui.GetFrameHeight(); - float rowSpacing = EditorWidget.style.inspectorTargetHeaderRowSpacing; + float rowHeight = Ui.GetFrameHeight(); + float rowSpacing = EditorUI.current.widgets.style.inspectorTargetHeaderRowSpacing; float headerContentHeight = rowHeight * 2f + rowSpacing; DrawIcon(drawer.GetIcon(context), headerContentHeight); - NativeImGui.SameLine(0f, -1f); - - NativeImGui.BeginGroup(); + Ui.SameLine(0f, -1f); + Ui.BeginGroup(); try { DrawNameRow(drawer, context, rowHeight); - NativeImGui.SetCursorPosY(NativeImGui.GetCursorPosY() + rowSpacing); + Ui.SetCursorPosY(Ui.GetCursorPosY() + rowSpacing); DrawCustomRow(drawer, context, rowHeight); } finally { - NativeImGui.EndGroup(); + Ui.EndGroup(); } } finally { - NativeImGui.PopStyleVar(2); + Ui.PopStyleVar(2); } } @@ -126,53 +111,42 @@ private void DrawNameRow( InspectionDrawContext context, float rowHeight ) { - Vector2 lockSize = EditorWidget.GetCompactIconSize(); - float itemSpacing = NativeImGui.Raw.GetStyle()->ItemSpacing.X; - float nameWidth = MathF.Max( - 1f, - NativeImGui.GetContentRegionAvail().X - lockSize.X - itemSpacing); + Vector2 lockSize = EditorUI.current.widgets.GetCompactIconSize(); + float itemSpacing = UiDrawing.GetLayout().itemSpacing.X; + float nameWidth = MathF.Max(1f, Ui.GetContentRegionAvailable().X - lockSize.X - itemSpacing); (string name, Action? nameSetter) = drawer.BindName(context); if (nameSetter is not null) { - NativeImGui.SetNextItemWidth(nameWidth); - if (EditorImGui.InputText( - $"##inspector_target_name_{GetTargetId(context.target)}", - ref name, - C_NAME_BUFFER_SIZE, - ImGuiInputTextFlags.None)) + Ui.SetNextItemWidth(nameWidth); + if (EditorImGui.InputText($"##inspector_target_name_{GetTargetId(context.target)}", ref name, C_NAME_BUFFER_SIZE, ImGuiInputTextFlags.None)) { nameSetter(name); } - NativeImGui.SameLine(0f, itemSpacing); + + Ui.SameLine(0f, itemSpacing); } else { - NativeImGui.AlignTextToFramePadding(); - Vector2 textOrigin = NativeImGui.GetCursorScreenPos(); - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - NativeVector2 clipMinimum = Unsafe.BitCast(textOrigin); - NativeVector2 clipMaximum = Unsafe.BitCast(textOrigin + new Vector2(nameWidth, rowHeight)); - NativeImGui.Raw.PushClipRect( - drawList, - &clipMinimum, - &clipMaximum, - 1 - ); + Ui.AlignTextToFramePadding(); + Vector2 textOrigin = Ui.GetCursorScreenPosition(); + EditorDrawList drawList = EditorImGui.GetDrawList(); + drawList.PushClip(textOrigin, textOrigin + new Vector2(nameWidth, rowHeight)); try { - EditorImGui.DrawText(textOrigin, NativeImGui.GetColorU32((int)ImGuiCol.Text), name); + EditorImGui.DrawText(textOrigin, Ui.GetStyleColor(ImGuiCol.Text), name); } finally { - NativeImGui.Raw.PopClipRect(drawList); + drawList.PopClip(); } - NativeImGui.Dummy(new Vector2(nameWidth, rowHeight)); - NativeImGui.SameLine(0f, itemSpacing); + + Ui.Dummy(new Vector2(nameWidth, rowHeight)); + Ui.SameLine(0f, itemSpacing); } string lockIcon = m_lock.isLocked ? ImGuiIcon.Lock : ImGuiIcon.LockOpen; string tooltip = m_lock.isLocked ? "Unlock Inspector" : "Lock Inspector"; - if (EditorWidget.ClickableIcon("inspector_target_lock", lockIcon, tooltip)) + if (EditorUI.current.widgets.ClickableIcon("inspector_target_lock", lockIcon, tooltip)) m_lock.Toggle(context.interactions, context.target); } @@ -180,15 +154,14 @@ private static void DrawIcon( string icon, float slotSize ) { - Vector2 minimum = NativeImGui.GetCursorScreenPos(); - EditorWidget.AddGlyphCentered( - NativeImGui.Raw.GetWindowDrawList(), - NativeImGui.Raw.GetFont(), - NativeImGui.GetFontSize() * EditorWidget.style.inspectorTargetIconScale, + Vector2 minimum = Ui.GetCursorScreenPosition(); + EditorUI.current.widgets.AddGlyphCentered( + Ui.GetFontSize() * EditorUI.current.widgets.style.inspectorTargetIconScale, icon, minimum + new Vector2(slotSize * 0.5f), - NativeImGui.GetColorU32((int)ImGuiCol.Text)); - NativeImGui.Dummy(new Vector2(slotSize)); + Ui.GetStyleColor(ImGuiCol.Text) + ); + Ui.Dummy(new Vector2(slotSize)); } private static void DrawCustomRow( @@ -196,14 +169,8 @@ private static void DrawCustomRow( InspectionDrawContext context, float rowHeight ) { - ImGuiWindowFlags flags = ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoScrollWithMouse | - ImGuiWindowFlags.NoSavedSettings; - bool visible = NativeImGui.BeginChild( - $"##inspector_target_custom_{GetTargetId(context.target)}", - new Vector2(0f, rowHeight), - (int)ImGuiChildFlags.None, - (int)flags); + ImGuiWindowFlags flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; + bool visible = Ui.BeginChild($"##inspector_target_custom_{GetTargetId(context.target)}", new Vector2(0f, rowHeight), ImGuiChildFlags.None, flags); try { if (visible) @@ -211,10 +178,9 @@ float rowHeight } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } - private static string GetTargetId(object target) - => $"{target.GetType().FullName}_{System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(target)}"; + private static string GetTargetId(object target) => $"{target.GetType().FullName}_{System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(target)}"; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs index 850dc2423..b8c39018b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs @@ -1,27 +1,25 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Reflection; - using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Extensibility.Modules; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - internal static class InspectorTypeOrigin { internal static void Draw(Type type) { ArgumentNullException.ThrowIfNull(type); - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.assetBreadcrumbText); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetBreadcrumbText); try { - ImGuiWidget.WrappedText("Source: " + Describe(type)); + EditorUI.current.widgets.WrappedText("Source: " + Describe(type)); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/AssetReferencePropertyDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/AssetReferencePropertyDrawer.cs index 45603e72d..6734731c7 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/AssetReferencePropertyDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/AssetReferencePropertyDrawer.cs @@ -1,10 +1,9 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Numerics; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; @@ -12,22 +11,17 @@ using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [PropertyDrawer(typeof(AssetObject), useForChildren: true, priority: 100)] internal sealed class AssetReferencePropertyDrawer : IPropertyDrawer, IDisposable { private const nuint C_SEARCH_BUFFER_SIZE = 256; - private readonly object m_sync = new(); private readonly AssetPipeline m_assets; private ConditionalWeakTable m_candidatesByType = new(); private readonly Dictionary m_searchByPath = new(StringComparer.Ordinal); - internal AssetReferencePropertyDrawer(AssetPipeline assets) { m_assets = assets ?? throw new ArgumentNullException(nameof(assets)); @@ -60,76 +54,58 @@ public void Draw(PropertyDrawContext context) object? currentValue = context.GetValue(); Guid persistentId = ReadPersistentId(currentValue); AssetCandidate? selected = Array.Find(candidates, candidate => candidate.persistentId == persistentId); - string preview = persistentId == Guid.Empty - ? "None" - : currentValue is AssetObject { isMissing: true } missing - ? $"Missing {missing.GetType().Name} [{persistentId}]" - : selected is null - ? $"Missing ({persistentId})" - : selected.displayName; - - bool open = EditorWidget.BeginBoundedCombo($"##{context.path}", preview); - Vector2 dropMinimum = NativeImGui.GetItemRectMin(); - Vector2 dropMaximum = NativeImGui.GetItemRectMax(); - EditorDropWidgetResult drop = EditorDragDropRenderer.Target( - context.interactions.For( - InspectorInteractionIds.C_ASSET_REFERENCE_AREA, - new AssetReferenceDropTarget( - assetType, - persistentIdToAssign => - { - AssetCandidate? dropped = Array.Find( - candidates, - candidate => candidate.persistentId == persistentIdToAssign); - if (dropped is not null) - AssignAsset(context, assetType, dropped); - }))); + string preview = persistentId == Guid.Empty ? "None" : currentValue is AssetObject { isMissing: true } missing ? $"Missing {missing.GetType().Name} [{persistentId}]" : selected is null ? $"Missing ({persistentId})" : selected.displayName; + bool open = EditorUI.current.widgets.BeginBoundedCombo($"##{context.path}", preview); + Vector2 dropMinimum = Ui.GetItemMinimum(); + Vector2 dropMaximum = Ui.GetItemMaximum(); + EditorDropWidgetResult drop = EditorDragDropRenderer.Target(context.interactions.For(InspectorInteractionIds.C_ASSET_REFERENCE_AREA, new AssetReferenceDropTarget(assetType, persistentIdToAssign => + { + AssetCandidate? dropped = Array.Find(candidates, candidate => candidate.persistentId == persistentIdToAssign); + if (dropped is not null) + AssignAsset(context, assetType, dropped); + }))); if (drop.isPreviewing && drop.status.canDrop) - EditorWidget.DropTargetHighlight(dropMinimum, dropMaximum); - + EditorUI.current.widgets.DropTargetHighlight(dropMinimum, dropMaximum); if (!open) return; try { - string search = m_searchByPath.TryGetValue(context.path, out string? existingSearch) - ? existingSearch - : string.Empty; - _ = EditorWidget.SearchInput( - context.path, - "Search assets...", - ref search, - C_SEARCH_BUFFER_SIZE); + string search = m_searchByPath.TryGetValue(context.path, out string? existingSearch) ? existingSearch : string.Empty; + _ = EditorUI.current.widgets.SearchInput(context.path, "Search assets...", ref search, C_SEARCH_BUFFER_SIZE); m_searchByPath[context.path] = search; - - if (NativeImGui.Selectable("None", persistentId == Guid.Empty)) + if (Ui.Selectable("None", persistentId == Guid.Empty)) context.SetValue(null); - for (int i = 0; i < candidates.Length; i++) { AssetCandidate candidate = candidates[i]; - if (!string.IsNullOrWhiteSpace(search) && - candidate.displayName.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0 && - candidate.fullPath.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0) + if (!string.IsNullOrWhiteSpace(search) + && candidate.displayName.IndexOf( + search, + StringComparison.OrdinalIgnoreCase + ) < 0 + && candidate.fullPath.IndexOf( + search, + StringComparison.OrdinalIgnoreCase + ) < 0) { continue; } - if (NativeImGui.Selectable( - $"{candidate.displayName}##{candidate.persistentId:N}", - candidate.persistentId == persistentId)) + if (Ui.Selectable($"{candidate.displayName}##{candidate.persistentId:N}", candidate.persistentId == persistentId)) { AssignAsset(context, assetType, candidate); } - if (NativeImGui.IsItemHovered() && EditorWidget.BeginMenuTooltip()) + + if (Ui.IsItemHovered() && EditorUI.current.widgets.BeginMenuTooltip()) { - NativeImGui.TextUnformatted(candidate.fullPath); - EditorWidget.EndMenuTooltip(); + Ui.Text(candidate.fullPath); + EditorUI.current.widgets.EndMenuTooltip(); } } } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } @@ -139,15 +115,12 @@ private AssetCandidate[] GetCandidates(Type targetAssetType) { if (m_candidatesByType.TryGetValue(targetAssetType, out CandidatesBox? cached)) return cached.candidates; - IReadOnlyList entries = m_assets.GetFileSystemEntries(includeDirectories: false); var candidates = new List(entries.Count); for (int i = 0; i < entries.Count; i++) { AssetFileEntry entry = entries[i]; - if (!m_assets.TryGetAssetType(entry.assetPath, out Type? assetType) || - assetType is null || - !targetAssetType.IsAssignableFrom(assetType)) + if (!m_assets.TryGetAssetType(entry.assetPath, out Type? assetType) || assetType is null || !targetAssetType.IsAssignableFrom(assetType)) { continue; } @@ -156,22 +129,14 @@ assetType is null || { string name = Path.GetFileNameWithoutExtension(entry.assetPath.localPath); string displayName = $"{entry.assetPath.source.value}:{name}"; - candidates.Add(new AssetCandidate( - entry.assetPath, - persistentId, - displayName, - entry.assetPath.ToString())); + candidates.Add(new AssetCandidate(entry.assetPath, persistentId, displayName, entry.assetPath.ToString())); } } candidates.Sort(static ( left, right - ) => - string.Compare( - left.displayName, - right.displayName, - StringComparison.OrdinalIgnoreCase)); + ) => string.Compare(left.displayName, right.displayName, StringComparison.OrdinalIgnoreCase)); AssetCandidate[] result = candidates.ToArray(); m_candidatesByType.Add(targetAssetType, new CandidatesBox(result)); return result; @@ -179,7 +144,6 @@ assetType is null || } private static Guid ReadPersistentId(object? reference) => reference is AssetObject asset ? asset.identity.persistentId : Guid.Empty; - private void AssignAsset( PropertyDrawContext context, Type assetType, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/EngineObjectReferencePropertyDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/EngineObjectReferencePropertyDrawer.cs index 7006565b7..1a1d05064 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/EngineObjectReferencePropertyDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/EngineObjectReferencePropertyDrawer.cs @@ -1,31 +1,24 @@ - - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Editor.Core; using Inno.Editor.Inspection; using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Runtime; using Inno.Scene; using Inno.Scene.Components; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [PropertyDrawer(typeof(GameObject), useForChildren: true, priority: 100)] [PropertyDrawer(typeof(GameComponent), useForChildren: true, priority: 100)] internal sealed class EngineObjectReferencePropertyDrawer : IPropertyDrawer { private const nuint C_SEARCH_BUFFER_SIZE = 256; - private readonly Dictionary m_searchByPath = new(StringComparer.Ordinal); private readonly RuntimeSession m_runtimeSession; - internal EngineObjectReferencePropertyDrawer(RuntimeSession runtimeSession) { m_runtimeSession = runtimeSession ?? throw new ArgumentNullException(nameof(runtimeSession)); @@ -42,51 +35,34 @@ public void Draw(PropertyDrawContext context) Type targetType = context.propertyType; EngineObject? selected = context.GetValue() as EngineObject; List candidates = CollectCandidates(targetType); - string preview = selected is null || selected.isDestroyed - ? "None" - : GetDisplayName(selected); - - bool open = EditorWidget.BeginBoundedCombo($"##{context.path}", preview); - _ = EditorDragDropRenderer.Target( - context.interactions.For( - InspectorInteractionIds.C_ENGINE_OBJECT_REFERENCE_AREA, - new EngineObjectReferenceDropTarget(targetType, context.SetValue))); - + string preview = selected is null || selected.isDestroyed ? "None" : GetDisplayName(selected); + bool open = EditorUI.current.widgets.BeginBoundedCombo($"##{context.path}", preview); + _ = EditorDragDropRenderer.Target(context.interactions.For(InspectorInteractionIds.C_ENGINE_OBJECT_REFERENCE_AREA, new EngineObjectReferenceDropTarget(targetType, value => context.SetValue(value)))); if (!open) return; - try { - string search = m_searchByPath.TryGetValue(context.path, out string? currentSearch) - ? currentSearch - : string.Empty; - _ = EditorWidget.SearchInput( - context.path, - "Search scene objects...", - ref search, - C_SEARCH_BUFFER_SIZE); + string search = m_searchByPath.TryGetValue(context.path, out string? currentSearch) ? currentSearch : string.Empty; + _ = EditorUI.current.widgets.SearchInput(context.path, "Search scene objects...", ref search, C_SEARCH_BUFFER_SIZE); m_searchByPath[context.path] = search; - - if (NativeImGui.Selectable("None", selected is null)) + if (Ui.Selectable("None", selected is null)) context.SetValue(null); - for (int i = 0; i < candidates.Count; i++) { EngineObject candidate = candidates[i]; string displayName = GetDisplayName(candidate); - if (!string.IsNullOrWhiteSpace(search) && - displayName.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0) + if (!string.IsNullOrWhiteSpace(search) && displayName.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0) { continue; } - if (NativeImGui.Selectable(displayName, ReferenceEquals(candidate, selected))) + if (Ui.Selectable(displayName, ReferenceEquals(candidate, selected))) context.SetValue(candidate); } } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } @@ -100,7 +76,6 @@ private List CollectCandidates(Type targetType) GameObject gameObject = objects[objectIndex]; if (targetType.IsInstanceOfType(gameObject)) candidates.Add(gameObject); - IReadOnlyList components = gameObject.GetComponents(); for (int componentIndex = 0; componentIndex < components.Count; componentIndex++) { @@ -108,6 +83,7 @@ private List CollectCandidates(Type targetType) candidates.Add(components[componentIndex]); } } + return candidates; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/SceneInspectionPropertyEditService.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/SceneInspectionPropertyEditService.cs index 16f5a223f..735f32075 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/SceneInspectionPropertyEditService.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/PropertyDrawing/SceneInspectionPropertyEditService.cs @@ -1,18 +1,15 @@ using System; - using Inno.Editor.Inspection; using Inno.Editor.Scene; using Inno.Scene; namespace Inno.Editor.Panel.Inspector; - /// /// Adapts generic inspection property edits to compact Scene history records. /// internal sealed class SceneInspectionPropertyEditService : IInspectionPropertyEditService { private readonly SceneEdits m_edits; - /// /// Creates the Scene property edit adapter. /// @@ -24,39 +21,17 @@ internal SceneInspectionPropertyEditService(SceneEdits edits) m_edits = edits ?? throw new ArgumentNullException(nameof(edits)); } - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the operation succeeds or its condition is satisfied; otherwise, . - /// + /// public bool ChangeProperty( object owner, string propertyName, Action mutation, - string historyName + string historyName, + string? mergeKey, + string? componentName ) { ArgumentNullException.ThrowIfNull(owner); - EngineObject sceneObject = owner as EngineObject ?? throw new ArgumentException( - $"Scene property edits require an '{typeof(EngineObject).FullName}' owner.", - nameof(owner)); - return m_edits.ChangeProperty( - sceneObject, - propertyName, - mutation, - historyName, - $"scene-property:{sceneObject.identity.persistentId:N}:{propertyName}"); + EngineObject sceneObject = owner as EngineObject ?? throw new ArgumentException($"Scene property edits require an '{typeof(EngineObject).FullName}' owner.", nameof(owner)); + return m_edits.ChangeProperty(sceneObject, propertyName, mutation, historyName, mergeKey); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/ComponentEditorTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/ComponentEditorTarget.cs deleted file mode 100644 index b42cd8730..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/ComponentEditorTarget.cs +++ /dev/null @@ -1,42 +0,0 @@ - -using System; - -using Inno.Scene; - -namespace Inno.Editor.Panel.Inspector; - -/// -/// Identifies a component together with its owning game object. -/// -internal sealed class ComponentEditorTarget -{ - /// - /// Creates an inspector action target that keeps a component paired with its owning game object. - /// - /// - /// The live game object that owns the component. - /// - /// - /// The component operated on by the inspector action. - /// - /// - /// Thrown when either argument is . - /// - internal ComponentEditorTarget( - GameObject gameObject, - GameComponent component - ) { - this.gameObject = gameObject ?? throw new ArgumentNullException(nameof(gameObject)); - this.component = component ?? throw new ArgumentNullException(nameof(component)); - } - - /// - /// Gets the owning game object. - /// - public GameObject gameObject { get; } - - /// - /// Gets the component. - /// - public GameComponent component { get; } -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs index db21f7c1f..a76904421 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs @@ -1,8 +1,7 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Core.Serialization; using Inno.Core.Logging; using Inno.Editor.Core; @@ -10,22 +9,17 @@ using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Scene; using Inno.Editor.Settings; using Inno.Scene; using Inno.Scene.Components; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [InspectionDrawer(typeof(GameObject))] internal sealed class GameObjectInspectionDrawer : InspectionDrawer { private const nuint C_SEARCH_BUFFER_SIZE = 256; - private readonly InspectorCardControls m_cardControls; private readonly SceneEdits m_edits; private readonly SerializationRegistry m_serialization; @@ -33,7 +27,6 @@ internal sealed class GameObjectInspectionDrawer : InspectionDrawer private readonly GameObjectLayerSelector m_layerSelector; private readonly EditorSettings m_settings; private string m_componentSearch = string.Empty; - /// /// Creates a GameObject drawer backed by the current project classification settings. /// @@ -53,7 +46,7 @@ internal sealed class GameObjectInspectionDrawer : InspectionDrawer /// The application log router used by component card controls. /// /// - /// Thrown when , , or + /// Thrown when , , or /// is . /// internal GameObjectInspectionDrawer( @@ -65,12 +58,8 @@ LogRouter logs ) { m_edits = edits ?? throw new ArgumentNullException(nameof(edits)); ArgumentNullException.ThrowIfNull(classificationSettings); - m_tagSelector = new GameObjectTagSelector( - classificationSettings, - edits); - m_layerSelector = new GameObjectLayerSelector( - classificationSettings, - edits); + m_tagSelector = new GameObjectTagSelector(classificationSettings, edits); + m_layerSelector = new GameObjectLayerSelector(classificationSettings, edits); m_settings = settings ?? throw new ArgumentNullException(nameof(settings)); m_serialization = serialization ?? throw new ArgumentNullException(nameof(serialization)); m_cardControls = new InspectorCardControls(logs); @@ -79,9 +68,7 @@ LogRouter logs /// /// Gets the icon glyph used to represent this item in the editor. /// - public override string icon => m_settings - .Get("Editor/Appearance/Icons/GameObject") - .GetAsString("value", ImGuiIcon.Cube)!; + public override string icon => m_settings.Get("Editor/Appearance/Icons/GameObject").GetAsString("value", ImGuiIcon.Cube)!; /// /// Binds a caller-visible label to the current inspection target. @@ -98,9 +85,7 @@ LogRouter logs protected override (string name, Action? setter) BindName( InspectionDrawContext context, GameObject target - ) - => (target.name, name => m_edits.RenameGameObject(target, name)); - + ) => (target.name, name => m_edits.RenameGameObject(target, name)); /// /// Renders the header presentation for the current editor frame. /// @@ -115,26 +100,21 @@ protected override void DrawHeader( GameObject target ) { bool active = target.activeSelf; - if (EditorWidget.CompactCheckbox( - $"target_active_{target.identity.persistentId:N}", - ref active, - tooltip: "Enable or disable this GameObject.")) + if (EditorUI.current.widgets.CompactCheckbox($"target_active_{target.identity.persistentId:N}", ref active, tooltip: "Enable or disable this GameObject.")) { m_edits.SetGameObjectActive(target, active); } - NativeImGui.SameLine(0f, -1f); - NativeImGui.TextUnformatted("Active"); - NativeImGui.SameLine(0f, EditorWidget.style.inspectorHeaderSectionSpacing); - float available = NativeImGui.GetContentRegionAvail().X; - float tagLabelWidth = EditorWidget.GetLabelChipSize("Tag").X; - float layerLabelWidth = EditorWidget.GetLabelChipSize("Layer").X; - float controlWidth = MathF.Max( - 1f, - (available - tagLabelWidth - layerLabelWidth - - EditorWidget.style.inspectorHeaderSectionSpacing) * 0.5f); - m_tagSelector.Draw(target, controlWidth); - NativeImGui.SameLine(0f, EditorWidget.style.inspectorHeaderSectionSpacing); - m_layerSelector.Draw(target, controlWidth); + + Ui.SameLine(0f, -1f); + Ui.Text("Active"); + Ui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); + float available = Ui.GetContentRegionAvailable().X; + float tagLabelWidth = EditorUI.current.widgets.GetLabelChipSize("Tag").X; + float layerLabelWidth = EditorUI.current.widgets.GetLabelChipSize("Layer").X; + float controlWidth = MathF.Max(1f, (available - tagLabelWidth - layerLabelWidth - EditorUI.current.widgets.style.inspectorHeaderSectionSpacing) * 0.5f); + m_tagSelector.Draw([target], context.interactions, controlWidth); + Ui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); + m_layerSelector.Draw([target], context.interactions, controlWidth); } /// @@ -153,32 +133,28 @@ GameObject gameObject if (!gameObject.isRuntimeValid || !gameObject.scene.isLoaded) { _ = context.interactions.For(context.interactions.focusedArea).Select(); - NativeImGui.TextUnformatted("Selected GameObject is not available in the active scene."); + Ui.Text("Selected GameObject is not available in the active scene."); return; } if (!m_layerSelector.IsLayerDefined(gameObject.layer)) { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.error); - ImGuiWidget.WrappedText( - $"Layer slot {gameObject.layer.index} is not defined in the current project settings. " + - "Choose Default or restore that layer definition."); - NativeImGui.PopStyleColor(); - NativeImGui.Spacing(); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.error); + EditorUI.current.widgets.WrappedText($"Layer slot {gameObject.layer.index} is not defined in the current project settings. " + "Choose Default or restore that layer definition."); + Ui.PopStyleColor(); + Ui.Spacing(); } if (!m_tagSelector.IsTagDefined(gameObject.tag)) { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.error); - ImGuiWidget.WrappedText( - $"Tag '{gameObject.tag}' is not defined in the current Project Settings. " + - "Choose another tag or restore that tag definition."); - NativeImGui.PopStyleColor(); - NativeImGui.Spacing(); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.error); + EditorUI.current.widgets.WrappedText($"Tag '{gameObject.tag}' is not defined in the current Project Settings. " + "Choose another tag or restore that tag definition."); + Ui.PopStyleColor(); + Ui.Spacing(); } - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.ItemSpacing), EditorWidget.style.compactItemSpacing); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.compactFramePadding); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.compactItemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.compactFramePadding); try { DrawComponents(context, gameObject); @@ -186,7 +162,7 @@ GameObject gameObject } finally { - NativeImGui.PopStyleVar(2); + Ui.PopStyleVar(2); } } @@ -200,127 +176,82 @@ GameObject gameObject GameComponent component = components[i]; Type componentType = component.GetType(); MissingGameComponent? missing = component as MissingGameComponent; - IReadOnlyList properties = missing is null - ? m_serialization.GetProperties(component) - : Array.Empty(); + IReadOnlyList properties = missing is null ? m_serialization.GetProperties(component) : Array.Empty(); string componentId = component.identity.persistentId.ToString("N"); GameBehavior? behavior = component as GameBehavior; - var editorTarget = new ComponentEditorTarget(gameObject, component); + var editorTarget = component; string title = missing?.missingTypeName ?? componentType.Name; bool canRemove = componentType != typeof(Transform); bool dimmed = behavior is { enabled: false }; Vector2 cardMinimum = default; Vector2 headerMaximum = default; - bool open = EditorWidget.CollapsingCard( - componentId, - title, - behavior is not null - ? () => - { - bool enabled = behavior.enabled; - if (EditorWidget.CompactCheckbox( - $"enabled_{componentId}", - ref enabled, - tooltip: "Enable or disable this component.")) - { - _ = m_edits.ChangeProperty( - behavior, - "enabled", - () => behavior.enabled = enabled, - enabled ? "Enable Component" : "Disable Component", - mergeKey: null); - } - } - : null, - () => m_cardControls.DrawComponent( - m_edits, - component, - canRemove, - () => context.interactions - .For( - InspectorInteractionIds.C_COMPONENT_AREA, - editorTarget) - .Enqueue(InspectorInteractionIds.C_REMOVE_COMPONENT)), - dimmed: dimmed, - trailingControlWidth: m_cardControls.GetWidth(canRemove), - drawContextMenu: () => + bool open = EditorUI.current.widgets.CollapsingCard(componentId, title, behavior is not null ? () => + { + bool enabled = behavior.enabled; + if (EditorUI.current.widgets.CompactCheckbox($"enabled_{componentId}", ref enabled, tooltip: "Enable or disable this component.")) { - cardMinimum = NativeImGui.GetItemRectMin(); - headerMaximum = NativeImGui.GetItemRectMax(); - _ = EditorMenuRenderer.ContextMenu( - $"##component_menu_{componentId}", - context.interactions.For(InspectorInteractionIds.C_COMPONENT_AREA, editorTarget)); - m_cardControls.DrawComponentDragSource(component, title, dimmed); - }); - + _ = m_edits.ChangeProperty(behavior, "enabled", () => behavior.enabled = enabled, enabled ? "Enable Component" : "Disable Component", mergeKey: null); + } + } : null, () => m_cardControls.DrawComponent( + m_edits, + component, + canRemove, + () => context.interactions.For(InspectorInteractionIds.C_COMPONENT_AREA, editorTarget).Enqueue(InspectorInteractionIds.C_REMOVE_COMPONENT) + ), dimmed: dimmed, trailingControlWidth: m_cardControls.GetWidth(canRemove), drawContextMenu: () => + { + cardMinimum = Ui.GetItemMinimum(); + headerMaximum = Ui.GetItemMaximum(); + _ = EditorUI.current.menus.ContextMenu($"##component_menu_{componentId}", context.interactions.For(InspectorInteractionIds.C_COMPONENT_AREA, editorTarget)); + m_cardControls.DrawComponentDragSource(component, title, dimmed); + }); Vector2 cardMaximum = headerMaximum; if (open) { - NativeImGui.Unindent(); - EditorWidget.CardBody( - componentId, - () => EditorWidget.SectionLayout(() => + Ui.Unindent(); + EditorUI.current.widgets.CardBody(componentId, () => EditorUI.current.widgets.SectionLayout(() => + { + if (missing is not null) { - if (missing is not null) - { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.error); - ImGuiWidget.WrappedText( - $"Missing component script ({missing.missingType.stableId:D}). " + - "Its serialized state is preserved and will recover automatically when the type returns."); - NativeImGui.PopStyleColor(); - return; - } - if (context.TryDrawInline(component)) - return; - if (properties.Count == 0) - { - InspectorTypeOrigin.Draw(componentType); - return; - } - for (int propertyIndex = 0; propertyIndex < properties.Count; propertyIndex++) - { - context.properties.Draw( - context.editorContext, - component, - $"gameObject.{gameObject.identity.persistentId:N}.{componentId}", - properties[propertyIndex]); - } - }), - dimmed: dimmed); - cardMaximum = NativeImGui.GetItemRectMax(); - NativeImGui.Indent(); - NativeImGui.TreePop(); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.error); + EditorUI.current.widgets.WrappedText($"Missing component script ({missing.missingType.stableId:D}). " + "Its serialized state is preserved and will recover automatically when the type returns."); + Ui.PopStyleColor(); + return; + } + + if (context.TryDrawInline(component)) + return; + if (properties.Count == 0) + { + InspectorTypeOrigin.Draw(componentType); + return; + } + + for (int propertyIndex = 0; propertyIndex < properties.Count; propertyIndex++) + { + context.properties.Draw(context.editorContext, component, $"gameObject.{gameObject.identity.persistentId:N}.{componentId}", properties[propertyIndex]); + } + }), dimmed: dimmed); + cardMaximum = Ui.GetItemMaximum(); + Ui.Indent(); + Ui.TreePop(); } - m_cardControls.DrawComponentDropTarget( - context.interactions, - m_edits, - component, - i, - cardMinimum, - cardMaximum); - NativeImGui.Dummy(new Vector2(0f, EditorWidget.style.inspectorCardSpacing)); + m_cardControls.DrawComponentDropTarget(context.interactions, m_edits, component, i, cardMinimum, cardMaximum); + Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.inspectorCardSpacing)); } - } private void DrawAddComponent( InspectionDrawContext context, GameObject gameObject ) { - if (EditorWidget.CenteredButton( - "Add Component", - EditorWidget.style.inspectorAddButtonTopPadding)) + if (EditorUI.current.widgets.CenteredButton("Add Component", EditorUI.current.widgets.style.inspectorAddButtonTopPadding)) { m_componentSearch = string.Empty; - NativeImGui.OpenPopup("##add_component_popup"); + Ui.OpenPopup("##add_component_popup"); } - if (!EditorWidget.BeginSearchPopup( - "##add_component_popup", - ref m_componentSearch, - "Search components...", - C_SEARCH_BUFFER_SIZE)) + if (!EditorUI.current.widgets.BeginSearchPopup("##add_component_popup", ref m_componentSearch, "Search components...", C_SEARCH_BUFFER_SIZE)) { return; } @@ -328,16 +259,12 @@ GameObject gameObject try { EditorInteraction interaction = context.interactions.For(InspectorInteractionIds.C_COMPONENT_AREA, gameObject); - if (EditorMenuRenderer.DrawSearchItems( - interaction, - interaction.BuildMenu().items, - m_componentSearch)) - NativeImGui.CloseCurrentPopup(); + if (EditorUI.current.menus.DrawSearchItems(interaction, interaction.BuildMenu().items, m_componentSearch)) + Ui.CloseCurrentPopup(); } finally { - EditorWidget.EndSearchPopup(); + EditorUI.current.widgets.EndSearchPopup(); } } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs index 762f83d9f..85e07ecdb 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs @@ -1,16 +1,15 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - +using System.Linq; +using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Scene; using Inno.Scene; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - /// /// Draws the project-defined tag picker used by GameObject target headers. /// @@ -18,7 +17,6 @@ internal sealed class GameObjectTagSelector { private readonly SceneProjectSettingsModule m_settings; private readonly SceneEdits m_edits; - /// /// Creates a tag selector backed by the effective Project Settings snapshot. /// @@ -29,7 +27,7 @@ internal sealed class GameObjectTagSelector /// The Scene editing service used to record GameObject tag changes. /// /// - /// Thrown when or is . + /// Thrown when or is . /// internal GameObjectTagSelector( SceneProjectSettingsModule settings, @@ -42,48 +40,54 @@ SceneEdits edits /// /// Draws a compact tag selector for one live GameObject. /// - /// + /// /// The GameObject whose tag should be displayed and edited. /// + /// + /// The authoritative shared History owner. + /// /// /// The width reserved for the selector. /// internal void Draw( - GameObject target, + IReadOnlyList targets, + EditorInteractions interactions, float width ) { - ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(targets); + if (targets.Count == 0) + return; + GameObject target = targets[0]; + bool mixed = targets.Any(value => value.tag != target.tag); IReadOnlyList tags = m_settings.tagCatalog.GetTags(); - string preview = m_settings.tagCatalog.IsDefined(target.tag) - ? target.tag - : $"{target.tag} (Undefined)"; - EditorWidget.LabelChip("Tag", EditorPalette.inspectorTagLabel); - NativeImGui.SameLine(0f, 0f); + string preview = m_settings.tagCatalog.IsDefined(target.tag) ? target.tag : $"{target.tag} (Undefined)"; + if (mixed) + preview = "Mixed values"; + EditorUI.current.widgets.LabelChip("Tag", EditorPalette.inspectorTagLabel); + Ui.SameLine(0f, 0f); float selectorWidth = MathF.Max(1f, width); - if (!EditorWidget.BeginMenuSelector( - $"game_object_tag_{target.identity.persistentId:N}", - preview, - selectorWidth, - selectorWidth)) + if (!EditorUI.current.widgets.BeginMenuSelector($"game_object_tag_{target.identity.persistentId:N}", preview, selectorWidth, selectorWidth)) { return; } + try { for (int i = 0; i < tags.Count; i++) { string tag = tags[i]; - if (NativeImGui.Selectable( - tag, - string.Equals(target.tag, tag, StringComparison.Ordinal))) + if (Ui.Selectable(tag, string.Equals(target.tag, tag, StringComparison.Ordinal))) { - m_edits.SetGameObjectTag(target, tag); + using EditorHistoryTransaction transaction = interactions.history.BeginTransaction("Set Selection Tag"); + foreach (GameObject selected in targets) + m_edits.SetGameObjectTag(selected, tag); + transaction.Commit(); } } } finally { - EditorWidget.EndMenuSelector(); + EditorUI.current.widgets.EndMenuSelector(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs index b56475cf5..c8da97237 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs @@ -1,8 +1,7 @@ - +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Core.Serialization; using Inno.Core.Logging; using Inno.Editor.Core; @@ -10,27 +9,21 @@ using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Scene; using Inno.Editor.Settings; using Inno.Scene; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [InspectionDrawer(typeof(GameScene))] internal sealed class GameSceneInspectionDrawer : InspectionDrawer { private const nuint C_SEARCH_BUFFER_SIZE = 256; - private readonly InspectorCardControls m_cardControls; private readonly SceneEdits m_edits; private readonly EditorSettings m_settings; private readonly SerializationRegistry m_serialization; private string m_systemSearch = string.Empty; - /// /// Creates a Scene drawer backed by the Scene editing service. /// @@ -47,7 +40,7 @@ internal sealed class GameSceneInspectionDrawer : InspectionDrawer /// The application log router used by component card controls. /// /// - /// Thrown when or is . + /// Thrown when or is . /// internal GameSceneInspectionDrawer( SceneEdits edits, @@ -64,9 +57,7 @@ LogRouter logs /// /// Gets the icon glyph used to represent this item in the editor. /// - public override string icon => m_settings - .Get("Editor/Appearance/Icons/Scene") - .GetAsString("value", ImGuiIcon.Cubes)!; + public override string icon => m_settings.Get("Editor/Appearance/Icons/Scene").GetAsString("value", ImGuiIcon.Cubes)!; /// /// Binds a caller-visible label to the current inspection target. @@ -83,9 +74,7 @@ LogRouter logs protected override (string name, Action? setter) BindName( InspectionDrawContext context, GameScene target - ) - => (target.name, name => m_edits.RenameScene(target, name)); - + ) => (target.name, name => m_edits.RenameScene(target, name)); /// /// Renders the header presentation for the current editor frame. /// @@ -98,9 +87,7 @@ GameScene target protected override void DrawHeader( InspectionDrawContext context, GameScene target - ) - => NativeImGui.TextUnformatted(target.isLoaded ? "Loaded Scene" : "Scene"); - + ) => Ui.Text(target.isLoaded ? "Loaded Scene" : "Scene"); /// /// Renders the value presentation for the current editor frame. /// @@ -117,12 +104,12 @@ GameScene scene if (!scene.isLoaded || scene.isDestroyed) { _ = context.interactions.For(context.interactions.focusedArea).Select(); - NativeImGui.TextUnformatted("Selected scene is no longer loaded."); + Ui.Text("Selected scene is no longer loaded."); return; } - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.ItemSpacing), EditorWidget.style.compactItemSpacing); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.compactFramePadding); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.compactItemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.compactFramePadding); try { DrawSystems(context, scene); @@ -130,7 +117,7 @@ GameScene scene } finally { - NativeImGui.PopStyleVar(2); + Ui.PopStyleVar(2); } } @@ -143,135 +130,88 @@ GameScene scene { GameSystem system = systems[i]; MissingGameSystem? missing = system as MissingGameSystem; - IReadOnlyList properties = missing is null - ? m_serialization.GetProperties(system) - : Array.Empty(); + IReadOnlyList properties = missing is null ? m_serialization.GetProperties(system) : Array.Empty(); string systemId = system.identity.persistentId.ToString("N"); - var editorTarget = new SystemEditorTarget(scene, system); + var editorTarget = system; string title = missing?.missingTypeName ?? system.GetType().Name; bool dimmed = !system.enabled; Vector2 cardMinimum = default; Vector2 headerMaximum = default; - bool open = EditorWidget.CollapsingCard( - systemId, - title, - missing is null ? () => - { - bool enabled = system.enabled; - if (EditorWidget.CompactCheckbox( - $"enabled_{systemId}", - ref enabled, - tooltip: "Enable or disable this system.")) - { - _ = m_edits.ChangeProperty( - system, - "enabled", - () => system.enabled = enabled, - enabled ? "Enable System" : "Disable System", - mergeKey: null); - } - } : null, - () => m_cardControls.DrawSystem( - m_edits, - scene, - system, - () => context.interactions - .For( - InspectorInteractionIds.C_SYSTEM_AREA, - editorTarget) - .Enqueue(InspectorInteractionIds.C_REMOVE_SYSTEM)), - dimmed: dimmed, - trailingControlWidth: m_cardControls.GetWidth(canRemove: true), - drawContextMenu: () => + bool open = EditorUI.current.widgets.CollapsingCard(systemId, title, missing is null ? () => + { + bool enabled = system.enabled; + if (EditorUI.current.widgets.CompactCheckbox($"enabled_{systemId}", ref enabled, tooltip: "Enable or disable this system.")) { - cardMinimum = NativeImGui.GetItemRectMin(); - headerMaximum = NativeImGui.GetItemRectMax(); - _ = EditorMenuRenderer.ContextMenu( - $"##system_menu_{systemId}", - context.interactions.For(InspectorInteractionIds.C_SYSTEM_AREA, editorTarget)); - m_cardControls.DrawSystemDragSource(system, title, dimmed); - }); + _ = m_edits.ChangeProperty(system, "enabled", () => system.enabled = enabled, enabled ? "Enable System" : "Disable System", mergeKey: null); + } + } : null, () => m_cardControls.DrawSystem( + m_edits, + scene, + system, + () => context.interactions.For(InspectorInteractionIds.C_SYSTEM_AREA, editorTarget).Enqueue(InspectorInteractionIds.C_REMOVE_SYSTEM) + ), dimmed: dimmed, trailingControlWidth: m_cardControls.GetWidth(canRemove: true), drawContextMenu: () => + { + cardMinimum = Ui.GetItemMinimum(); + headerMaximum = Ui.GetItemMaximum(); + _ = EditorUI.current.menus.ContextMenu($"##system_menu_{systemId}", context.interactions.For(InspectorInteractionIds.C_SYSTEM_AREA, editorTarget)); + m_cardControls.DrawSystemDragSource(system, title, dimmed); + }); Vector2 cardMaximum = headerMaximum; if (open) { - NativeImGui.Unindent(); - EditorWidget.CardBody( - systemId, - () => EditorWidget.SectionLayout(() => + Ui.Unindent(); + EditorUI.current.widgets.CardBody(systemId, () => EditorUI.current.widgets.SectionLayout(() => + { + if (missing is not null) + { + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.error); + EditorUI.current.widgets.WrappedText($"Missing system script ({missing.missingType.stableId:D}). " + "Its serialized state is preserved and will recover automatically when the type returns."); + Ui.PopStyleColor(); + return; + } + + if (properties.Count == 0) { - if (missing is not null) - { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.error); - ImGuiWidget.WrappedText( - $"Missing system script ({missing.missingType.stableId:D}). " + - "Its serialized state is preserved and will recover automatically when the type returns."); - NativeImGui.PopStyleColor(); - return; - } - if (properties.Count == 0) - { - InspectorTypeOrigin.Draw(system.GetType()); - return; - } - for (int propertyIndex = 0; propertyIndex < properties.Count; propertyIndex++) - { - context.properties.Draw( - context.editorContext, - system, - $"scene.{scene.identity.persistentId:N}.{systemId}", - properties[propertyIndex]); - } - }), - dimmed: dimmed); - cardMaximum = NativeImGui.GetItemRectMax(); - NativeImGui.Indent(); - NativeImGui.TreePop(); + InspectorTypeOrigin.Draw(system.GetType()); + return; + } + + for (int propertyIndex = 0; propertyIndex < properties.Count; propertyIndex++) + { + context.properties.Draw(context.editorContext, system, $"scene.{scene.identity.persistentId:N}.{systemId}", properties[propertyIndex]); + } + }), dimmed: dimmed); + cardMaximum = Ui.GetItemMaximum(); + Ui.Indent(); + Ui.TreePop(); } - m_cardControls.DrawSystemDropTarget( - context.interactions, - m_edits, - scene, - system, - i, - cardMinimum, - cardMaximum); - NativeImGui.Dummy(new Vector2(0f, EditorWidget.style.inspectorCardSpacing)); + m_cardControls.DrawSystemDropTarget(context.interactions, m_edits, scene, system, i, cardMinimum, cardMaximum); + Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.inspectorCardSpacing)); } - } private void DrawAddSystem( InspectionDrawContext context, GameScene scene ) { - if (EditorWidget.CenteredButton( - "Add System", - EditorWidget.style.inspectorAddButtonTopPadding)) + if (EditorUI.current.widgets.CenteredButton("Add System", EditorUI.current.widgets.style.inspectorAddButtonTopPadding)) { m_systemSearch = string.Empty; - NativeImGui.OpenPopup("##add_system_popup"); + Ui.OpenPopup("##add_system_popup"); } - if (!EditorWidget.BeginSearchPopup( - "##add_system_popup", - ref m_systemSearch, - "Search systems...", - C_SEARCH_BUFFER_SIZE)) - return; + if (!EditorUI.current.widgets.BeginSearchPopup("##add_system_popup", ref m_systemSearch, "Search systems...", C_SEARCH_BUFFER_SIZE)) + return; try { EditorInteraction interaction = context.interactions.For(InspectorInteractionIds.C_SYSTEM_AREA, scene); - if (EditorMenuRenderer.DrawSearchItems( - interaction, - interaction.BuildMenu().items, - m_systemSearch)) - NativeImGui.CloseCurrentPopup(); + if (EditorUI.current.menus.DrawSearchItems(interaction, interaction.BuildMenu().items, m_systemSearch)) + Ui.CloseCurrentPopup(); } finally { - EditorWidget.EndSearchPopup(); + EditorUI.current.widgets.EndSearchPopup(); } } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs index e8e2600bb..176c85df5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs @@ -1,28 +1,23 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Core.Settings; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Settings; using Inno.Scene; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [ProjectSettingPath("Project/Scene/Tags")] -internal sealed unsafe class GameTagsSetting : ProjectSettingEditor +internal sealed class GameTagsSetting : ProjectSettingEditor { private const int C_TAG_BUFFER_SIZE = 128; - private string m_error = string.Empty; private string m_newTag = string.Empty; - /// /// Gets the stable project-setting identity used for discovery and persistence. /// @@ -30,7 +25,6 @@ internal sealed unsafe class GameTagsSetting : ProjectSettingEditor public override string section => "Definitions"; - /// /// Gets the user-facing explanation of this feature or setting. /// @@ -45,33 +39,22 @@ internal sealed unsafe class GameTagsSetting : ProjectSettingEditorItemSpacing.X; - float actionWidth = EditorWidget.GetCompactIconSize().X; - NativeImGui.SetNextItemWidth(MathF.Max( - 1f, - NativeImGui.GetContentRegionAvail().X - actionWidth - spacing)); - bool submit = EditorImGui.InputTextWithHint( - "##new_project_tag", - "New tag", - ref m_newTag, - C_TAG_BUFFER_SIZE, - ImGuiInputTextFlags.EnterReturnsTrue); - NativeImGui.SameLine(0f, spacing); - submit |= EditorWidget.ClickableIcon( - "add_project_tag", - ImGuiIcon.Plus, - "Add project tag"); + float spacing = UiDrawing.GetLayout().itemSpacing.X; + float actionWidth = EditorUI.current.widgets.GetCompactIconSize().X; + Ui.SetNextItemWidth(MathF.Max(1f, Ui.GetContentRegionAvailable().X - actionWidth - spacing)); + bool submit = EditorImGui.InputTextWithHint("##new_project_tag", "New tag", ref m_newTag, C_TAG_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); + Ui.SameLine(0f, spacing); + submit |= EditorUI.current.widgets.ClickableIcon("add_project_tag", ImGuiIcon.Plus, "Add project tag"); if (!submit || string.IsNullOrWhiteSpace(m_newTag)) return; - try { if (!setting.Add(m_newTag)) @@ -89,51 +72,42 @@ private void DrawAddRow(GameTagCatalog setting) private static void DrawTags(GameTagCatalog setting) { IReadOnlyList tags = setting.GetTags(); - ImGuiTableFlags flags = ImGuiTableFlags.RowBg | - ImGuiTableFlags.BordersInnerH | - ImGuiTableFlags.BordersInnerV | - ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoSavedSettings; - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.CellPadding), ImGuiWidget.style.cellPadding); - if (!NativeImGui.BeginTable("##project_tag_definitions", 2, (int)(flags), default(System.Numerics.Vector2), 0f)) + ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerH | ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoSavedSettings; + Ui.PushStyleVar((ImGuiStyleVar.CellPadding), EditorUI.current.widgets.style.cellPadding); + if (!Ui.BeginTable("##project_tag_definitions", 2, flags, default(System.Numerics.Vector2), 0f)) { - NativeImGui.PopStyleVar(); + Ui.PopStyleVar(); return; } + try { - NativeImGui.TableSetupColumn( - "Tag", - (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize)); - NativeImGui.TableSetupColumn( - "Action", - (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), - 72f * ImGuiWidget.style.zoom); + Ui.TableSetupColumn("Tag", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize)); + Ui.TableSetupColumn("Action", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), 72f * EditorUI.current.widgets.style.zoom); DrawTagTableHeader(); for (int i = 0; i < tags.Count; i++) { string tag = tags[i]; bool isDefault = string.Equals(tag, GameObject.defaultTag, StringComparison.Ordinal); - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); - NativeImGui.AlignTextToFramePadding(); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); + Ui.AlignTextToFramePadding(); InsetPlainCell(); - NativeImGui.TextUnformatted(tag); - _ = NativeImGui.TableSetColumnIndex(1); + Ui.Text(tag); + _ = Ui.TableSetColumnIndex(1); InsetPlainCell(); if (isDefault) { - ImGuiWidget.ColoredText(EditorPalette.textDisabled, "Fixed"); + EditorUI.current.widgets.ColoredText(EditorPalette.textDisabled, "Fixed"); continue; } - if (EditorWidget.ClickableText( - $"remove_project_tag_{tag}", - "Remove", - new Vector2( - NativeImGui.CalcTextSize("Remove").X, - NativeImGui.GetFrameHeight()), - "Remove this definition. Existing scene assignments are preserved and become undefined.")) + + if (EditorUI.current.widgets.ClickableText( + $"remove_project_tag_{tag}", + "Remove", + new Vector2(Ui.MeasureText("Remove").X, Ui.GetFrameHeight()), + "Remove this definition. Existing scene assignments are preserved and become undefined." + )) { _ = setting.Remove(tag); } @@ -141,17 +115,17 @@ private static void DrawTags(GameTagCatalog setting) } finally { - NativeImGui.EndTable(); - NativeImGui.PopStyleVar(); + Ui.EndTable(); + Ui.PopStyleVar(); } } private static void DrawTagTableHeader() { - uint background = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionHeader); - NativeImGui.TableNextRow(); - NativeImGui.TableSetBgColor((int)(ImGuiTableBgTarget.RowBg0), background); - NativeImGui.TableSetBgColor((int)(ImGuiTableBgTarget.RowBg1), background); + uint background = Ui.ToPackedColor(EditorPalette.collectionHeader); + Ui.TableNextRow(); + Ui.TableSetBgColor((ImGuiTableBgTarget.RowBg0), background); + Ui.TableSetBgColor((ImGuiTableBgTarget.RowBg1), background); DrawHeaderCell(0, "Tag"); DrawHeaderCell(1, "Action"); } @@ -160,12 +134,10 @@ private static void DrawHeaderCell( int column, string label ) { - _ = NativeImGui.TableSetColumnIndex(column); + _ = Ui.TableSetColumnIndex(column); InsetPlainCell(); - NativeImGui.TextUnformatted(label); + Ui.Text(label); } - private static void InsetPlainCell() - => NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + NativeImGui.Raw.GetStyle()->FramePadding.X); + private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().framePadding.X); } diff --git a/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/PipelineInspectionDrawer.cs similarity index 62% rename from src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineInspectionDrawer.cs rename to src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/PipelineInspectionDrawer.cs index 658d51a3c..0ef4ddaa8 100644 --- a/src/composition/editor/features/Inno.Editor.Rendering/Pipelines/PipelineInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/PipelineInspectionDrawer.cs @@ -1,3 +1,6 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Rendering; using System; using System.Linq; using Inno.Assets; @@ -6,19 +9,18 @@ using Inno.Editor.Assets; using Inno.Editor.Inspection; using Inno.Rendering; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; -namespace Inno.Editor.Rendering; - +namespace Inno.Editor.Panel.Inspector; [InspectionDrawer(typeof(RenderPipelineAsset))] internal sealed class PipelineInspectionDrawer(IInspectionIconProvider icons) : InspectionDrawer { /// /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + public override string icon => Inno.Editor.ImGui.ImGuiIcon.File; + /// /// Retrieves the current icon from authoritative state. /// @@ -31,13 +33,20 @@ internal sealed class PipelineInspectionDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - RenderPipelineAsset target -) - => context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(target.identity.persistentId, out AssetInfo? info) && info is not null - && documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry) ? icons.GetIcon(entry) : icon; + protected override string GetIcon( + InspectionDrawContext context, + RenderPipelineAsset target + ) => context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + target.identity.persistentId, + out AssetInfo? info + ) + && info is not null + && documents.assets.TryGetFileSystemEntry( + info.assetPath, + out AssetFileEntry entry + ) ? icons.GetIcon(entry) : icon; /// /// Binds a caller-visible label to the current inspection target. /// @@ -50,11 +59,10 @@ RenderPipelineAsset target /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - RenderPipelineAsset target -) - => (target.name, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + RenderPipelineAsset target + ) => (target.name, null); /// /// Renders the header presentation for the current editor frame. /// @@ -64,14 +72,20 @@ RenderPipelineAsset target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - RenderPipelineAsset target -) { - if (context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(target.identity.persistentId, out AssetInfo? info) && info is not null) + protected override void DrawHeader( + InspectionDrawContext context, + RenderPipelineAsset target + ) { + if (context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + target.identity.persistentId, + out AssetInfo? info + ) + && info is not null) PipelineInspector.DrawHeader(context, documents, documents.Open(info.assetPath)); } + /// /// Renders the value presentation for the current editor frame. /// @@ -81,12 +95,17 @@ RenderPipelineAsset target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - RenderPipelineAsset target -) { - if (context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(target.identity.persistentId, out AssetInfo? info) && info is not null) + protected override void Draw( + InspectionDrawContext context, + RenderPipelineAsset target + ) { + if (context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + target.identity.persistentId, + out AssetInfo? info + ) + && info is not null) PipelineInspector.Draw(context, documents, documents.Open(info.assetPath)); } } @@ -97,7 +116,8 @@ internal sealed class PipelineSourceDrawer(IInspectionIconProvider /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + public override string icon => Inno.Editor.ImGui.ImGuiIcon.File; + /// /// Retrieves the current icon from authoritative state. /// @@ -110,10 +130,10 @@ internal sealed class PipelineSourceDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - AssetFileEntry target -) => icons.GetIcon(target); + protected override string GetIcon( + InspectionDrawContext context, + AssetFileEntry target + ) => icons.GetIcon(target); /// /// Checks whether this drawer supports the selected Inspector target. /// @@ -123,8 +143,7 @@ AssetFileEntry target /// /// when the documented condition is satisfied; otherwise, . /// -protected override bool CanInspect(AssetFileEntry target) - => !target.isDirectory && target.extension.Equals(".irenderpipeline", StringComparison.OrdinalIgnoreCase); + protected override bool CanInspect(AssetFileEntry target) => !target.isDirectory && target.extension.Equals(".irenderpipeline", StringComparison.OrdinalIgnoreCase); /// /// Binds a caller-visible label to the current inspection target. /// @@ -137,11 +156,10 @@ protected override bool CanInspect(AssetFileEntry target) /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - AssetFileEntry target -) - => (target.nameWithoutExtension, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + AssetFileEntry target + ) => (target.nameWithoutExtension, null); /// /// Renders the header presentation for the current editor frame. /// @@ -151,13 +169,14 @@ AssetFileEntry target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - AssetFileEntry target -) { + protected override void DrawHeader( + InspectionDrawContext context, + AssetFileEntry target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) PipelineInspector.DrawHeader(context, documents, documents.Open(target.assetPath)); } + /// /// Renders the value presentation for the current editor frame. /// @@ -167,10 +186,10 @@ AssetFileEntry target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - AssetFileEntry target -) { + protected override void Draw( + InspectionDrawContext context, + AssetFileEntry target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) PipelineInspector.Draw(context, documents, documents.Open(target.assetPath)); } @@ -205,39 +224,42 @@ internal static void Draw( Guid id ) { AssetDraftDocuments.Draft draft = documents.drafts.GetDraft(id); - documents.drafts.TouchInspection(id); RenderPipelineAsset pipeline = documents.Read(id); var edits = new DraftEdits(documents, id, pipeline); - ImGuiApi.PushID(id.ToString("N")); + ImGuiApi.PushId(id.ToString("N")); try { - if (Widget.SectionHeader("Render Pipeline", "This source is the configuration authority. Unsaved settings do not change Scene, Game or Player content.")) + if (EditorUI.current.widgets.SectionHeader( + "Render Pipeline", + "This source is the configuration authority. Unsaved settings do not change Scene, Game or Player content." + )) { - Widget.Hint(draft.isDirty ? "Unsaved changes · rendering unchanged" : "Saved · import and activation are separate"); + EditorUI.current.widgets.Hint(draft.isDirty ? "Unsaved changes · rendering unchanged" : "Saved · import and activation are separate"); if (draft.error.Length != 0) - Widget.Hint(draft.error); + EditorUI.current.widgets.Hint(draft.error); if (draft.readOnly) - Widget.Hint("Installed Pipeline · copy into the project to edit"); - Widget.Hint("Pipeline: " + pipeline.pipelineTypeId); + EditorUI.current.widgets.Hint("Installed Pipeline · copy into the project to edit"); + EditorUI.current.widgets.Hint("Pipeline: " + pipeline.pipelineTypeId); DrawSettings("pipeline.settings", pipeline.pipelineState, value => pipeline.pipelineState = value); } - bool featuresOpen = Widget.SectionHeader("Features", "Ordered extension settings belong to this Pipeline. Each enabled feature participates in its render graph."); + bool featuresOpen = EditorUI.current.widgets.SectionHeader( + "Features", + "Ordered extension settings belong to this Pipeline. Each enabled feature participates in its render graph." + ); RenderFeatureConfiguration[] features = pipeline.features; for (int index = 0; featuresOpen && index < features.Length; index++) { int slot = index; - ImGuiApi.PushID(slot); + ImGuiApi.PushId(slot); try { RenderFeatureConfiguration feature = features[slot]; - context.properties.DrawValue(context.editorContext, draft, "pipeline.features." + slot + ".enabled", - feature.featureTypeId, typeof(bool), () => features[slot].enabled, - value => - { - features[slot].enabled = (bool)value!; - pipeline.features = features; - }, edits, draft.readOnly); + context.properties.DrawValue(context.editorContext, draft, "pipeline.features." + slot + ".enabled", feature.featureTypeId, typeof(bool), () => features[slot].enabled, value => + { + features[slot].enabled = (bool)value!; + pipeline.features = features; + }, edits, draft.readOnly); DrawSettings("pipeline.features." + slot, feature.state, value => { features[slot].state = value; @@ -253,6 +275,7 @@ Guid id documents.Replace(id, pipeline); break; } + if (slot + 1 < features.Length) { if (slot > 0) @@ -273,17 +296,16 @@ Guid id } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } + if (featuresOpen && features.Length == 0) - Widget.Hint("No additional features"); - if (!ImGuiApi.IsAnyItemActive()) - documents.Commit(id); + EditorUI.current.widgets.Hint("No additional features"); } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } void DrawSettings( @@ -296,16 +318,18 @@ Action assign ISerializable? settings = documents.RestoreSettings(state); if (settings is null) { - Widget.Hint("No typed settings"); + EditorUI.current.widgets.Hint("No typed settings"); return; } + var settingsEdits = new SettingsEdits(edits, () => assign(new(documents.assets.CaptureProperties(settings)))); foreach (SerializedProperty property in documents.serialization.GetProperties(settings)) context.properties.DrawDraftProperty(context.editorContext, draft, settings, path, property, settingsEdits, draft.readOnly); } - catch (Exception error) when (PipelineDocuments.Recoverable(error)) + catch (Exception error) when ((error is System.IO.IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(error) is null) { - Widget.Hint("Settings unavailable · " + state.stableTypeId + " · " + error.Message + " · Stored properties and dependencies are retained."); + EditorUI.current.widgets.Hint("Settings unavailable · " + state.stableTypeId + " · " + error.Message + " · Stored properties and dependencies are retained."); } } } @@ -316,32 +340,17 @@ private sealed class DraftEdits( RenderPipelineAsset pipeline ) : IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) { + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) { mutation(); - documents.Replace(id, pipeline, !ImGuiApi.IsAnyItemActive()); + documents.Replace(id, pipeline, mergeKey); return true; } } @@ -351,34 +360,18 @@ private sealed class SettingsEdits( Action capture ) : IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) - => edits.ChangeProperty(owner, propertyName, () => - { - mutation(); - capture(); - }, historyName); + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) => edits.ChangeProperty(owner, propertyName, () => + { + mutation(); + capture(); + }, historyName, mergeKey, componentName); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs new file mode 100644 index 000000000..ebfbbb542 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Core.Identity; +using Inno.Core.Serialization; +using Inno.Editor.ImGui; +using Inno.Editor.Inspection; +using Inno.Editor.Interactions; +using Inno.Editor.Scene; +using Inno.Scene; + +namespace Inno.Editor.Panel.Inspector; +[InspectionDrawer(typeof(EditorSelectionSnapshot))] +internal sealed class SelectionInspectionDrawer( + SceneEdits edits, + SerializationRegistry serialization, + SceneProjectSettingsModule settings +) : InspectionDrawer +{ + private readonly GameObjectTagSelector m_tags = new(settings, edits); + private readonly GameObjectLayerSelector m_layers = new(settings, edits); + private string m_componentSearch = ""; + /// + public override string icon => ImGuiIcon.LayerGroup; + + /// + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) => ($"{target.identities.Count} Selected", null); + /// + protected override void Draw( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) { + var values = new List(target.identities.Count); + target.CopyTargets(context.interactions, values); + if (values.Count != target.identities.Count) + { + EditorUiSession.current.widgets.Hint("The selection includes unavailable or different content. Select compatible scene objects or assets to edit common values."); + return; + } + + if (values.Count == 0) + return; + int primaryIndex = values.FindIndex(value => value.identity.runtimeIdentity == target.primary); + if (primaryIndex > 0) + { + EngineObject primary = values[primaryIndex]; + values.RemoveAt(primaryIndex); + values.Insert(0, primary); + } + bool readOnly = values.Any(value => !edits.CanEdit(value)); + if (readOnly) + EditorUiSession.current.widgets.HelpBox("The selection includes a read-only owner. No batch changes will be applied.", ImGuiIcon.Lock, EditorPalette.warning); + EditorUiSession.current.widgets.Disabled(readOnly, () => + { + if (values.All(value => value is GameObject)) + DrawGameObjects(context, target, values.Cast().ToArray()); + else + DrawCommon(context, target, values, "selection"); + }); + } + + private void DrawGameObjects( + InspectionDrawContext context, + EditorSelectionSnapshot state, + IReadOnlyList targets + ) { + bool active = targets[0].activeSelf; + bool mixed = targets.Any(value => value.activeSelf != active); + EditorUiSession.current.widgets.MixedValue(mixed, () => + { + if (!EditorUiSession.current.widgets.CompactCheckbox("selection.active", ref active, tooltip: "Set the active state of every selected object.")) + return; + using EditorHistoryTransaction transaction = context.interactions.history.BeginTransaction("Set Selected Active"); + foreach (GameObject target in targets) + edits.SetGameObjectActive(target, active); + transaction.Commit(); + }); + EditorUiSession.current.widgets.Hint(mixed ? "Active \u00b7 Mixed values" : "Active"); + float width = MathF.Max(1, Inno.Editor.ImGui.ImGui.GetContentRegionAvailable().X); + m_tags.Draw(targets, context.interactions, width); + m_layers.Draw(targets, context.interactions, width); + IReadOnlyList primary = targets[0].GetComponents(); + var occurrences = new Dictionary(); + foreach (GameComponent component in primary) + { + Type type = component.GetType(); + int occurrence = occurrences.GetValueOrDefault(type); + occurrences[type] = occurrence + 1; + GameComponent? [] matches = targets.Select(value => value.GetComponents().Where(candidate => candidate.GetType() == type).Skip(occurrence).FirstOrDefault()).ToArray(); + if (matches.Any(value => value is null)) + continue; + if (EditorUiSession.current.widgets.SectionHeader($"{type.Name} ({occurrence + 1})", "Common component values. Changes apply atomically to the entire selection.")) + { + string id = $"selection.{type.FullName}.{occurrence}"; + DrawCommon(context, state, matches.Cast().ToArray(), id); + if (Inno.Editor.ImGui.ImGui.Button("Reset Selected##" + id)) + { + using EditorHistoryTransaction transaction = context.interactions.history.BeginTransaction("Reset Components"); + foreach (GameComponent match in matches.Cast()) + edits.ResetComponent(match); + transaction.Commit(); + } + + if (matches.All(value => value is not Inno.Scene.Components.Transform)) + { + Inno.Editor.ImGui.ImGui.SameLine(0, -1); + if (Inno.Editor.ImGui.ImGui.Button("Remove Selected##" + id)) + { + using EditorHistoryTransaction transaction = context.interactions.history.BeginTransaction("Remove Components"); + foreach (GameComponent match in matches.Cast()) + edits.RemoveComponent(match); + transaction.Commit(); + return; + } + } + } + } + + DrawAddComponents(context, targets[0]); + } + + private void DrawAddComponents( + InspectionDrawContext context, + GameObject primary + ) { + var widgets = EditorUiSession.current.widgets; + if (widgets.CenteredButton("Add Component to Selection", widgets.style.inspectorAddButtonTopPadding)) + { + m_componentSearch = ""; + Inno.Editor.ImGui.ImGui.OpenPopup("selection.add-component"); + } + + if (!widgets.BeginSearchPopup("selection.add-component", ref m_componentSearch, "Search components...", 256)) + return; + try + { + EditorInteraction interaction = context.interactions.For(InspectorInteractionIds.C_COMPONENT_AREA, primary); + if (EditorUiSession.current.menus.DrawSearchItems(interaction, interaction.BuildMenu().items, m_componentSearch)) + Inno.Editor.ImGui.ImGui.CloseCurrentPopup(); + } + finally + { + widgets.EndSearchPopup(); + } + } + + private void DrawCommon( + InspectionDrawContext context, + EditorSelectionSnapshot state, + IReadOnlyList targets, + string path + ) { + if (targets.Any(value => value is not ISerializable)) + { + EditorUiSession.current.widgets.Hint("These objects expose no common serialized properties."); + return; + } + + IReadOnlyList[] properties = targets.Select(value => serialization.GetProperties((ISerializable)value)).ToArray(); + int common = 0; + foreach (SerializedProperty property in properties[0]) + { + if (properties.Any(values => !values.Any(value => value.name == property.name && value.propertyType == property.propertyType))) + continue; + var availability = properties.Select(( + values, + index + ) => context.properties.GetPropertyAvailability( + targets[index], values.Single(value => value.name == property.name && value.propertyType == property.propertyType))).ToArray(); + if (availability.Any(value => !value.isVisible)) + continue; + bool readOnly = availability.Any(value => value.isReadOnly); + common++; + bool mixed = !edits.HaveSamePropertyValues(targets, property.name); + EditorUiSession.current.widgets.MixedValue( + mixed, + () => context.properties.DrawDraftProperty(context.editorContext, state, targets[0], path, property, new SelectionEdits(edits, targets), readOnly) + ); + if (mixed) + EditorUiSession.current.widgets.Hint("Mixed values"); + } + + if (common == 0) + EditorUiSession.current.widgets.Hint("No common editable properties."); + } + + private sealed class SelectionEdits( + SceneEdits edits, + IReadOnlyList targets + ) : IInspectionPropertyEditService + { + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) => edits.ChangeProperties(targets, propertyName, mutation, historyName, mergeKey, componentName); + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SystemEditorTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SystemEditorTarget.cs deleted file mode 100644 index e78339ceb..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SystemEditorTarget.cs +++ /dev/null @@ -1,42 +0,0 @@ - -using System; - -using Inno.Scene; - -namespace Inno.Editor.Panel.Inspector; - -/// -/// Identifies a system together with its owning scene. -/// -internal sealed class SystemEditorTarget -{ - /// - /// Creates an inspector action target that keeps a system paired with its owning scene. - /// - /// - /// The loaded scene that owns the system. - /// - /// - /// The system operated on by the inspector action. - /// - /// - /// Thrown when either argument is . - /// - internal SystemEditorTarget( - GameScene scene, - GameSystem system - ) { - this.scene = scene ?? throw new ArgumentNullException(nameof(scene)); - this.system = system ?? throw new ArgumentNullException(nameof(system)); - } - - /// - /// Gets the owning scene. - /// - public GameScene scene { get; } - - /// - /// Gets the system. - /// - public GameSystem system { get; } -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs index f868283fd..543aad1fa 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs @@ -1,20 +1,22 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; -using Inno.Adapter.Presentation.ImGui; using Inno.Core.Mathematics; using Inno.Editor.ImGui; using Inno.Editor.Inspection; +using Inno.Editor.Interactions; using Inno.Editor.Scene; using Inno.Scene.Components; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; - [InspectionDrawer(typeof(Transform))] -internal sealed unsafe class TransformInspectionDrawer(SceneEdits edits) : InspectionDrawer +internal sealed class TransformInspectionDrawer( + SceneEdits edits, + EditorInteractions interactions +) : InspectionDrawer { private bool m_world; - /// /// Gets the icon glyph used to represent this item in the editor. /// @@ -35,9 +37,7 @@ internal sealed unsafe class TransformInspectionDrawer(SceneEdits edits) : Inspe protected override (string name, Action? setter) BindName( InspectionDrawContext context, Transform target - ) - => ("Transform", null); - + ) => ("Transform", null); /// /// Renders the value presentation for the current editor frame. /// @@ -51,15 +51,11 @@ protected override void Draw( InspectionDrawContext context, Transform target ) { - if (!EditorWidget.SectionHeader( + if (!EditorUI.current.widgets.SectionHeader( m_world ? "World Space" : "Local Space", - m_world - ? "Position, rotation, and scale include the parent hierarchy. Edits are stored as local values and support Undo/Redo." - : "Position, rotation, and scale are relative to the parent. Rotation is expressed in degrees.", - () => _ = EditorWidget.CompactCheckbox( - "##transform_space", - ref m_world, - tooltip: m_world ? "Switch to Local Space." : "Switch to World Space."))) + m_world ? "Position, rotation, and scale include the parent hierarchy. Edits are stored as local values and support Undo/Redo." : "Position, rotation, and scale are relative to the parent. Rotation is expressed in degrees.", + () => _ = EditorUI.current.widgets.CompactCheckbox("##transform_space", ref m_world, tooltip: m_world ? "Switch to Local Space." : "Switch to World Space.") + )) { return; } @@ -76,24 +72,28 @@ Transform target invertible = false; } } + if (!invertible) - EditorWidget.HelpBox("World editing requires an invertible parent transform. Edit in Local Space or restore the parent's zero scale.", - ImGuiIcon.TriangleExclamation, EditorPalette.warning); - NativeImGui.BeginDisabled(!invertible); + EditorUI.current.widgets.HelpBox( + "World editing requires an invertible parent transform. Edit in Local Space or restore the parent's zero scale.", + ImGuiIcon.TriangleExclamation, + EditorPalette.warning + ); + Ui.BeginDisabled(!invertible); try { Vector3 position = m_world ? target.worldPosition : target.localPosition; Quaternion rotation = m_world ? target.worldRotation : target.localRotation; Vector3 euler = rotation.ToEulerAnglesZYXDegrees(); Vector3 scale = m_world ? target.worldScale : target.localScale; - DrawVector("Position", "Translation in world units.", position, value => Change(target, "localPosition", () => + DrawVector(EditPath(target, "localPosition"), "Position", "Translation in world units.", position, value => Change(target, "localPosition", () => { if (m_world) target.worldPosition = value; else target.localPosition = value; })); - DrawVector("Rotation", "Euler rotation in degrees.", euler, value => Change(target, "localRotation", () => + DrawVector(EditPath(target, "localRotation"), "Rotation", "Euler rotation in degrees.", euler, value => Change(target, "localRotation", () => { Quaternion next = Quaternion.FromEulerAnglesZYXDegrees(value); if (m_world) @@ -101,7 +101,7 @@ Transform target else target.localRotation = next; })); - DrawVector("Scale", "Scale along each axis. One preserves the original size.", scale, value => Change(target, "localScale", () => + DrawVector(EditPath(target, "localScale"), "Scale", "Scale along each axis. One preserves the original size.", scale, value => Change(target, "localScale", () => { if (m_world) target.worldScale = value; @@ -111,7 +111,7 @@ Transform target } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } } @@ -119,26 +119,42 @@ private void Change( Transform target, string property, Action mutation - ) - => edits.ChangeProperty(target, property, mutation, "Change Transform " + property, - $"transform:{target.identity.persistentId:N}:{property}:{m_world}"); + ) { + string path = EditPath(target, property); + EditorUI.current.widgets.BeginPropertyEdit(interactions.history, path, "Change Transform " + property); + try + { + _ = edits.ChangeProperty(target, property, mutation, "Change Transform " + property, EditorUI.current.widgets.editMergeKey); + } + catch + { + EditorUI.current.widgets.CancelEdits(); + throw; + } + } + private string EditPath( + Transform target, + string property + ) => $"transform:{target.identity.persistentId:N}:{property}:{m_world}"; private static void DrawVector( + string path, string label, string tooltip, Vector3 value, Action apply ) { - EditorWidget.PropertyRow("transform." + label, label, () => + EditorUI.current.widgets.PropertyRow("transform." + label, label, () => { - float width = MathF.Max(1f, (NativeImGui.GetContentRegionAvail().X - NativeImGui.Raw.GetStyle()->ItemSpacing.X * 2f) / 3f); - bool changed = EditorWidget.AxisDragFloat(label, "X", ref value.x, width); - NativeImGui.SameLine(0f, -1f); - changed |= EditorWidget.AxisDragFloat(label, "Y", ref value.y, width); - NativeImGui.SameLine(0f, -1f); - changed |= EditorWidget.AxisDragFloat(label, "Z", ref value.z, width); + float width = MathF.Max(1f, (Ui.GetContentRegionAvailable().X - UiDrawing.GetLayout().itemSpacing.X * 2f) / 3f); + bool changed = EditorUI.current.widgets.AxisDragFloat(label, "X", ref value.x, width); + Ui.SameLine(0f, -1f); + changed |= EditorUI.current.widgets.AxisDragFloat(label, "Y", ref value.y, width); + Ui.SameLine(0f, -1f); + changed |= EditorUI.current.widgets.AxisDragFloat(label, "Z", ref value.z, width); if (changed) apply(value); }, tooltip: tooltip); + EditorUI.current.widgets.CompletePropertyEdit(path); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs index d70a38f5f..e1c20853c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs @@ -1,11 +1,9 @@ using System; using System.Text; - using Inno.Editor.Diagnostics; namespace Inno.Editor.Panel.Logging; - -internal sealed class ConsoleEntryCopyTarget +internal readonly struct ConsoleEntryCopyTarget { internal ConsoleEntryCopyTarget( EditorConsoleOccurrence entry, @@ -23,15 +21,7 @@ private static string FormatFullText( int repeatCount ) { var builder = new StringBuilder(); - builder.Append('[') - .Append(entry.time.ToString("yyyy-MM-dd HH:mm:ss.fff")) - .Append("] [") - .Append(entry.kind) - .Append("] [") - .Append(entry.level) - .Append("] [") - .Append(entry.category) - .Append(']'); + builder.Append('[').Append(entry.time.ToString("yyyy-MM-dd HH:mm:ss.fff")).Append("] [").Append(entry.kind).Append("] [").Append(entry.level).Append("] [").Append(entry.category).Append(']'); builder.Append(' ').Append(entry.displayMessage); if (repeatCount > 1) builder.Append(" (x").Append(repeatCount).Append(')'); @@ -47,6 +37,7 @@ int repeatCount builder.Append(')'); } } + return builder.ToString(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryImGuiIdentity.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryImGuiIdentity.cs index fe0cf7550..1f4c557ba 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryImGuiIdentity.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryImGuiIdentity.cs @@ -1,16 +1,15 @@ -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - internal static class ConsoleEntryImGuiIdentity { internal static void Push(string identity) { - NativeImGui.PushID(identity); + Ui.PushId(identity); } internal static void Pop() { - NativeImGui.PopID(); + Ui.PopId(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs index 309cbf27f..f0a984352 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs @@ -1,46 +1,41 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; - using Inno.Core.Logging; using Inno.Editor.Core; using Inno.Editor.Diagnostics; using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - /// /// Displays append-only logs and current diagnostics in the unified editor Console. /// [EditorPanel("diagnostics.console", "Console", order: 400, menuPath: "Diagnostics")] -internal sealed unsafe class ConsolePanel : EditorPanel +internal sealed class ConsolePanel : EditorPanel { - #region State +#region State private readonly ConsolePanelContent m_content = new(); private readonly HashSet m_filterLevels = Enum.GetValues().ToHashSet(); private readonly LogLevel[] m_levels = Enum.GetValues(); private readonly HashSet m_openEntries = new(StringComparer.Ordinal); - private bool m_collapse = true; private long m_lastRevision = -1; private bool m_requestScrollToBottom = true; private readonly IEditorConsole m_console; private readonly EditorInteractions m_interactions; - #endregion - +#endregion /// /// Lets the Console entries region own vertical scrolling. /// public override bool allowScrolling => false; - #region Lifecycle +#region Lifecycle /// /// Creates the panel. /// @@ -51,7 +46,7 @@ internal sealed unsafe class ConsolePanel : EditorPanel /// The editor interaction entry point used by contextual entry commands. /// /// - /// Thrown when or is . + /// Thrown when or is . /// internal ConsolePanel( IEditorConsole console, @@ -91,36 +86,32 @@ protected override void Restore(EditorState state) /// protected override void OnDraw(EditorContext context) { - NativeImGui.BeginChild( - "ConsoleChild", - Vector2.Zero, - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); + Ui.BeginChild("ConsoleChild", Vector2.Zero, (ImGuiChildFlags.None), (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); DrawToolbar(); - NativeImGui.Separator(); + Ui.Separator(); DrawConsoleRegion(); - NativeImGui.EndChild(); + Ui.EndChild(); } - #endregion - #region Toolbar +#endregion +#region Toolbar private void DrawToolbar() { - bool collapseChanged = NativeImGui.Checkbox("Collapse", ref m_collapse); + bool collapseChanged = Ui.Checkbox("Collapse", ref m_collapse); if (collapseChanged) { m_requestScrollToBottom = true; } - NativeImGui.SameLine(0f, -1f); + Ui.SameLine(0f, -1f); bool filterChanged = DrawFilterCombo(); if (filterChanged) { m_requestScrollToBottom = true; } - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.Button("Clear")) + Ui.SameLine(0f, -1f); + if (Ui.Button("Clear")) { m_console.Clear(); m_openEntries.Clear(); @@ -130,10 +121,7 @@ private void DrawToolbar() private bool DrawFilterCombo() { - if (!EditorWidget.BeginBoundedCombo( - "##ConsoleFilter", - "Filter", - ImGuiComboFlags.WidthFitPreview)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##ConsoleFilter", "Filter", ImGuiComboFlags.WidthFitPreview)) { return false; } @@ -143,7 +131,7 @@ private bool DrawFilterCombo() { LogLevel level = m_levels[i]; bool selected = m_filterLevels.Contains(level); - if (!NativeImGui.Checkbox(level.ToString(), ref selected)) + if (!Ui.Checkbox(level.ToString(), ref selected)) { continue; } @@ -160,38 +148,33 @@ private bool DrawFilterCombo() changed = true; } - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); return changed; } - #endregion - - #region Console Region +#endregion +#region Console Region private void DrawConsoleRegion() { - NativeImGui.BeginChild("ConsoleRegion", Vector2.Zero, 0, 0); - + Ui.BeginChild("ConsoleRegion", Vector2.Zero, 0, 0); EditorConsoleSnapshot snapshot = m_console.Capture(); RemoveStaleOpenEntries(snapshot); bool hasNewEntries = m_lastRevision >= 0 && snapshot.revision != m_lastRevision; m_lastRevision = snapshot.revision; - - bool scrollAtBottom = NativeImGui.GetScrollY() >= - NativeImGui.GetScrollMaxY() - EditorWidget.style.logAutoScrollTolerance; + bool scrollAtBottom = Ui.GetScrollY() >= Ui.GetScrollMaxY() - EditorUI.current.widgets.style.logAutoScrollTolerance; bool shouldScrollToBottom = m_requestScrollToBottom || (hasNewEntries && scrollAtBottom); DrawEntries(snapshot); - if (shouldScrollToBottom) { - NativeImGui.SetScrollHereY(1f); + Ui.SetScrollHereY(1f); m_requestScrollToBottom = false; } - NativeImGui.EndChild(); + Ui.EndChild(); } - #endregion - #region Entries +#endregion +#region Entries private void DrawEntries(EditorConsoleSnapshot snapshot) { if (snapshot.occurrences.Count == 0) @@ -209,11 +192,7 @@ private void DrawEntries(EditorConsoleSnapshot snapshot) if (!m_filterLevels.Contains(group.latest.level)) continue; visibleCount++; - DrawConsoleEntry( - group.latest, - group.occurrences, - group.count, - $"group/{group.identity}"); + DrawConsoleEntry(group.latest, group.occurrences, group.count, $"group/{group.identity}"); } } else @@ -224,20 +203,16 @@ private void DrawEntries(EditorConsoleSnapshot snapshot) if (!m_filterLevels.Contains(occurrence.level)) continue; visibleCount++; - DrawConsoleEntry( - occurrence, - [occurrence], - 1, - $"occurrence/{occurrence.sequence}"); + DrawConsoleEntry(occurrence, [occurrence], 1, $"occurrence/{occurrence.sequence}"); } } if (visibleCount == 0) m_content.DrawDisabledHint("All logs are filtered out."); } - #endregion - #region Entry Card +#endregion +#region Entry Card private void DrawConsoleEntry( EditorConsoleOccurrence entry, IReadOnlyList occurrences, @@ -245,83 +220,77 @@ private void DrawConsoleEntry( string identity ) { (Vector4 levelColor, string levelIcon) = m_content.GetLevelVisual(entry.level); - ConsoleEntryImGuiIdentity.Push(identity); bool open = m_openEntries.Contains(identity); if (!open) { Vector4 collapsedCardBg = m_content.GetCollapsedBgColor(); Vector4 collapsedCardBorder = m_content.GetCollapsedBorderColor(); - NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), collapsedCardBg); - NativeImGui.PushStyleColor((int)(ImGuiCol.Border), collapsedCardBorder); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameRounding), NativeImGui.Raw.GetStyle()->FrameRounding); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), EditorWidget.style.borderSize); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.framePadding); - + Ui.PushStyleColor((ImGuiCol.FrameBg), collapsedCardBg); + Ui.PushStyleColor((ImGuiCol.Border), collapsedCardBorder); + Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), UiDrawing.GetLayout().frameRounding); + Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), EditorUI.current.widgets.style.borderSize); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.framePadding); ImGuiChildFlags collapsedChildFlags = ImGuiChildFlags.FrameStyle; ImGuiWindowFlags collapsedChildWindowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; - float collapsedHeight = NativeImGui.GetFrameHeight() + NativeImGui.Raw.GetStyle()->FramePadding.Y * 2f; - if (NativeImGui.BeginChild("##ConsoleEntryCard", new Vector2(0f, collapsedHeight), (int)(collapsedChildFlags), (int)(collapsedChildWindowFlags))) + float collapsedHeight = Ui.GetFrameHeight() + UiDrawing.GetLayout().framePadding.Y * 2f; + if (Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, collapsedHeight), collapsedChildFlags, collapsedChildWindowFlags)) { DrawHeader(entry, repeatCount, levelColor, levelIcon, false, collapsedCardBg, out bool toggled); if (toggled) m_openEntries.Add(identity); } - NativeImGui.EndChild(); + Ui.EndChild(); DrawEntryContextMenu(entry, repeatCount); - NativeImGui.PopStyleVar(3); - NativeImGui.PopStyleColor(2); + Ui.PopStyleVar(3); + Ui.PopStyleColor(2); ConsoleEntryImGuiIdentity.Pop(); return; } Vector4 cardBg = m_content.GetExpandedBgColor(levelColor); Vector4 cardBorder = EditorPalette.GetLogExpandedBorder(levelColor); - NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), cardBg); - NativeImGui.PushStyleColor((int)(ImGuiCol.Border), cardBorder); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameRounding), NativeImGui.Raw.GetStyle()->FrameRounding); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), EditorWidget.style.borderSize); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.framePadding); - + Ui.PushStyleColor((ImGuiCol.FrameBg), cardBg); + Ui.PushStyleColor((ImGuiCol.Border), cardBorder); + Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), UiDrawing.GetLayout().frameRounding); + Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), EditorUI.current.widgets.style.borderSize); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.framePadding); ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | ImGuiChildFlags.AutoResizeY; ImGuiWindowFlags childWindowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; - if (NativeImGui.BeginChild("##ConsoleEntryCard", new Vector2(0f, 0f), (int)(childFlags), (int)(childWindowFlags))) + if (Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, 0f), childFlags, childWindowFlags)) { DrawHeader(entry, repeatCount, levelColor, levelIcon, true, cardBg, out bool toggled); if (toggled) m_openEntries.Remove(identity); - - NativeImGui.PushStyleColor( - (int)(ImGuiCol.Separator), - EditorPalette.GetLogSeparator(cardBg)); + Ui.PushStyleColor((ImGuiCol.Separator), EditorPalette.GetLogSeparator(cardBg)); try { - NativeImGui.Separator(); + Ui.Separator(); for (int i = 0; i < occurrences.Count; i++) { if (i > 0) - NativeImGui.Separator(); - NativeImGui.PushID(unchecked((int)occurrences[i].sequence)); + Ui.Separator(); + Ui.PushId(unchecked((int)occurrences[i].sequence)); DrawDetail(occurrences[i]); - NativeImGui.PopID(); + Ui.PopId(); } } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } - NativeImGui.EndChild(); + Ui.EndChild(); DrawEntryContextMenu(entry, repeatCount); - NativeImGui.PopStyleVar(3); - NativeImGui.PopStyleColor(2); + Ui.PopStyleVar(3); + Ui.PopStyleColor(2); ConsoleEntryImGuiIdentity.Pop(); } - #endregion - #region Entry Header / Detail +#endregion +#region Entry Header / Detail private void DrawHeader( EditorConsoleOccurrence entry, int repeatCount, @@ -332,85 +301,75 @@ private void DrawHeader( out bool toggled ) { toggled = false; - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - + EditorUiLayout style = UiDrawing.GetLayout(); string content = isOpen ? entry.displayMessage : m_content.GetFirstLine(entry.displayMessage); string repeatText = repeatCount > 1 ? $" (x{repeatCount})" : string.Empty; string prefix = $"{levelIcon} [{entry.level}] "; string toggleText = isOpen ? "▼" : "▶"; - - float toggleW = NativeImGui.CalcTextSize(toggleText).X + EditorWidget.style.logDisclosurePadding.X * 2f; - float prefixW = NativeImGui.CalcTextSize(prefix).X + toggleW + style->ItemInnerSpacing.X; - float suffixW = NativeImGui.CalcTextSize(repeatText).X; + float toggleW = Ui.MeasureText(toggleText).X + EditorUI.current.widgets.style.logDisclosurePadding.X * 2f; + float prefixW = Ui.MeasureText(prefix).X + toggleW + style.itemInnerSpacing.X; + float suffixW = Ui.MeasureText(repeatText).X; float suffixColW = MathF.Max(1f, suffixW); - float tableOuterW = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); + float tableOuterW = MathF.Max(1f, Ui.GetContentRegionAvailable().X); float contentW = MathF.Max(1f, tableOuterW - prefixW - suffixColW); - if (!isOpen) { content = m_content.FitTextWithEllipsis(m_content.GetFirstLine(entry.displayMessage), contentW); } Vector2 tableOuterSize = new(tableOuterW, 0f); - ImGuiTableFlags flags = - ImGuiTableFlags.SizingFixedFit | - ImGuiTableFlags.NoHostExtendX | - ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoPadInnerX | - ImGuiTableFlags.NoSavedSettings; - - if (NativeImGui.BeginTable("##HeaderTable", 3, (int)(flags), tableOuterSize)) + ImGuiTableFlags flags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoPadInnerX | ImGuiTableFlags.NoSavedSettings; + if (Ui.BeginTable("##HeaderTable", 3, flags, tableOuterSize)) { - NativeImGui.TableSetupColumn("##prefix", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), prefixW); - NativeImGui.TableSetupColumn("##content", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), contentW); - NativeImGui.TableSetupColumn("##suffix", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), suffixColW); - - NativeImGui.TableNextRow(); + Ui.TableSetupColumn("##prefix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), prefixW); + Ui.TableSetupColumn("##content", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), contentW); + Ui.TableSetupColumn("##suffix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), suffixColW); + Ui.TableNextRow(); if (isOpen) { - uint headerBgU32 = NativeImGui.ColorConvertFloat4ToU32(headerBgColor); - NativeImGui.TableSetBgColor((int)(ImGuiTableBgTarget.RowBg0), headerBgU32); + uint headerBgU32 = Ui.ToPackedColor(headerBgColor); + Ui.TableSetBgColor((ImGuiTableBgTarget.RowBg0), headerBgU32); } - _ = NativeImGui.TableSetColumnIndex(0); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorWidget.style.logDisclosurePadding); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), 0f); - NativeImGui.PushStyleColor((int)(ImGuiCol.Button), EditorPalette.logToggle); - NativeImGui.PushStyleColor((int)(ImGuiCol.ButtonHovered), EditorPalette.logToggleHovered); - NativeImGui.PushStyleColor((int)(ImGuiCol.ButtonActive), EditorPalette.logToggleActive); - if (NativeImGui.SmallButton($"{toggleText}##HeaderToggle")) + _ = Ui.TableSetColumnIndex(0); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.logDisclosurePadding); + Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), 0f); + Ui.PushStyleColor((ImGuiCol.Button), EditorPalette.logToggle); + Ui.PushStyleColor((ImGuiCol.ButtonHovered), EditorPalette.logToggleHovered); + Ui.PushStyleColor((ImGuiCol.ButtonActive), EditorPalette.logToggleActive); + if (Ui.SmallButton($"{toggleText}##HeaderToggle")) { toggled = true; } - NativeImGui.PopStyleColor(3); - NativeImGui.PopStyleVar(2); - NativeImGui.SameLine(0f, -1f); - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), levelColor); - NativeImGui.TextUnformatted(prefix); - NativeImGui.PopStyleColor(); - - _ = NativeImGui.TableSetColumnIndex(1); + + Ui.PopStyleColor(3); + Ui.PopStyleVar(2); + Ui.SameLine(0f, -1f); + Ui.PushStyleColor((ImGuiCol.Text), levelColor); + Ui.Text(prefix); + Ui.PopStyleColor(); + _ = Ui.TableSetColumnIndex(1); if (isOpen) { - NativeImGui.PushTextWrapPos(0f); - NativeImGui.TextUnformatted(content); - NativeImGui.PopTextWrapPos(); + Ui.PushTextWrapPos(0f); + Ui.Text(content); + Ui.PopTextWrapPos(); } else { - NativeImGui.TextUnformatted(content); + Ui.Text(content); } - _ = NativeImGui.TableSetColumnIndex(2); + _ = Ui.TableSetColumnIndex(2); if (!string.IsNullOrEmpty(repeatText)) { - float remainW = NativeImGui.GetContentRegionAvail().X; + float remainW = Ui.GetContentRegionAvailable().X; float suffixOffsetX = MathF.Max(0f, remainW - suffixW); - NativeImGui.SetCursorPosX(NativeImGui.GetCursorPosX() + suffixOffsetX); - NativeImGui.TextUnformatted(repeatText); + Ui.SetCursorPosX(Ui.GetCursorPosX() + suffixOffsetX); + Ui.Text(repeatText); } - NativeImGui.EndTable(); + Ui.EndTable(); } } @@ -424,16 +383,11 @@ private static void DrawDetail(EditorConsoleOccurrence entry) } string fileText = string.IsNullOrWhiteSpace(entry.file) ? "-" : entry.file; - string fileWithLineText = entry.line > 0 - ? entry.column > 0 - ? $"{fileText}:{entry.line}:{entry.column}" - : $"{fileText}:{entry.line}" - : fileText; - + string fileWithLineText = entry.line > 0 ? entry.column > 0 ? $"{fileText}:{entry.line}:{entry.column}" : $"{fileText}:{entry.line}" : fileText; // These cards may be culled between frames; do not rely on a previous frame's auto-fit width. - float labelWidth = NativeImGui.CalcTextSize("Session:").X; - float availableWidth = NativeImGui.GetContentRegionAvail().X; - if (availableWidth < labelWidth + NativeImGui.CalcTextSize("Diagnostic").X + NativeImGui.Raw.GetStyle()->CellPadding.X * 4) + float labelWidth = Ui.MeasureText("Session:").X; + float availableWidth = Ui.GetContentRegionAvailable().X; + if (availableWidth < labelWidth + Ui.MeasureText("Diagnostic").X + UiDrawing.GetLayout().cellPadding.X * 4) { DrawStackedField("Kind:", entry.kind.ToString()); DrawStackedField("File:", fileWithLineText); @@ -442,26 +396,25 @@ private static void DrawDetail(EditorConsoleOccurrence entry) DrawStackedField("Session:", entry.sessionId.ToString()); DrawStackedField("Time:", timeText); } - else if (NativeImGui.BeginTable("##ConsoleEntryDetails", 2, (int)(ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings), - new Vector2(availableWidth, 0))) + else if (Ui.BeginTable("##ConsoleEntryDetails", 2, (ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings), new Vector2(availableWidth, 0))) { - NativeImGui.TableSetupColumn("##label", (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), labelWidth); - NativeImGui.TableSetupColumn("##value", (int)(ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), 1f); + Ui.TableSetupColumn("##label", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), labelWidth); + Ui.TableSetupColumn("##value", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), 1f); DrawDetailFieldRow("Kind:", entry.kind.ToString()); DrawDetailFieldRow("File:", fileWithLineText); DrawDetailFieldRow("Source:", sourceText); if (entry.sessionId.isAssigned) DrawDetailFieldRow("Session:", entry.sessionId.ToString()); DrawDetailFieldRow("Time:", timeText); - NativeImGui.EndTable(); + Ui.EndTable(); } if (!string.IsNullOrWhiteSpace(entry.stackTrace)) { - NativeImGui.Separator(); - NativeImGui.PushTextWrapPos(0f); - NativeImGui.TextUnformatted(entry.stackTrace); - NativeImGui.PopTextWrapPos(); + Ui.Separator(); + Ui.PushTextWrapPos(0f); + Ui.Text(entry.stackTrace); + Ui.PopTextWrapPos(); } } @@ -469,11 +422,7 @@ private void DrawEntryContextMenu( EditorConsoleOccurrence entry, int repeatCount ) { - _ = EditorMenuRenderer.ContextMenu( - "##ConsoleEntryContextMenu", - m_interactions.For( - LoggingInteractionIds.C_ENTRY_AREA, - new ConsoleEntryCopyTarget(entry, repeatCount))); + _ = EditorUI.current.menus.ContextMenu("##ConsoleEntryContextMenu", m_interactions.For(LoggingInteractionIds.C_ENTRY_AREA, new ConsoleEntryCopyTarget(entry, repeatCount))); } private void RemoveStaleOpenEntries(EditorConsoleSnapshot snapshot) @@ -492,24 +441,23 @@ private static void DrawDetailFieldRow( string label, string value ) { - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); - _ = NativeImGui.TableSetColumnIndex(1); - NativeImGui.PushTextWrapPos(0f); - NativeImGui.TextUnformatted(value); - NativeImGui.PopTextWrapPos(); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); + _ = Ui.TableSetColumnIndex(1); + Ui.PushTextWrapPos(0f); + Ui.Text(value); + Ui.PopTextWrapPos(); } private static void DrawStackedField( string label, string value ) { - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); - NativeImGui.PushTextWrapPos(0f); - NativeImGui.TextUnformatted(value); - NativeImGui.PopTextWrapPos(); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); + Ui.PushTextWrapPos(0f); + Ui.Text(value); + Ui.PopTextWrapPos(); } - #endregion - +#endregion } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs index e4be43156..be07e53fb 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs @@ -1,14 +1,10 @@ using System; using System.Numerics; - using Inno.Core.Logging; using Inno.Editor.ImGui; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - internal sealed class ConsolePanelContent { private const string C_ICON_BUG = ImGuiIcon.Bug; @@ -17,12 +13,11 @@ internal sealed class ConsolePanelContent private const string C_ICON_ERROR = ImGuiIcon.CircleXmark; private const string C_ICON_FATAL = ImGuiIcon.SkullCrossbones; private const string C_ICON_DEFAULT = ImGuiIcon.FileLines; - internal void DrawDisabledHint(string text) { - NativeImGui.BeginDisabled(true); - NativeImGui.TextUnformatted(text); - NativeImGui.EndDisabled(); + Ui.BeginDisabled(true); + Ui.Text(text); + Ui.EndDisabled(); } internal (Vector4 color, string icon) GetLevelVisual(LogLevel level) @@ -34,16 +29,12 @@ internal void DrawDisabledHint(string text) LogLevel.Warn => (EditorPalette.logWarning, C_ICON_WARN), LogLevel.Error => (EditorPalette.logError, C_ICON_ERROR), LogLevel.Fatal => (EditorPalette.logFatal, C_ICON_FATAL), - _ => (EditorPalette.text, C_ICON_DEFAULT) - }; + _ => (EditorPalette.text, C_ICON_DEFAULT)}; } internal Vector4 GetCollapsedBgColor() => EditorPalette.logCollapsedCard; - internal Vector4 GetCollapsedBorderColor() => EditorPalette.logCollapsedBorder; - internal Vector4 GetExpandedBgColor(Vector4 levelColor) => EditorPalette.GetLogExpandedCard(levelColor); - internal string GetFirstLine(string text) { if (string.IsNullOrEmpty(text)) @@ -53,6 +44,7 @@ internal string GetFirstLine(string text) if (text[i] is '\n' or '\r') return text[..i]; } + return text; } @@ -65,23 +57,23 @@ float maxWidth return string.Empty; if (maxWidth <= 1f) return c_ellipsis; - if (NativeImGui.CalcTextSize(text).X <= maxWidth) + if (Ui.MeasureText(text).X <= maxWidth) return text; - float ellipsisWidth = NativeImGui.CalcTextSize(c_ellipsis).X; + float ellipsisWidth = Ui.MeasureText(c_ellipsis).X; if (ellipsisWidth >= maxWidth) return c_ellipsis; - float maxTextWidth = maxWidth - ellipsisWidth; int low = 0; int high = text.Length; while (low < high) { int middle = (low + high + 1) / 2; - if (NativeImGui.CalcTextSize(text[..middle]).X <= maxTextWidth) + if (Ui.MeasureText(text[..middle]).X <= maxTextWidth) low = middle; else high = middle - 1; } + return low <= 0 ? c_ellipsis : text[..low] + c_ellipsis; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs index f6b61c262..3e65dcebc 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs @@ -1,8 +1,7 @@ using Inno.Editor.Interactions; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - [EditorAction(LoggingInteractionIds.C_COPY_DETAILS, LoggingInteractionIds.C_ENTRY_AREA)] [EditorMenu(LoggingInteractionIds.C_ENTRY_AREA, "Copy Full Entry", order: 110)] internal sealed class CopyConsoleDetailsCommand : EditorAction @@ -13,6 +12,5 @@ internal sealed class CopyConsoleDetailsCommand : EditorAction /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) - => NativeImGui.SetClipboardText(context.target.fullText); + protected override void Execute(EditorActionContext context) => Ui.SetClipboardText(context.target.fullText); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs index b2238b061..c96125f25 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs @@ -1,8 +1,7 @@ using Inno.Editor.Interactions; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - [EditorAction(LoggingInteractionIds.C_COPY_MESSAGE, LoggingInteractionIds.C_ENTRY_AREA)] [EditorMenu(LoggingInteractionIds.C_ENTRY_AREA, "Copy Message", order: 100)] internal sealed class CopyConsoleMessageCommand : EditorAction @@ -13,6 +12,5 @@ internal sealed class CopyConsoleMessageCommand : EditorAction /// The operation scope that provides state, services, and ownership boundaries. /// - protected override void Execute(EditorActionContext context) - => NativeImGui.SetClipboardText(context.target.message); + protected override void Execute(EditorActionContext context) => Ui.SetClipboardText(context.target.message); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj index bd58d3070..64e593ed5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Inno.Editor.Panel.Logging.csproj @@ -4,7 +4,6 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor @@ -12,31 +11,13 @@ - - - - - - + + + + - - + + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Logging/LoggingModule.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Logging/LoggingModule.cs index b2281ee74..852bfebe8 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Logging/LoggingModule.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Logging/LoggingModule.cs @@ -1,5 +1,4 @@ using System; - using Inno.Core.Diagnostics; using Inno.Core.Logging; using Inno.Editor.Core; @@ -8,13 +7,12 @@ using Inno.Editor.Settings; namespace Inno.Editor.Panel.Logging; - [EditorModule("diagnostics-logging", order: int.MinValue)] internal sealed class LoggingModule : EditorModule, IEditorConsole { + private IDisposable? m_settingsSubscription; private readonly EditorConsole m_console; private readonly EditorSettings m_settings; - internal LoggingModule( LogRouter logRouter, DiagnosticHub diagnosticHub, @@ -34,7 +32,7 @@ EditorSettings settings protected override void OnStart(EditorContext context) { ApplySettings(m_settings); - m_settings.changed += ApplySettings; + m_settingsSubscription = m_settings.ListenChanges(ApplySettings); m_console.Start(); } @@ -46,32 +44,24 @@ protected override void OnStart(EditorContext context) /// protected override void OnStop(EditorContext context) { - m_settings.changed -= ApplySettings; + m_settingsSubscription?.Dispose(); m_console.Dispose(); } /// /// Releases resources retained by this feature after it has stopped. /// - protected override void OnDispose() => m_console.Dispose(); - - private void ApplySettings(EditorSettings settings) => m_console.clearOnPlay = ClearConsoleOnPlaySetting.Read(settings); - - int IEditorConsole.capacity + protected override void OnDispose() { - get => m_console.capacity; - set => m_console.capacity = value; + m_settingsSubscription?.Dispose(); + m_console.Dispose(); } - bool IEditorConsole.clearOnPlay - { - get => m_console.clearOnPlay; - set => m_console.clearOnPlay = value; - } + private void ApplySettings(EditorSettings settings) => m_console.clearOnPlay = ClearConsoleOnPlaySetting.Read(settings); + int IEditorConsole.capacity { get => m_console.capacity; set => m_console.capacity = value; } - EditorConsoleSnapshot IEditorConsole.Capture() - => m_console.Capture(); + bool IEditorConsole.clearOnPlay { get => m_console.clearOnPlay; set => m_console.clearOnPlay = value; } - void IEditorConsole.Clear() - => m_console.Clear(); + EditorConsoleSnapshot IEditorConsole.Capture() => m_console.Capture(); + void IEditorConsole.Clear() => m_console.Clear(); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Settings/ConsoleSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Settings/ConsoleSettings.cs index 8e3222eec..ef1b31961 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Settings/ConsoleSettings.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Settings/ConsoleSettings.cs @@ -1,8 +1,7 @@ using Inno.Editor.Settings; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; - [EditorSettingPath("Editor/Diagnostics")] internal sealed class DiagnosticsSettingsPage : EditorSetting { @@ -25,7 +24,6 @@ internal sealed class ConsoleSettingsPage : EditorSetting internal sealed class ClearConsoleOnPlaySetting : EditorSetting { internal const string C_PATH = "Editor/Diagnostics/Console/Clear on Play"; - /// /// Gets a new value initialized to the canonical Console retention policy. /// @@ -43,12 +41,10 @@ public override EditorSettingObject defaultValue /// Gets the presentation section that groups this setting. /// public override string section => "Play Mode"; - /// /// Gets the user-facing explanation of this feature or setting. /// - public override string description - => "Clear ordinary Console logs when a new Play Mode request begins while retaining active diagnostics."; + public override string description => "Clear ordinary Console logs when a new Play Mode request begins while retaining active diagnostics."; /// /// Draws the staged Console retention preference. @@ -59,7 +55,7 @@ public override string description protected override void OnDraw(EditorSettingObject setting) { bool value = setting.GetAsBoolean("value", true); - if (NativeImGui.Checkbox("Clear ordinary logs when entering Play Mode", ref value)) + if (Ui.Checkbox("Clear ordinary logs when entering Play Mode", ref value)) setting.SetAsBoolean("value", value); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/Inno.Editor.Panel.SceneView.csproj b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/Inno.Editor.Panel.SceneView.csproj index 8cca2b8d4..7e0763a1a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/Inno.Editor.Panel.SceneView.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/Inno.Editor.Panel.SceneView.csproj @@ -7,45 +7,21 @@ true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - true - - - + - + - - - + + + - - - + + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs index add1c4479..b54711e94 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs @@ -1,28 +1,23 @@ using System.Numerics; - using Inno.Editor.ImGui; using Inno.Editor.Settings; using EditorImGui = Inno.Editor.ImGui.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.SceneView; - [EditorSettingPath(C_PATH)] internal sealed class SceneViewBackgroundSetting : EditorSetting { internal const string C_PATH = "Editor/Appearance/Viewports/Scene Background"; private static readonly float[] S_DEFAULT = [0.11f, 0.12f, 0.14f, EditorPalette.opacityOpaque]; - /// /// Gets a new value initialized to this setting's canonical default state. /// public override EditorSettingObject defaultValue => CreateDefault(); - /// /// Gets the presentation section that groups this setting. /// public override string section => "Viewport Backgrounds"; - /// /// Gets the user-facing explanation of this feature or setting. /// @@ -37,13 +32,12 @@ internal sealed class SceneViewBackgroundSetting : EditorSetting protected override void OnDraw(EditorSettingObject setting) { Vector4 value = ReadVector(setting); - NativeImGui.SetNextItemWidth(-1f); + Ui.SetNextItemWidth(-1f); if (EditorImGui.ColorEditLinear4("##scene_view_background", ref value)) setting.SetAsSingleArray("value", [value.X, value.Y, value.Z, value.W]); } internal static Vector4 Read(EditorSettings settings) => ReadVector(settings.Get(C_PATH)); - private static EditorSettingObject CreateDefault() { var result = new EditorSettingObject(); @@ -54,8 +48,6 @@ private static EditorSettingObject CreateDefault() private static Vector4 ReadVector(EditorSettingObject setting) { float[] values = setting.GetAsSingleArray("value", S_DEFAULT); - return values.Length == 4 - ? new Vector4(values[0], values[1], values[2], values[3]) - : new Vector4(S_DEFAULT[0], S_DEFAULT[1], S_DEFAULT[2], S_DEFAULT[3]); + return values.Length == 4 ? new Vector4(values[0], values[1], values[2], values[3]) : new Vector4(S_DEFAULT[0], S_DEFAULT[1], S_DEFAULT[2], S_DEFAULT[3]); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs index bd0657f2b..ca7fa87fe 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs @@ -1,3 +1,5 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using EditorImGui = Inno.Editor.ImGui.ImGui; using Inno.References; using Inno.Core.Identity; @@ -5,8 +7,6 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using BGCS.Runtime; - using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; @@ -17,33 +17,28 @@ using Inno.Editor.Settings; using Inno.Scene; using Inno.Scene.Components; -using Inno.Native.ImGui; -using ImGuizmoMode = Inno.Native.ImGuizmo.ImGuizmoMode; -using ImGuizmoOperation = Inno.Native.ImGuizmo.ImGuizmoOperation; -using Inno.Adapter.Presentation.ImGui; +using ImGuizmoMode = Inno.Editor.ImGui.ImGuiWidget.TransformGizmoSpace; +using ImGuizmoOperation = Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation; using Inno.Rendering; -using NativeImGui = Inno.Native.ImGui.ImGui; -using NativeImGuizmo = Inno.Native.ImGuizmo.ImGuizmo; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using Ui = Inno.Editor.ImGui.ImGui; using EngineMatrix = Inno.Core.Mathematics.Matrix; using EngineQuaternion = Inno.Core.Mathematics.Quaternion; using EngineVector3 = Inno.Core.Mathematics.Vector3; namespace Inno.Editor.Panel.SceneView; - /// /// Presents the active Plugin provider and host-owned transform manipulation for the Scene viewport. /// [EditorPanel("rendering.scene-view", "Scene", order: 210, menuPath: "Viewports")] -internal sealed unsafe class SceneViewPanel : EditorPanel +internal sealed class SceneViewPanel : EditorPanel { + private IDisposable? m_settingsSubscription; private const string C_VIEWPORT_ID = "scene-view"; private const string C_GIZMO_CLUSTER_POPUP = "##scene-gizmo-cluster"; private const int C_MANIPULATION_TOOL_COUNT = 4; - private const float C_IMGUIZMO_AXIS_LIMIT_DEFAULT = 0.0025f; + private const string C_TRANSFORM_EDIT = "scene-view.transform"; private static readonly EditorViewportKindId S_KIND = new("inno.editor.viewport.scene"); private static readonly Vector2 S_UNAVAILABLE_PADDING = new(48f, 32f); - private readonly EditorRenderingModule m_rendering; private readonly EditorInteractions m_interactions; private readonly SceneEdits m_sceneEdits; @@ -56,10 +51,9 @@ internal sealed unsafe class SceneViewPanel : EditorPanel private ImGuiMouseButton m_navigationDragButton; private ImGuizmoOperation m_operation = ImGuizmoOperation.Translate; private ImGuizmoMode m_mode = ImGuizmoMode.World; - private Transform? m_gestureTarget; - private TransformSnapshot m_gestureBefore; + private RuntimeIdentity? m_gestureTarget; + private RuntimeIdentity[] m_gestureTargets = []; private Identity[] m_popupGizmoOwners = []; - internal SceneViewPanel( EditorRenderingModule rendering, EditorInteractions interactions, @@ -80,7 +74,6 @@ EditorSettings settings /// Gets whether use window padding is enabled for this implementation. /// public override bool useWindowPadding => false; - /// /// Gets whether allow scrolling is enabled for this implementation. /// @@ -96,43 +89,29 @@ protected override void OnDraw(EditorContext context) { using IDisposable presentationScope = m_playModeLoop.EnterPresentationScope(); _ = context; - if (m_gestureTarget is not null && !NativeImGui.IsMouseDown((int)ImGuiMouseButton.Left)) + if (m_gestureTarget is not null && !Ui.IsMouseButtonDown(ImGuiMouseButton.Left)) CommitGesture(); - - Vector2 available = NativeImGui.GetContentRegionAvail(); + Vector2 available = Ui.GetContentRegionAvailable(); if (available.X <= 0f || available.Y <= 0f) return; int width = Math.Max(1, (int)MathF.Floor(available.X)); int height = Math.Max(1, (int)MathF.Floor(available.Y)); - m_rendering.SetPresentation( - C_VIEWPORT_ID, - new EditorViewportPresentation(ToEngineColor(m_backgroundColor))); + m_rendering.SetPresentation(C_VIEWPORT_ID, new EditorViewportPresentation(ToEngineColor(m_backgroundColor))); m_rendering.SetContentScope(C_VIEWPORT_ID, CreateContentScope()); - Vector2 minimum = NativeImGui.GetCursorScreenPos(); + Vector2 minimum = Ui.GetCursorScreenPosition(); Vector2 maximum = minimum + new Vector2(width, height); ManipulationToolbarLayout toolbar = CreateManipulationToolbarLayout(minimum, maximum); - bool hovered = NativeImGui.IsWindowHovered((int)ImGuiHoveredFlags.RootAndChildWindows) - && NativeImGui.IsMouseHoveringRect(minimum, maximum); + bool hovered = Ui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows) && Ui.IsMouseHoveringRect(minimum, maximum); bool toolbarHovered = IsManipulationToolbarHovered(toolbar); - _ = m_rendering.TryConfigureNavigation( - S_KIND, - C_VIEWPORT_ID, - width, - height, - out EditorViewportNavigationProfile navigationProfile); - bool navigationOwnsPointer = HandleNavigation( - navigationProfile, - hovered && !toolbarHovered, - minimum, - maximum); + _ = m_rendering.TryConfigureNavigation(S_KIND, C_VIEWPORT_ID, width, height, out EditorViewportNavigationProfile navigationProfile); + bool navigationOwnsPointer = HandleNavigation(navigationProfile, hovered && !toolbarHovered, minimum, maximum); if (!m_rendering.TrySubmit(S_KIND, C_VIEWPORT_ID, width, height, out EditorViewportOutput output)) { - DrawUnavailable( - available, - m_rendering.GetCompositionError(C_VIEWPORT_ID) ?? "No rendering model contributes to Scene View."); + DrawUnavailable(available, m_rendering.GetCompositionError(C_VIEWPORT_ID) ?? "No rendering model contributes to Scene View."); _ = DrawManipulationToolbar(toolbar); return; } + if (!output.isReady) { DrawUnavailable(available, "Preparing Scene View GPU target..."); @@ -141,40 +120,34 @@ protected override void OnDraw(EditorContext context) } m_rendering.Draw(output, new Vector2(width, height)); - minimum = NativeImGui.GetItemRectMin(); - maximum = NativeImGui.GetItemRectMax(); - bool viewportClicked = NativeImGui.IsItemClicked((int)ImGuiMouseButton.Left); + minimum = Ui.GetItemMinimum(); + maximum = Ui.GetItemMaximum(); + bool viewportClicked = Ui.IsItemClicked(ImGuiMouseButton.Left); toolbar = CreateManipulationToolbarLayout(minimum, maximum); toolbarHovered = IsManipulationToolbarHovered(toolbar); - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); + EditorDrawList drawList = EditorImGui.GetDrawList(); IReadOnlyList<(Vector2 center, EditorGizmoIcon icon)> icons; bool toolbarOwnsPointer; bool gizmoOwnsPointer; - NativeImGui.Raw.ChannelsSplit(drawList, 3); + drawList.SplitChannels(3); try { - NativeImGui.Raw.ChannelsSetCurrent(drawList, 0); + drawList.SetChannel(0); icons = DrawGizmos(minimum, maximum); - NativeImGui.Raw.ChannelsSetCurrent(drawList, 2); + drawList.SetChannel(2); toolbarOwnsPointer = DrawManipulationToolbar(toolbar); - NativeImGui.Raw.ChannelsSetCurrent(drawList, 1); - gizmoOwnsPointer = !navigationOwnsPointer - && DrawTransformGizmo(minimum, maximum, hovered && !toolbarHovered); + drawList.SetChannel(1); + gizmoOwnsPointer = !navigationOwnsPointer && DrawTransformGizmo(minimum, maximum, hovered && !toolbarHovered); } finally { - NativeImGui.Raw.ChannelsMerge(drawList); + drawList.MergeChannels(); } - bool iconOwnsPointer = SelectGizmoIcons(icons, - viewportClicked && !toolbarOwnsPointer && !gizmoOwnsPointer && !navigationOwnsPointer); - if (!viewportClicked - || toolbarOwnsPointer - || gizmoOwnsPointer - || iconOwnsPointer - || navigationOwnsPointer - || NativeImGui.Raw.GetIO()->KeyAlt != 0) + + bool iconOwnsPointer = SelectGizmoIcons(icons, viewportClicked && !toolbarOwnsPointer && !gizmoOwnsPointer && !navigationOwnsPointer); + if (!viewportClicked || toolbarOwnsPointer || gizmoOwnsPointer || iconOwnsPointer || navigationOwnsPointer || UiDrawing.GetInput().alt) return; - Vector2 mouse = NativeImGui.GetMousePos(); + Vector2 mouse = Ui.GetMousePos(); float x = (mouse.X - minimum.X) / Math.Max(1f, maximum.X - minimum.X); float y = (mouse.Y - minimum.Y) / Math.Max(1f, maximum.Y - minimum.Y); m_rendering.HandlePointer(S_KIND, C_VIEWPORT_ID, width, height, x, y, button: 0); @@ -190,7 +163,7 @@ protected override void OnAttach(EditorContext context) { _ = context; ApplySettings(m_settings); - m_settings.changed += ApplySettings; + m_settingsSubscription = m_settings.ListenChanges(ApplySettings); } /// @@ -202,7 +175,7 @@ protected override void OnAttach(EditorContext context) protected override void OnDetach(EditorContext context) { _ = context; - m_settings.changed -= ApplySettings; + m_settingsSubscription?.Dispose(); m_planarNavigation.Cancel(); m_navigationDrag = NavigationDrag.None; CommitGesture(); @@ -251,22 +224,15 @@ protected override void Restore(EditorState state) { if (!state.Get("navigation.initialized", false)) return; - var position = new EngineVector3( - state.Get("navigation.position.x", 0f), - state.Get("navigation.position.y", 0f), - state.Get("navigation.position.z", 0f)); + var position = new EngineVector3(state.Get("navigation.position.x", 0f), state.Get("navigation.position.y", 0f), state.Get("navigation.position.z", 0f)); var rotation = new EngineQuaternion( state.Get("navigation.rotation.x", 0f), state.Get("navigation.rotation.y", 0f), state.Get("navigation.rotation.z", 0f), - state.Get("navigation.rotation.w", 1f)); - var pivot = new EngineVector3( - state.Get("navigation.pivot.x", 0f), - state.Get("navigation.pivot.y", 0f), - state.Get("navigation.pivot.z", 0f)); - EditorViewportProjection projection = (EditorViewportProjection)state.Get( - "navigation.projection", - (int)EditorViewportProjection.Orthographic); + state.Get("navigation.rotation.w", 1f) + ); + var pivot = new EngineVector3(state.Get("navigation.pivot.x", 0f), state.Get("navigation.pivot.y", 0f), state.Get("navigation.pivot.z", 0f)); + EditorViewportProjection projection = (EditorViewportProjection)state.Get("navigation.projection", (int)EditorViewportProjection.Orthographic); try { EditorViewportNavigationState navigation = m_rendering.GetNavigationState(C_VIEWPORT_ID); @@ -277,118 +243,76 @@ protected override void Restore(EditorState state) rotation, state.Get("navigation.fieldOfView", 60f), state.Get("navigation.nearClip", 0.01f), - state.Get("navigation.farClip", 1000f)); + state.Get("navigation.farClip", 1000f) + ); } else { - navigation.ConfigureOrthographic( - position, - rotation, - state.Get("navigation.orthographicSize", 5f)); + navigation.ConfigureOrthographic(position, rotation, state.Get("navigation.orthographicSize", 5f)); } + navigation.pivot = pivot; navigation.focusDistance = state.Get("navigation.focusDistance", 10f); navigation.movementSpeed = state.Get("navigation.movementSpeed", 5f); - navigation.mode = (EditorViewportNavigationMode)state.Get( - "navigation.mode", - (int)EditorViewportNavigationMode.Planar); + navigation.mode = (EditorViewportNavigationMode)state.Get("navigation.mode", (int)EditorViewportNavigationMode.Planar); } catch (ArgumentException) { - // Malformed workspace camera state is ignored and the active provider supplies a fresh default. + // Malformed workspace camera state is ignored and the active provider supplies a fresh default. } } private void ApplySettings(EditorSettings settings) => m_backgroundColor = SceneViewBackgroundSetting.Read(settings); - private ContentReadScope CreateContentScope() => m_scenePresentation.Capture(); - private IReadOnlyList<(Vector2 center, EditorGizmoIcon icon)> DrawGizmos( Vector2 minimum, Vector2 maximum ) { - if (!m_rendering.TryGetManipulationSpace(C_VIEWPORT_ID, - out EditorViewportManipulationSpace space)) - return []; + if (!m_rendering.TryGetManipulationSpace(C_VIEWPORT_ID, out EditorViewportManipulationSpace space)) + return[]; int width = Math.Max(1, (int)(maximum.X - minimum.X)); int height = Math.Max(1, (int)(maximum.Y - minimum.Y)); EditorGizmoFrame frame = m_rendering.CollectGizmos(C_VIEWPORT_ID, width, height); EngineMatrix worldToClip = space.projectionMatrix * space.viewMatrix; - var draw = NativeImGui.Raw.GetWindowDrawList(); - uint white = NativeImGui.ColorConvertFloat4ToU32(Vector4.One); - uint outline = NativeImGui.ColorConvertFloat4ToU32(new Vector4(0f, 0f, 0f, 0.75f)); + EditorDrawList draw = EditorImGui.GetDrawList(); + uint white = Ui.ToPackedColor(Vector4.One); + uint outline = Ui.ToPackedColor(new Vector4(0f, 0f, 0f, 0.75f)); foreach (EditorGizmoLine line in frame.lines) { if (!TryProject(line.start, out Vector2 start) || !TryProject(line.end, out Vector2 end)) continue; - EditorImGui.DrawLine( - start + Vector2.One, - end + Vector2.One, - outline, - 3.5f - ); - EditorImGui.DrawLine( - start, - end, - white, - 2.5f - ); + EditorImGui.DrawLine(start + Vector2.One, end + Vector2.One, outline, 3.5f); + EditorImGui.DrawLine(start, end, white, 2.5f); } - float iconSize = 24f * EditorWidget.style.zoom; + + float iconSize = 24f * EditorUI.current.widgets.style.zoom; var displayIcons = new List<(Vector2 center, EditorGizmoIcon icon)>(); - ImFont* font = NativeImGui.Raw.GetFont(); foreach (EditorGizmoIcon icon in frame.icons) { if (!TryProject(icon.position, out Vector2 center)) continue; displayIcons.Add((center, icon)); string glyph = GizmoIconGlyph(icon.iconId); - using NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(glyph, allowsNull: false); - ImVec2C measured = default; - NativeImGui.Raw.CalcTextSizeA(font, iconSize, float.MaxValue, 0f, (sbyte*)nativeText.pointer, null, null, &measured); - Vector2 size = new(measured.X, measured.Y); + float fontScale = iconSize / Ui.GetFontSize(); + Vector2 size = EditorImGui.MeasureText(glyph, fontScale); Vector2 origin = center - size * 0.5f; - ImVec2C outlinePosition = new() { X = origin.X + 1f, Y = origin.Y + 1f }; - ImVec2C textPosition = new() { X = origin.X, Y = origin.Y }; - NativeImGui.Raw.AddText( - draw, - font, - iconSize, - &outlinePosition, - outline, - (sbyte*)nativeText.pointer, - null, - 0f, - null - ); - NativeImGui.Raw.AddText( - draw, - font, - iconSize, - &textPosition, - white, - (sbyte*)nativeText.pointer, - null, - 0f, - null - ); + draw.Text(origin + Vector2.One, outline, glyph, fontScale); + draw.Text(origin, white, glyph, fontScale); } - return displayIcons; + return displayIcons; bool TryProject( EngineVector3 world, out Vector2 screen ) { - Inno.Core.Mathematics.Vector4 clip = Inno.Core.Mathematics.Vector4.Transform( - new Inno.Core.Mathematics.Vector4(world.x, world.y, world.z, 1f), worldToClip); + Inno.Core.Mathematics.Vector4 clip = Inno.Core.Mathematics.Vector4.Transform(new Inno.Core.Mathematics.Vector4(world.x, world.y, world.z, 1f), worldToClip); if (clip.w <= 0.000001f) { screen = default; return false; } - screen = minimum + new Vector2( - (clip.x / clip.w + 1f) * width * 0.5f, - (1f - clip.y / clip.w) * height * 0.5f); + + screen = minimum + new Vector2((clip.x / clip.w + 1f) * width * 0.5f, (1f - clip.y / clip.w) * height * 0.5f); return float.IsFinite(screen.X) && float.IsFinite(screen.Y); } } @@ -400,13 +324,9 @@ bool maySelect bool iconClicked = false; if (maySelect) { - Vector2 mouse = NativeImGui.GetMousePos(); - float radius = 20f * EditorWidget.style.zoom; - Identity[] owners = displayIcons - .Where(icon => Vector2.DistanceSquared(mouse, icon.center) <= radius * radius) - .Select(static icon => icon.icon.owner) - .DistinctBy(static owner => owner.persistentId) - .ToArray(); + Vector2 mouse = Ui.GetMousePos(); + float radius = 20f * EditorUI.current.widgets.style.zoom; + Identity[] owners = displayIcons.Where(icon => Vector2.DistanceSquared(mouse, icon.center) <= radius * radius).Select(static icon => icon.icon.owner).DistinctBy(static owner => owner.persistentId).ToArray(); if (owners.Length == 1 && owners[0].Resolve() is { isDestroyed: false } owner) { m_interactions.SetSelection(owner); @@ -415,17 +335,18 @@ bool maySelect else if (owners.Length > 1) { m_popupGizmoOwners = owners; - NativeImGui.OpenPopup(C_GIZMO_CLUSTER_POPUP); + Ui.OpenPopup(C_GIZMO_CLUSTER_POPUP); iconClicked = true; } } + DrawGizmoClusterPopup(); return iconClicked; } private void DrawGizmoClusterPopup() { - if (!NativeImGui.BeginPopup(C_GIZMO_CLUSTER_POPUP)) + if (!Ui.BeginPopup(C_GIZMO_CLUSTER_POPUP)) return; foreach (Identity identity in m_popupGizmoOwners) { @@ -433,24 +354,23 @@ private void DrawGizmoClusterPopup() if (owner is null || owner.isDestroyed) continue; string label = $"{owner.name}##{identity.runtimeIdentity}"; - if (!NativeImGui.MenuItem(label, string.Empty, false, true)) + if (!Ui.MenuItem(label, string.Empty, false, true)) continue; m_interactions.SetSelection(owner); - NativeImGui.CloseCurrentPopup(); + Ui.CloseCurrentPopup(); break; } - NativeImGui.EndPopup(); - } - private static string GizmoIconGlyph(string iconId) - => iconId switch - { - "camera" => ImGuiIcon.Camera, - "light" => ImGuiIcon.Lightbulb, - "canvas" => ImGuiIcon.VectorSquare, - _ => ImGuiIcon.CircleQuestion - }; + Ui.EndPopup(); + } + private static string GizmoIconGlyph(string iconId) => iconId switch + { + "camera" => ImGuiIcon.Camera, + "light" => ImGuiIcon.Lightbulb, + "canvas" => ImGuiIcon.VectorSquare, + _ => ImGuiIcon.CircleQuestion + }; private bool HandleNavigation( EditorViewportNavigationProfile profile, bool hovered, @@ -458,8 +378,7 @@ private bool HandleNavigation( Vector2 maximum ) { EditorViewportNavigationState navigation = m_rendering.GetNavigationState(C_VIEWPORT_ID); - if (!navigation.isInitialized - || profile.capabilities == EditorViewportNavigationCapabilities.None) + if (!navigation.isInitialized || profile.capabilities == EditorViewportNavigationCapabilities.None) { m_navigationDrag = NavigationDrag.None; m_planarNavigation.Cancel(); @@ -468,20 +387,24 @@ Vector2 maximum if (!SupportsMode(profile, navigation.mode)) navigation.mode = profile.defaultMode; - Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); - if (m_navigationDrag != NavigationDrag.None - && !NativeImGui.IsMouseDown((int)m_navigationDragButton)) + EditorUiInput io = UiDrawing.GetInput(); + if (m_navigationDrag != NavigationDrag.None && !Ui.IsMouseButtonDown(m_navigationDragButton)) { m_navigationDrag = NavigationDrag.None; } - bool panning = m_planarNavigation.Update(hovered && m_navigationDrag == NavigationDrag.None - && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Pan), - NativeImGui.IsMouseClicked((int)ImGuiMouseButton.Left), NativeImGui.IsMouseClicked((int)ImGuiMouseButton.Middle), - NativeImGui.IsMouseDown((int)ImGuiMouseButton.Left), NativeImGui.IsMouseDown((int)ImGuiMouseButton.Middle), io->KeyAlt != 0, - allowAltPrimary: !profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Orbit)); + + bool panning = m_planarNavigation.Update( + hovered && m_navigationDrag == NavigationDrag.None && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Pan), + Ui.IsMouseClicked(ImGuiMouseButton.Left), + Ui.IsMouseClicked(ImGuiMouseButton.Middle), + Ui.IsMouseButtonDown(ImGuiMouseButton.Left), + Ui.IsMouseButtonDown(ImGuiMouseButton.Middle), + io.alt, + allowAltPrimary: !profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Orbit) + ); if (hovered && !panning && m_navigationDrag == NavigationDrag.None) { - if (io->KeyAlt != 0 && NativeImGui.IsMouseClicked((int)ImGuiMouseButton.Left)) + if (io.alt && Ui.IsMouseClicked(ImGuiMouseButton.Left)) { if (profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Orbit)) { @@ -489,20 +412,20 @@ Vector2 maximum m_navigationDragButton = ImGuiMouseButton.Left; } } - else if (NativeImGui.IsMouseClicked((int)ImGuiMouseButton.Right) - && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Fly)) + else if (Ui.IsMouseClicked(ImGuiMouseButton.Right) && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Fly)) { m_navigationDrag = NavigationDrag.Fly; m_navigationDragButton = ImGuiMouseButton.Right; } } - if (io->WantTextInput == 0 && NativeImGui.IsKeyPressed(ImGuiKey.Escape, repeat: false)) + if (!io.wantsText && Ui.IsKeyPressed(ImGuiKey.Escape, repeat: false)) { m_planarNavigation.Cancel(); m_navigationDrag = NavigationDrag.None; panning = false; } + bool ownsPointer = panning || m_navigationDrag != NavigationDrag.None; if (!hovered && !ownsPointer) return false; @@ -511,26 +434,23 @@ Vector2 maximum float aspect = width / height; if (panning) { - Pan(navigation, new Vector2(io->MouseDelta.X, io->MouseDelta.Y), height); - NativeImGui.SetMouseCursor((int)ImGuiMouseCursor.ResizeAll); + Pan(navigation, new Vector2(io.mouseDelta.X, io.mouseDelta.Y), height); + Ui.SetMouseCursor(ImGuiMouseCursor.ResizeAll); } + switch (m_navigationDrag) { case NavigationDrag.Orbit: - Orbit(navigation, profile, new Vector2(io->MouseDelta.X, io->MouseDelta.Y)); + Orbit(navigation, profile, new Vector2(io.mouseDelta.X, io.mouseDelta.Y)); break; case NavigationDrag.Fly: Fly(navigation, profile, io); break; } - if (hovered - && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Zoom) - && io->MouseWheel != 0f) - Zoom(navigation, profile, io->MouseWheel, minimum, width, height, aspect); - if (hovered - && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.FrameSelection) - && NativeImGui.IsKeyPressed(ImGuiKey.F, repeat: false)) + if (hovered && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Zoom) && io.wheel != 0f) + Zoom(navigation, profile, io.wheel, minimum, width, height, aspect); + if (hovered && profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.FrameSelection) && Ui.IsKeyPressed(ImGuiKey.F, repeat: false)) { EditorViewportFocusBounds? bounds = profile.focusBounds; if (bounds is null && TryGetSelectedTransform(out Transform? selected)) @@ -538,6 +458,7 @@ Vector2 maximum if (bounds is EditorViewportFocusBounds focus) Frame(navigation, profile, focus); } + return ownsPointer; } @@ -546,15 +467,11 @@ private static void Pan( Vector2 mouseDelta, float height ) { - float verticalSpan = navigation.projection == EditorViewportProjection.Orthographic - ? navigation.orthographicSize * 2f - : 2f * navigation.focusDistance - * MathF.Tan(navigation.fieldOfView * MathF.PI / 360f); + float verticalSpan = navigation.projection == EditorViewportProjection.Orthographic ? navigation.orthographicSize * 2f : 2f * navigation.focusDistance * MathF.Tan(navigation.fieldOfView * MathF.PI / 360f); float unitsPerPixel = verticalSpan / height; EngineVector3 right = EngineVector3.Transform(EngineVector3.RIGHT, navigation.rotation); EngineVector3 up = EngineVector3.Transform(EngineVector3.UP, navigation.rotation); - EngineVector3 delta = right * (-mouseDelta.X * unitsPerPixel) - + up * (mouseDelta.Y * unitsPerPixel); + EngineVector3 delta = right * (-mouseDelta.X * unitsPerPixel) + up * (mouseDelta.Y * unitsPerPixel); navigation.position += delta; navigation.pivot += delta; } @@ -566,16 +483,10 @@ Vector2 mouseDelta ) { EngineVector3 worldUp = GetWorldUp(profile); float sensitivity = GetPositive(profile.rotationSensitivity, 0.005f); - EngineQuaternion yaw = EngineQuaternion.CreateFromAxisAngle( - worldUp, - -mouseDelta.X * sensitivity); + EngineQuaternion yaw = EngineQuaternion.CreateFromAxisAngle(worldUp, -mouseDelta.X * sensitivity); EngineQuaternion yawed = (yaw * navigation.rotation).normalized; EngineVector3 right = EngineVector3.Transform(EngineVector3.RIGHT, yawed).normalized; - navigation.rotation = ApplyConstrainedPitch( - yawed, - right, - worldUp, - -mouseDelta.Y * sensitivity); + navigation.rotation = ApplyConstrainedPitch(yawed, right, worldUp, -mouseDelta.Y * sensitivity); navigation.mode = EditorViewportNavigationMode.Orbit; EngineVector3 forward = EngineVector3.Transform(EngineVector3.FORWARD, navigation.rotation); navigation.position = navigation.pivot - forward * navigation.focusDistance; @@ -584,46 +495,34 @@ Vector2 mouseDelta private static void Fly( EditorViewportNavigationState navigation, EditorViewportNavigationProfile profile, - Inno.Native.ImGui.ImGuiIO* io + EditorUiInput io ) { float sensitivity = GetPositive(profile.rotationSensitivity, 0.005f); EngineVector3 worldUp = GetWorldUp(profile); - EngineQuaternion yaw = EngineQuaternion.CreateFromAxisAngle( - worldUp, - -io->MouseDelta.X * sensitivity); + EngineQuaternion yaw = EngineQuaternion.CreateFromAxisAngle(worldUp, -io.mouseDelta.X * sensitivity); EngineQuaternion yawed = (yaw * navigation.rotation).normalized; EngineVector3 right = EngineVector3.Transform(EngineVector3.RIGHT, yawed).normalized; - navigation.rotation = ApplyConstrainedPitch( - yawed, - right, - worldUp, - -io->MouseDelta.Y * sensitivity); + navigation.rotation = ApplyConstrainedPitch(yawed, right, worldUp, -io.mouseDelta.Y * sensitivity); navigation.mode = EditorViewportNavigationMode.Fly; - EngineVector3 movement = EngineVector3.ZERO; EngineVector3 forward = EngineVector3.Transform(EngineVector3.FORWARD, navigation.rotation); right = EngineVector3.Transform(EngineVector3.RIGHT, navigation.rotation); - if (NativeImGui.IsKeyDown(ImGuiKey.W)) + if (Ui.IsKeyDown(ImGuiKey.W)) movement += forward; - if (NativeImGui.IsKeyDown(ImGuiKey.S)) + if (Ui.IsKeyDown(ImGuiKey.S)) movement -= forward; - if (NativeImGui.IsKeyDown(ImGuiKey.D)) + if (Ui.IsKeyDown(ImGuiKey.D)) movement += right; - if (NativeImGui.IsKeyDown(ImGuiKey.A)) + if (Ui.IsKeyDown(ImGuiKey.A)) movement -= right; - if (NativeImGui.IsKeyDown(ImGuiKey.E)) + if (Ui.IsKeyDown(ImGuiKey.E)) movement += worldUp; - if (NativeImGui.IsKeyDown(ImGuiKey.Q)) + if (Ui.IsKeyDown(ImGuiKey.Q)) movement -= worldUp; if (movement.LengthSquared() > 0.000001f) { - float multiplier = io->KeyShift != 0 - ? GetPositive(profile.fastMovementMultiplier, 4f) - : 1f; - navigation.position += movement.normalized - * navigation.movementSpeed - * multiplier - * MathF.Max(0f, io->DeltaTime); + float multiplier = io.shift ? GetPositive(profile.fastMovementMultiplier, 4f) : 1f; + navigation.position += movement.normalized * navigation.movementSpeed * multiplier * MathF.Max(0f, io.deltaTime); navigation.pivot = navigation.position + forward * navigation.focusDistance; } } @@ -640,30 +539,15 @@ float aspect float zoomSensitivity = GetPositive(profile.zoomSensitivity, 0.16f); if (navigation.projection == EditorViewportProjection.Orthographic) { - Vector2 mouse = NativeImGui.GetMousePos(); + Vector2 mouse = Ui.GetMousePos(); float normalizedX = Math.Clamp((mouse.X - minimum.X) / width, 0f, 1f); float normalizedY = Math.Clamp((mouse.Y - minimum.Y) / height, 0f, 1f); float previousSize = navigation.orthographicSize; - EngineVector3 previousOffset = GetViewportOffset( - normalizedX, - normalizedY, - previousSize, - aspect, - navigation.rotation); + EngineVector3 previousOffset = GetViewportOffset(normalizedX, normalizedY, previousSize, aspect, navigation.rotation); float minimumSize = GetPositive(profile.minimumOrthographicSize, 0.001f); - float maximumSize = MathF.Max( - minimumSize, - GetPositive(profile.maximumOrthographicSize, 100000f)); - float nextSize = Math.Clamp( - previousSize / EditorPlanarNavigation.WheelFactor(wheel, zoomSensitivity), - minimumSize, - maximumSize); - EngineVector3 nextOffset = GetViewportOffset( - normalizedX, - normalizedY, - nextSize, - aspect, - navigation.rotation); + float maximumSize = MathF.Max(minimumSize, GetPositive(profile.maximumOrthographicSize, 100000f)); + float nextSize = Math.Clamp(previousSize / EditorPlanarNavigation.WheelFactor(wheel, zoomSensitivity), minimumSize, maximumSize); + EngineVector3 nextOffset = GetViewportOffset(normalizedX, normalizedY, nextSize, aspect, navigation.rotation); EngineVector3 delta = previousOffset - nextOffset; navigation.position += delta; navigation.pivot += delta; @@ -672,13 +556,8 @@ float aspect } float minimumDistance = GetPositive(profile.minimumFocusDistance, 0.01f); - float maximumDistance = MathF.Max( - minimumDistance, - GetPositive(profile.maximumFocusDistance, 1000000f)); - navigation.focusDistance = Math.Clamp( - navigation.focusDistance / EditorPlanarNavigation.WheelFactor(wheel, zoomSensitivity), - minimumDistance, - maximumDistance); + float maximumDistance = MathF.Max(minimumDistance, GetPositive(profile.maximumFocusDistance, 1000000f)); + navigation.focusDistance = Math.Clamp(navigation.focusDistance / EditorPlanarNavigation.WheelFactor(wheel, zoomSensitivity), minimumDistance, maximumDistance); EngineVector3 direction = EngineVector3.Transform(EngineVector3.FORWARD, navigation.rotation); navigation.position = navigation.pivot - direction * navigation.focusDistance; } @@ -695,9 +574,7 @@ EditorViewportFocusBounds focus if (navigation.projection == EditorViewportProjection.Orthographic) { float minimumSize = GetPositive(profile.minimumOrthographicSize, 0.001f); - float maximumSize = MathF.Max( - minimumSize, - GetPositive(profile.maximumOrthographicSize, 100000f)); + float maximumSize = MathF.Max(minimumSize, GetPositive(profile.maximumOrthographicSize, 100000f)); navigation.orthographicSize = Math.Clamp(radius * padding, minimumSize, maximumSize); navigation.focusDistance = MathF.Max(navigation.focusDistance, radius * 2f); } @@ -705,27 +582,21 @@ EditorViewportFocusBounds focus { float halfFov = navigation.fieldOfView * MathF.PI / 360f; float distance = radius * padding / MathF.Max(0.001f, MathF.Tan(halfFov)); - navigation.focusDistance = Math.Clamp( - distance, - GetPositive(profile.minimumFocusDistance, 0.01f), - GetPositive(profile.maximumFocusDistance, 1000000f)); + navigation.focusDistance = Math.Clamp(distance, GetPositive(profile.minimumFocusDistance, 0.01f), GetPositive(profile.maximumFocusDistance, 1000000f)); } + navigation.position = focus.center - forward * navigation.focusDistance; } private static bool SupportsMode( EditorViewportNavigationProfile profile, EditorViewportNavigationMode mode - ) - => mode switch - { - EditorViewportNavigationMode.Orbit => - profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Orbit), - EditorViewportNavigationMode.Fly => - profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Fly), - _ => (profile.capabilities & EditorViewportNavigationCapabilities.Planar) != 0 - }; - + ) => mode switch + { + EditorViewportNavigationMode.Orbit => profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Orbit), + EditorViewportNavigationMode.Fly => profile.capabilities.HasFlag(EditorViewportNavigationCapabilities.Fly), + _ => (profile.capabilities & EditorViewportNavigationCapabilities.Planar) != 0 + }; private static EngineQuaternion ApplyConstrainedPitch( EngineQuaternion yawed, EngineVector3 right, @@ -735,21 +606,14 @@ float angle EngineQuaternion pitch = EngineQuaternion.CreateFromAxisAngle(right, angle); EngineQuaternion candidate = (pitch * yawed).normalized; EngineVector3 forward = EngineVector3.Transform(EngineVector3.FORWARD, candidate).normalized; - return MathF.Abs(EngineVector3.Dot(forward, worldUp)) < 0.999f - ? candidate - : yawed; + return MathF.Abs(EngineVector3.Dot(forward, worldUp)) < 0.999f ? candidate : yawed; } - private static EngineVector3 GetWorldUp(EditorViewportNavigationProfile profile) - => profile.worldUp.LengthSquared() > 0.000001f - ? profile.worldUp.normalized - : EngineVector3.UP; - + private static EngineVector3 GetWorldUp(EditorViewportNavigationProfile profile) => profile.worldUp.LengthSquared() > 0.000001f ? profile.worldUp.normalized : EngineVector3.UP; private static float GetPositive( float value, float fallback ) => float.IsFinite(value) && value > 0f ? value : fallback; - private static EngineVector3 GetViewportOffset( float normalizedX, float normalizedY, @@ -757,10 +621,7 @@ private static EngineVector3 GetViewportOffset( float aspect, EngineQuaternion rotation ) { - var local = new EngineVector3( - (normalizedX * 2f - 1f) * halfHeight * aspect, - (1f - normalizedY * 2f) * halfHeight, - 0f); + var local = new EngineVector3((normalizedX * 2f - 1f) * halfHeight * aspect, (1f - normalizedY * 2f) * halfHeight, 0f); return EngineVector3.Transform(local, rotation); } @@ -768,134 +629,90 @@ private void DrawUnavailable( Vector2 size, string message ) { - Vector2 minimum = NativeImGui.GetCursorScreenPos(); + Vector2 minimum = Ui.GetCursorScreenPosition(); Vector2 maximum = minimum + size; - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - EditorImGui.DrawFilledRectangle( - minimum, - maximum, - NativeImGui.ColorConvertFloat4ToU32(m_backgroundColor), - 0 - ); - NativeImGui.PushStyleColor((int)ImGuiCol.Text, EditorPalette.textDisabled); + EditorDrawList drawList = EditorImGui.GetDrawList(); + EditorImGui.DrawFilledRectangle(minimum, maximum, Ui.ToPackedColor(m_backgroundColor), 0); + Ui.PushStyleColor(ImGuiCol.Text, EditorPalette.textDisabled); try { - EditorWidget.CenteredWrappedText(message, size, S_UNAVAILABLE_PADDING); + EditorUI.current.widgets.CenteredWrappedText(message, size, S_UNAVAILABLE_PADDING); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } private static Inno.Core.Mathematics.Color ToEngineColor(Vector4 value) => new(value.X, value.Y, value.Z, value.W); - private bool DrawManipulationToolbar(ManipulationToolbarLayout layout) { - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); + EditorDrawList drawList = EditorImGui.GetDrawList(); Vector4 background = EditorPalette.inspectorTargetHeader; background.W = EditorPalette.opacityMedium; - EditorImGui.DrawFilledRectangle( - layout.minimum, - layout.maximum, - NativeImGui.ColorConvertFloat4ToU32(background), - EditorWidget.style.frameRounding - ); - + EditorImGui.DrawFilledRectangle(layout.minimum, layout.maximum, Ui.ToPackedColor(background), EditorUI.current.widgets.style.frameRounding); float itemX = layout.minimum.X + layout.padding; float itemY = layout.minimum.Y + layout.padding; try { - if (DrawManipulationTool( - "move", - ImGuiIcon.UpDownLeftRight, - "Move", - new Vector2(itemX, itemY), - layout.itemSize, - m_operation == ImGuizmoOperation.Translate)) + if (DrawManipulationTool("move", ImGuiIcon.UpDownLeftRight, "Move", new Vector2(itemX, itemY), layout.itemSize, m_operation == ImGuizmoOperation.Translate)) { m_operation = ImGuizmoOperation.Translate; } - itemY += layout.itemSize.Y + layout.spacing; - if (DrawManipulationTool( - "rotate", - ImGuiIcon.Rotate, - "Rotate", - new Vector2(itemX, itemY), - layout.itemSize, - m_operation == ImGuizmoOperation.Rotate)) + itemY += layout.itemSize.Y + layout.spacing; + if (DrawManipulationTool("rotate", ImGuiIcon.Rotate, "Rotate", new Vector2(itemX, itemY), layout.itemSize, m_operation == ImGuizmoOperation.Rotate)) { m_operation = ImGuizmoOperation.Rotate; } - itemY += layout.itemSize.Y + layout.spacing; - if (DrawManipulationTool( - "scale", - ImGuiIcon.UpRightAndDownLeftFromCenter, - "Scale", - new Vector2(itemX, itemY), - layout.itemSize, - m_operation == ImGuizmoOperation.Scale)) + itemY += layout.itemSize.Y + layout.spacing; + if (DrawManipulationTool("scale", ImGuiIcon.UpRightAndDownLeftFromCenter, "Scale", new Vector2(itemX, itemY), layout.itemSize, m_operation == ImGuizmoOperation.Scale)) { m_operation = ImGuizmoOperation.Scale; } - itemY += layout.itemSize.Y + layout.sectionSpacing; + itemY += layout.itemSize.Y + layout.sectionSpacing; float separatorY = itemY - layout.sectionSpacing * 0.5f; EditorImGui.DrawLine( new Vector2(layout.minimum.X + layout.padding, separatorY), new Vector2(layout.maximum.X - layout.padding, separatorY), - NativeImGui.ColorConvertFloat4ToU32(Vector4.One), - EditorWidget.style.borderSize + Ui.ToPackedColor(Vector4.One), + EditorUI.current.widgets.style.borderSize ); - bool supportsCoordinateSpace = m_operation != ImGuizmoOperation.Scale; - string modeIcon = supportsCoordinateSpace && m_mode == ImGuizmoMode.World - ? ImGuiIcon.Globe - : ImGuiIcon.Cube; - string modeTooltip = !supportsCoordinateSpace - ? "Coordinate Space: Local (Scale is always local)" - : m_mode == ImGuizmoMode.Local - ? "Coordinate Space: Local (axes follow the selected transform)" - : "Coordinate Space: World (axes follow the world)"; + string modeIcon = supportsCoordinateSpace && m_mode == ImGuizmoMode.World ? ImGuiIcon.Globe : ImGuiIcon.Cube; + string modeTooltip = !supportsCoordinateSpace ? "Coordinate Space: Local (Scale is always local)" : m_mode == ImGuizmoMode.Local ? "Coordinate Space: Local (axes follow the selected transform)" : "Coordinate Space: World (axes follow the world)"; if (!supportsCoordinateSpace) - NativeImGui.BeginDisabled(true); + Ui.BeginDisabled(true); bool toggleCoordinateSpace; try { - toggleCoordinateSpace = DrawManipulationTool( - "coordinate_space", - modeIcon, - modeTooltip, - new Vector2(itemX, itemY), - layout.itemSize, - selected: false); + toggleCoordinateSpace = DrawManipulationTool("coordinate_space", modeIcon, modeTooltip, new Vector2(itemX, itemY), layout.itemSize, selected: false); } finally { if (!supportsCoordinateSpace) - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } - if (!supportsCoordinateSpace && - NativeImGui.IsItemHovered((int)ImGuiHoveredFlags.AllowWhenDisabled) && - EditorWidget.BeginMenuTooltip()) + + if (!supportsCoordinateSpace && Ui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) && EditorUI.current.widgets.BeginMenuTooltip()) { - NativeImGui.TextUnformatted(modeTooltip); - EditorWidget.EndMenuTooltip(); + Ui.Text(modeTooltip); + EditorUI.current.widgets.EndMenuTooltip(); } + if (toggleCoordinateSpace && supportsCoordinateSpace) { - m_mode = m_mode == ImGuizmoMode.Local - ? ImGuizmoMode.World - : ImGuizmoMode.Local; + m_mode = m_mode == ImGuizmoMode.Local ? ImGuizmoMode.World : ImGuizmoMode.Local; } } finally { - NativeImGui.SetCursorScreenPos(layout.viewportMaximum); - NativeImGui.Dummy(Vector2.Zero); + Ui.SetCursorScreenPosition(layout.viewportMaximum); + Ui.Dummy(Vector2.Zero); } + return IsManipulationToolbarHovered(layout); } @@ -907,36 +724,28 @@ private static bool DrawManipulationTool( Vector2 size, bool selected ) { - NativeImGui.SetCursorScreenPos(position); + Ui.SetCursorScreenPosition(position); if (selected) - NativeImGui.PushStyleColor((int)ImGuiCol.Text, Vector4.One); + Ui.PushStyleColor(ImGuiCol.Text, Vector4.One); bool pressed; try { - pressed = EditorWidget.ClickableText( - $"scene_transform_{id}", - icon, - size, - tooltip); + pressed = EditorUI.current.widgets.ClickableText($"scene_transform_{id}", icon, size, tooltip); } finally { if (selected) - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } if (selected) { - Vector2 minimum = NativeImGui.GetItemRectMin(); - Vector2 maximum = NativeImGui.GetItemRectMax(); - float thickness = EditorWidget.style.interactionOverlayThickness; - EditorImGui.DrawFilledRectangle( - minimum, - new Vector2(minimum.X + thickness, maximum.Y), - NativeImGui.ColorConvertFloat4ToU32(Vector4.One), - 0 - ); + Vector2 minimum = Ui.GetItemMinimum(); + Vector2 maximum = Ui.GetItemMaximum(); + float thickness = EditorUI.current.widgets.style.interactionOverlayThickness; + EditorImGui.DrawFilledRectangle(minimum, new Vector2(minimum.X + thickness, maximum.Y), Ui.ToPackedColor(Vector4.One), 0); } + return pressed; } @@ -944,18 +753,15 @@ private static ManipulationToolbarLayout CreateManipulationToolbarLayout( Vector2 viewportMinimum, Vector2 viewportMaximum ) { - float zoom = EditorWidget.style.zoom; + float zoom = EditorUI.current.widgets.style.zoom; float inset = 10f * zoom; float padding = 4f * zoom; float spacing = 2f * zoom; float sectionSpacing = 8f * zoom; - float itemExtent = MathF.Max(NativeImGui.GetFrameHeight(), 26f * zoom); + float itemExtent = MathF.Max(Ui.GetFrameHeight(), 26f * zoom); Vector2 itemSize = new(itemExtent); float width = itemExtent + padding * 2f; - float height = padding * 2f - + itemExtent * C_MANIPULATION_TOOL_COUNT - + spacing * 2f - + sectionSpacing; + float height = padding * 2f + itemExtent * C_MANIPULATION_TOOL_COUNT + spacing * 2f + sectionSpacing; Vector2 minimum = viewportMinimum + new Vector2(inset); Vector2 maximum = minimum + new Vector2(width, height); if (maximum.X > viewportMaximum.X - inset) @@ -964,136 +770,156 @@ Vector2 viewportMaximum minimum.X -= offset; maximum.X -= offset; } + if (maximum.Y > viewportMaximum.Y - inset) { float offset = maximum.Y - (viewportMaximum.Y - inset); minimum.Y -= offset; maximum.Y -= offset; } - return new ManipulationToolbarLayout( - minimum, - maximum, - viewportMaximum, - itemSize, - padding, - spacing, - sectionSpacing); - } - private static bool IsManipulationToolbarHovered(ManipulationToolbarLayout layout) - => NativeImGui.IsWindowHovered((int)ImGuiHoveredFlags.RootAndChildWindows) - && NativeImGui.IsMouseHoveringRect(layout.minimum, layout.maximum); + return new ManipulationToolbarLayout(minimum, maximum, viewportMaximum, itemSize, padding, spacing, sectionSpacing); + } - private unsafe bool DrawTransformGizmo( + private static bool IsManipulationToolbarHovered(ManipulationToolbarLayout layout) => Ui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows) + && Ui.IsMouseHoveringRect( + layout.minimum, + layout.maximum + ); + private bool DrawTransformGizmo( Vector2 minimum, Vector2 maximum, bool canInteract ) { - if (!m_rendering.TryGetManipulationSpace( - C_VIEWPORT_ID, - out EditorViewportManipulationSpace manipulationSpace) - || !TryGetSelectedTransform(out Transform? selected)) + if (!m_rendering.TryGetManipulationSpace(C_VIEWPORT_ID, out EditorViewportManipulationSpace manipulationSpace) || !TryGetSelectedTransform(out Transform? selected)) { return false; } - Transform target = m_gestureTarget ?? selected!; + if (m_gestureTarget is not null && !EditorUI.current.widgets.IsEditing(C_TRANSFORM_EDIT)) + { + m_gestureTarget = null; + m_gestureTargets = []; + } + + Transform target = m_gestureTarget is RuntimeIdentity identity + && m_interactions.TryResolveIdentity( + identity, + out IdentityObject? resolved + ) + && resolved is Transform active ? active : selected!; EngineMatrix world = target.localToWorldMatrix; - float* view = stackalloc float[16]; - float* projection = stackalloc float[16]; - float* model = stackalloc float[16]; - WriteColumnMajor(manipulationSpace.viewMatrix, view); - WriteColumnMajor(manipulationSpace.projectionMatrix, projection); - WriteColumnMajor(world, model); - - NativeImGuizmo.BeginFrame(); - NativeImGuizmo.Raw.SetDrawlist( - (Inno.Native.ImGuizmo.ImDrawList*)(nint)NativeImGui.Raw.GetWindowDrawList()); - NativeImGuizmo.SetRect( - minimum.X, - minimum.Y, - MathF.Max(1f, maximum.X - minimum.X), - MathF.Max(1f, maximum.Y - minimum.Y)); - NativeImGuizmo.SetOrthographic(manipulationSpace.isOrthographic); - NativeImGuizmo.SetGizmoSizeClipSpace(0.21f); - var gizmoStyle = NativeImGuizmo.Raw.GetStyle(); - gizmoStyle->TranslationLineThickness = 3.5f; - gizmoStyle->TranslationLineArrowSize = 9f; - gizmoStyle->RotationLineThickness = 3f; - gizmoStyle->RotationOuterLineThickness = 4f; - gizmoStyle->ScaleLineThickness = 3.5f; - gizmoStyle->ScaleLineCircleSize = 9f; - gizmoStyle->CenterCircleSize = 8f; - ImGuizmoMode effectiveMode = m_operation == ImGuizmoOperation.Scale - ? ImGuizmoMode.Local - : m_mode; ImGuizmoOperation operation = SelectManipulationOperation(manipulationSpace.plane, m_operation); - bool planarTranslation = manipulationSpace.plane != EditorViewportManipulationPlane.Spatial - && m_operation == ImGuizmoOperation.Translate; - bool changed; - bool isUsing; - bool isOver; - try - { - NativeImGuizmo.Enable(canInteract || m_gestureTarget is not null); - if (planarTranslation) - { - NativeImGuizmo.SetAxisMask( - manipulationSpace.plane == EditorViewportManipulationPlane.YZ, - manipulationSpace.plane == EditorViewportManipulationPlane.XZ, - manipulationSpace.plane == EditorViewportManipulationPlane.XY); - NativeImGuizmo.SetAxisLimit(float.PositiveInfinity); + bool planarTranslation = manipulationSpace.plane != EditorViewportManipulationPlane.Spatial && m_operation == ImGuizmoOperation.Translate; + TransformGizmoResult result = EditorUI.current.widgets.TransformGizmo( + world, + new TransformGizmoOptions { + minimum = minimum, + maximum = maximum, + view = manipulationSpace.viewMatrix, + projection = manipulationSpace.projectionMatrix, + orthographic = manipulationSpace.isOrthographic, + enabled = canInteract || m_gestureTarget is not null, + operation = operation, + space = m_operation == ImGuizmoOperation.Scale ? ImGuizmoMode.Local : m_mode, + maskX = planarTranslation && manipulationSpace.plane == EditorViewportManipulationPlane.YZ, + maskY = planarTranslation && manipulationSpace.plane == EditorViewportManipulationPlane.XZ, + maskZ = planarTranslation && manipulationSpace.plane == EditorViewportManipulationPlane.XY } - changed = NativeImGuizmo.Raw.Manipulate(view, projection, operation, effectiveMode, model, - null, null, null, null) != 0; - isUsing = NativeImGuizmo.IsUsing(); - isOver = NativeImGuizmo.IsOver(operation); + ); + if (Ui.IsKeyPressed(ImGuiKey.Escape, false) && m_gestureTarget is not null) + { + EditorUI.current.widgets.CancelEdits(); + m_gestureTarget = null; + m_gestureTargets = []; + return true; } - finally + + if (result.isUsing) { - NativeImGuizmo.Enable(true); - if (planarTranslation) + if (m_gestureTarget is null) { - NativeImGuizmo.SetAxisMask(false, false, false); - NativeImGuizmo.SetAxisLimit(C_IMGUIZMO_AXIS_LIMIT_DEFAULT); + m_gestureTarget = target.identity.runtimeIdentity; + m_gestureTargets = SelectedTransforms().Select(value => value.identity.runtimeIdentity!.Value).ToArray(); } + + EditorUI.current.widgets.BeginEdit(m_interactions.history, C_TRANSFORM_EDIT, "Transform Selection"); } - if (isUsing && m_gestureTarget is null) - { - m_gestureTarget = target; - m_gestureBefore = TransformSnapshot.Capture(target); - } - if (changed && TryReadManipulatedTransform( - ReadColumnMajor(model), target, m_operation, - out Inno.Core.Mathematics.Vector3 position, - out EngineQuaternion rotation, - out Inno.Core.Mathematics.Vector3 scale) - && IsUsable(position, rotation, scale)) + + if (result.changed && m_gestureTargets.Length != 0) { - target.SetWorldTransform(position, rotation, scale); + try + { + EngineMatrix delta = result.matrix * EngineMatrix.Invert(world); + foreach (RuntimeIdentity current in m_gestureTargets) + { + if (!m_interactions.TryResolveIdentity(current, out IdentityObject? value) || value is not Transform transform) + throw new InvalidOperationException("A transform gesture target is no longer available."); + EngineMatrix next = delta * transform.localToWorldMatrix; + if (!TryReadManipulatedTransform(next, transform, m_operation, out var position, out var rotation, out var scale) || !IsUsable(position, rotation, scale)) + throw new InvalidOperationException("The manipulated transform is not invertible or finite."); + _ = m_sceneEdits.ChangeProperty( + transform, + nameof(Transform.localPosition), + () => transform.SetWorldTransform(position, rotation, scale), + "Transform Selection", + EditorUI.current.widgets.editMergeKey + ); + } + } + catch + { + EditorUI.current.widgets.CancelEdits(); + m_gestureTarget = null; + m_gestureTargets = []; + throw; + } } - if (!isUsing && m_gestureTarget is not null) + + if (!result.isUsing && m_gestureTarget is not null) CommitGesture(); - return isUsing || isOver; + return result.isUsing || result.isOver; + } + + private Transform[] SelectedTransforms() + { + var selected = new List(); + m_interactions.selection.CopyTargets(selected); + Transform[] transforms = selected.Select(value => value switch + { + GameObject gameObject => gameObject.transform, + GameComponent component => component.transform, + _ => null + }).OfType().Distinct().ToArray(); + var set = transforms.ToHashSet(); + return transforms.Where(value => !HasSelectedAncestor(value, set)).ToArray(); + } + + private static bool HasSelectedAncestor( + Transform transform, + ISet selected + ) { + for (Transform? parent = transform.parent; parent is not null; parent = parent.parent) + if (selected.Contains(parent)) + return true; + return false; } private static ImGuizmoOperation SelectManipulationOperation( EditorViewportManipulationPlane plane, ImGuizmoOperation operation - ) - => operation switch + ) => operation switch + { + ImGuizmoOperation.Rotate => ImGuizmoOperation.Rotate, + ImGuizmoOperation.Scale => plane switch { - ImGuizmoOperation.Rotate => ImGuizmoOperation.Rotate, - ImGuizmoOperation.Scale => plane switch - { - EditorViewportManipulationPlane.XY => ImGuizmoOperation.ScaleX | ImGuizmoOperation.ScaleY, - EditorViewportManipulationPlane.XZ => ImGuizmoOperation.ScaleX | ImGuizmoOperation.ScaleZ, - EditorViewportManipulationPlane.YZ => ImGuizmoOperation.ScaleY | ImGuizmoOperation.ScaleZ, - _ => operation - }, + EditorViewportManipulationPlane.XY => ImGuizmoOperation.ScaleX | ImGuizmoOperation.ScaleY, + EditorViewportManipulationPlane.XZ => ImGuizmoOperation.ScaleX | ImGuizmoOperation.ScaleZ, + EditorViewportManipulationPlane.YZ => ImGuizmoOperation.ScaleY | ImGuizmoOperation.ScaleZ, _ => operation - }; - + }, + _ => operation + }; private static bool TryReadManipulatedTransform( EngineMatrix matrix, Transform target, @@ -1107,28 +933,37 @@ out Inno.Core.Mathematics.Vector3 scale scale = target.worldScale; if (operation == ImGuizmoOperation.Translate) return true; - if (operation == ImGuizmoOperation.Scale) { EngineMatrix original = target.localToWorldMatrix; scale = new Inno.Core.Mathematics.Vector3( - scale.x * SignedColumnRatio(original.m11, original.m21, original.m31, - matrix.m11, matrix.m21, matrix.m31), - scale.y * SignedColumnRatio(original.m12, original.m22, original.m32, - matrix.m12, matrix.m22, matrix.m32), - scale.z * SignedColumnRatio(original.m13, original.m23, original.m33, - matrix.m13, matrix.m23, matrix.m33)); + scale.x * SignedColumnRatio(original.m11, original.m21, original.m31, matrix.m11, matrix.m21, matrix.m31), + scale.y * SignedColumnRatio(original.m12, original.m22, original.m32, matrix.m12, matrix.m22, matrix.m32), + scale.z * SignedColumnRatio(original.m13, original.m23, original.m33, matrix.m13, matrix.m23, matrix.m33) + ); return true; } - if (MathF.Abs(scale.x) <= 0.00001f || MathF.Abs(scale.y) <= 0.00001f - || MathF.Abs(scale.z) <= 0.00001f) + if (MathF.Abs(scale.x) <= 0.00001f || MathF.Abs(scale.y) <= 0.00001f || MathF.Abs(scale.z) <= 0.00001f) return false; EngineMatrix normalized = new( - matrix.m11 / scale.x, matrix.m12 / scale.y, matrix.m13 / scale.z, 0f, - matrix.m21 / scale.x, matrix.m22 / scale.y, matrix.m23 / scale.z, 0f, - matrix.m31 / scale.x, matrix.m32 / scale.y, matrix.m33 / scale.z, 0f, - 0f, 0f, 0f, 1f); + matrix.m11 / scale.x, + matrix.m12 / scale.y, + matrix.m13 / scale.z, + 0f, + matrix.m21 / scale.x, + matrix.m22 / scale.y, + matrix.m23 / scale.z, + 0f, + matrix.m31 / scale.x, + matrix.m32 / scale.y, + matrix.m33 / scale.z, + 0f, + 0f, + 0f, + 0f, + 1f + ); rotation = EngineQuaternion.FromRotationMatrix(normalized).normalized; return true; } @@ -1142,9 +977,7 @@ private static float SignedColumnRatio( float changedZ ) { float squaredLength = originalX * originalX + originalY * originalY + originalZ * originalZ; - return squaredLength <= 0.0000000001f - ? 1f - : (originalX * changedX + originalY * changedY + originalZ * changedZ) / squaredLength; + return squaredLength <= 0.0000000001f ? 1f : (originalX * changedX + originalY * changedY + originalZ * changedZ) / squaredLength; } private bool TryGetSelectedTransform(out Transform? transform) @@ -1161,88 +994,31 @@ private bool TryGetSelectedTransform(out Transform? transform) private void CommitGesture() { - Transform? target = m_gestureTarget; - if (target is null) + if (m_gestureTarget is null) return; - TransformSnapshot before = m_gestureBefore; + if (EditorUI.current.widgets.IsEditing(C_TRANSFORM_EDIT)) + EditorUI.current.widgets.CommitEdits(); m_gestureTarget = null; - if (target.isDestroyed) - return; - TransformSnapshot after = TransformSnapshot.Capture(target); - before.ApplyLocal(target); - string name = m_operation switch - { - ImGuizmoOperation.Rotate => "Rotate GameObject", - ImGuizmoOperation.Scale => "Scale GameObject", - _ => "Move GameObject" - }; - using EditorHistoryTransaction transaction = m_interactions.history.BeginTransaction(name); - _ = m_sceneEdits.ChangeProperty( - target, - nameof(Transform.localPosition), - () => target.localPosition = after.position, - name); - _ = m_sceneEdits.ChangeProperty( - target, - nameof(Transform.localRotation), - () => target.localRotation = after.rotation, - name); - _ = m_sceneEdits.ChangeProperty( - target, - nameof(Transform.localScale), - () => target.localScale = after.scale, - name); - transaction.Commit(); + m_gestureTargets = []; } private static bool IsUsable( Inno.Core.Mathematics.Vector3 position, EngineQuaternion rotation, Inno.Core.Mathematics.Vector3 scale - ) - => float.IsFinite(position.x) - && float.IsFinite(position.y) - && float.IsFinite(position.z) - && float.IsFinite(rotation.x) - && float.IsFinite(rotation.y) - && float.IsFinite(rotation.z) - && float.IsFinite(rotation.w) - && float.IsFinite(scale.x) - && float.IsFinite(scale.y) - && float.IsFinite(scale.z) - && MathF.Abs(scale.x) > 0.00001f - && MathF.Abs(scale.y) > 0.00001f - && MathF.Abs(scale.z) > 0.00001f; - - private static unsafe void WriteColumnMajor( - EngineMatrix matrix, - float* destination - ) { - destination[0] = matrix.m11; - destination[1] = matrix.m21; - destination[2] = matrix.m31; - destination[3] = matrix.m41; - destination[4] = matrix.m12; - destination[5] = matrix.m22; - destination[6] = matrix.m32; - destination[7] = matrix.m42; - destination[8] = matrix.m13; - destination[9] = matrix.m23; - destination[10] = matrix.m33; - destination[11] = matrix.m43; - destination[12] = matrix.m14; - destination[13] = matrix.m24; - destination[14] = matrix.m34; - destination[15] = matrix.m44; - } - - private static unsafe EngineMatrix ReadColumnMajor(float* source) - => new( - source[0], source[4], source[8], source[12], - source[1], source[5], source[9], source[13], - source[2], source[6], source[10], source[14], - source[3], source[7], source[11], source[15]); - + ) => float.IsFinite(position.x) + && float.IsFinite(position.y) + && float.IsFinite(position.z) + && float.IsFinite(rotation.x) + && float.IsFinite(rotation.y) + && float.IsFinite(rotation.z) + && float.IsFinite(rotation.w) + && float.IsFinite(scale.x) + && float.IsFinite(scale.y) + && float.IsFinite(scale.z) + && MathF.Abs(scale.x) > 0.00001f + && MathF.Abs(scale.y) > 0.00001f + && MathF.Abs(scale.z) > 0.00001f; private readonly record struct ManipulationToolbarLayout( Vector2 minimum, Vector2 maximum, @@ -1252,23 +1028,6 @@ private readonly record struct ManipulationToolbarLayout( float spacing, float sectionSpacing ); - - private readonly record struct TransformSnapshot( - Inno.Core.Mathematics.Vector3 position, - EngineQuaternion rotation, - Inno.Core.Mathematics.Vector3 scale - ) { - internal static TransformSnapshot Capture(Transform transform) - => new(transform.localPosition, transform.localRotation, transform.localScale); - - internal void ApplyLocal(Transform transform) - { - transform.localPosition = position; - transform.localRotation = rotation; - transform.localScale = scale; - } - } - private enum NavigationDrag { None, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj index e86f51928..e8dd281b5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj @@ -4,7 +4,6 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor @@ -14,24 +13,10 @@ - - - - - - + + + + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs index 6ff38a6f5..e633c2710 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs @@ -1,15 +1,13 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Build; using Inno.Build.Managed; using EditorImGui = Inno.Editor.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; - internal enum BuildSettingsKey { GameProductName, @@ -28,7 +26,6 @@ internal enum BuildSettingsKey internal sealed class BuildSettingsField { private const int C_TEXT_CAPACITY = 4096; - internal BuildSettingsField( BuildSettingsKey key, string path, @@ -53,60 +50,37 @@ string description internal bool Draw( BuildSettings settings, BuildPipeline buildPipeline - ) - => key switch - { - BuildSettingsKey.GameProductName => DrawTextValue( - settings.gameProductName, - value => settings.gameProductName = value), - BuildSettingsKey.GamePersistentDataPath => DrawTextValue( - settings.gamePersistentDataPath, - value => settings.gamePersistentDataPath = value), - BuildSettingsKey.GameStartupScene => DrawTextValue( - settings.gameStartupScene, - value => settings.gameStartupScene = value), - BuildSettingsKey.GameWindowWidth => DrawPositiveInt( - settings.gameWindowWidth, - value => settings.gameWindowWidth = value), - BuildSettingsKey.GameWindowHeight => DrawPositiveInt( - settings.gameWindowHeight, - value => settings.gameWindowHeight = value), - BuildSettingsKey.GameTarget => DrawTarget(settings, buildPipeline), - BuildSettingsKey.GameManagedDeployment => DrawManagedDeployment(settings, buildPipeline), - BuildSettingsKey.GameOutputDirectory => DrawTextValue( - settings.gameOutputDirectory, - value => settings.gameOutputDirectory = value), - BuildSettingsKey.PluginDisplayName => DrawTextValue( - settings.pluginDisplayName, - value => settings.pluginDisplayName = value), - BuildSettingsKey.PluginOutputPath => DrawTextValue( - settings.pluginOutputPath, - value => settings.pluginOutputPath = value), - BuildSettingsKey.IncludePluginDependencies => DrawIncludeDependencies(settings), - _ => throw new InvalidOperationException($"Unknown Build Settings field '{key}'.") - }; - + ) => key switch + { + BuildSettingsKey.GameProductName => DrawTextValue(settings.gameProductName, value => settings.gameProductName = value), + BuildSettingsKey.GamePersistentDataPath => DrawTextValue(settings.gamePersistentDataPath, value => settings.gamePersistentDataPath = value), + BuildSettingsKey.GameStartupScene => DrawTextValue(settings.gameStartupScene, value => settings.gameStartupScene = value), + BuildSettingsKey.GameWindowWidth => DrawPositiveInt(settings.gameWindowWidth, value => settings.gameWindowWidth = value), + BuildSettingsKey.GameWindowHeight => DrawPositiveInt(settings.gameWindowHeight, value => settings.gameWindowHeight = value), + BuildSettingsKey.GameTarget => DrawTarget(settings, buildPipeline), + BuildSettingsKey.GameManagedDeployment => DrawManagedDeployment(settings, buildPipeline), + BuildSettingsKey.GameOutputDirectory => DrawTextValue(settings.gameOutputDirectory, value => settings.gameOutputDirectory = value), + BuildSettingsKey.PluginDisplayName => DrawTextValue(settings.pluginDisplayName, value => settings.pluginDisplayName = value), + BuildSettingsKey.PluginOutputPath => DrawTextValue(settings.pluginOutputPath, value => settings.pluginOutputPath = value), + BuildSettingsKey.IncludePluginDependencies => DrawIncludeDependencies(settings), + _ => throw new InvalidOperationException($"Unknown Build Settings field '{key}'.")}; internal bool IsDefault( BuildSettings settings, BuildSettings defaults - ) - => key switch - { - BuildSettingsKey.GameProductName => settings.gameProductName == defaults.gameProductName, - BuildSettingsKey.GamePersistentDataPath => settings.gamePersistentDataPath == defaults.gamePersistentDataPath, - BuildSettingsKey.GameStartupScene => settings.gameStartupScene == defaults.gameStartupScene, - BuildSettingsKey.GameWindowWidth => settings.gameWindowWidth == defaults.gameWindowWidth, - BuildSettingsKey.GameWindowHeight => settings.gameWindowHeight == defaults.gameWindowHeight, - BuildSettingsKey.GameTarget => settings.gameTarget == defaults.gameTarget, - BuildSettingsKey.GameManagedDeployment => settings.gameManagedDeployment == defaults.gameManagedDeployment, - BuildSettingsKey.GameOutputDirectory => settings.gameOutputDirectory == defaults.gameOutputDirectory, - BuildSettingsKey.PluginDisplayName => settings.pluginDisplayName == defaults.pluginDisplayName, - BuildSettingsKey.PluginOutputPath => settings.pluginOutputPath == defaults.pluginOutputPath, - BuildSettingsKey.IncludePluginDependencies => - settings.includePluginDependencies == defaults.includePluginDependencies, - _ => throw new InvalidOperationException($"Unknown Build Settings field '{key}'.") - }; - + ) => key switch + { + BuildSettingsKey.GameProductName => settings.gameProductName == defaults.gameProductName, + BuildSettingsKey.GamePersistentDataPath => settings.gamePersistentDataPath == defaults.gamePersistentDataPath, + BuildSettingsKey.GameStartupScene => settings.gameStartupScene == defaults.gameStartupScene, + BuildSettingsKey.GameWindowWidth => settings.gameWindowWidth == defaults.gameWindowWidth, + BuildSettingsKey.GameWindowHeight => settings.gameWindowHeight == defaults.gameWindowHeight, + BuildSettingsKey.GameTarget => settings.gameTarget == defaults.gameTarget, + BuildSettingsKey.GameManagedDeployment => settings.gameManagedDeployment == defaults.gameManagedDeployment, + BuildSettingsKey.GameOutputDirectory => settings.gameOutputDirectory == defaults.gameOutputDirectory, + BuildSettingsKey.PluginDisplayName => settings.pluginDisplayName == defaults.pluginDisplayName, + BuildSettingsKey.PluginOutputPath => settings.pluginOutputPath == defaults.pluginOutputPath, + BuildSettingsKey.IncludePluginDependencies => settings.includePluginDependencies == defaults.includePluginDependencies, + _ => throw new InvalidOperationException($"Unknown Build Settings field '{key}'.")}; internal void Reset( BuildSettings settings, BuildSettings defaults @@ -154,27 +128,48 @@ BuildSettings defaults internal static bool ValuesEqual( BuildSettings left, BuildSettings right + ) => string.Equals( + left.gameProductName, + right.gameProductName, + StringComparison.Ordinal ) - => string.Equals(left.gameProductName, right.gameProductName, StringComparison.Ordinal) - && string.Equals(left.gamePersistentDataPath, right.gamePersistentDataPath, StringComparison.Ordinal) - && string.Equals(left.gameStartupScene, right.gameStartupScene, StringComparison.Ordinal) - && string.Equals(left.gameOutputDirectory, right.gameOutputDirectory, StringComparison.Ordinal) - && left.gameWindowWidth == right.gameWindowWidth - && left.gameWindowHeight == right.gameWindowHeight - && left.gameTarget == right.gameTarget - && left.gameManagedDeployment == right.gameManagedDeployment - && string.Equals(left.pluginDisplayName, right.pluginDisplayName, StringComparison.Ordinal) - && string.Equals(left.pluginOutputPath, right.pluginOutputPath, StringComparison.Ordinal) - && left.includePluginDependencies == right.includePluginDependencies; - + && string.Equals( + left.gamePersistentDataPath, + right.gamePersistentDataPath, + StringComparison.Ordinal + ) + && string.Equals( + left.gameStartupScene, + right.gameStartupScene, + StringComparison.Ordinal + ) + && string.Equals( + left.gameOutputDirectory, + right.gameOutputDirectory, + StringComparison.Ordinal + ) + && left.gameWindowWidth == right.gameWindowWidth + && left.gameWindowHeight == right.gameWindowHeight + && left.gameTarget == right.gameTarget + && left.gameManagedDeployment == right.gameManagedDeployment + && string.Equals( + left.pluginDisplayName, + right.pluginDisplayName, + StringComparison.Ordinal + ) + && string.Equals( + left.pluginOutputPath, + right.pluginOutputPath, + StringComparison.Ordinal + ) + && left.includePluginDependencies == right.includePluginDependencies; private static bool DrawTextValue( string value, Action apply ) { - NativeImGui.SetNextItemWidth(-1f); + Ui.SetNextItemWidth(-1f); if (!EditorImGui.InputText("##value", ref value, C_TEXT_CAPACITY)) return false; - apply(value); return true; } @@ -183,10 +178,9 @@ private static bool DrawPositiveInt( int value, Action apply ) { - NativeImGui.SetNextItemWidth(-1f); - if (!NativeImGui.InputInt("##value", ref value, 1, 100, 0)) + Ui.SetNextItemWidth(-1f); + if (!Ui.InputInt("##value", ref value, 1, 100, 0)) return false; - apply(Math.Max(1, value)); return true; } @@ -196,19 +190,20 @@ private static bool DrawManagedDeployment( BuildPipeline buildPipeline ) { bool changed = false; - NativeImGui.SetNextItemWidth(-1f); - if (!EditorWidget.BeginBoundedCombo("##value", settings.gameManagedDeployment?.value ?? "Platform default")) + Ui.SetNextItemWidth(-1f); + if (!EditorUI.current.widgets.BeginBoundedCombo("##value", settings.gameManagedDeployment?.value ?? "Platform default")) return false; try { - if (NativeImGui.Selectable("Platform default", settings.gameManagedDeployment is null)) + if (Ui.Selectable("Platform default", settings.gameManagedDeployment is null)) { settings.gameManagedDeployment = null; changed = true; } + foreach (ManagedDeploymentId deployment in buildPipeline.GetManagedDeployments(settings.gameTarget)) { - if (!NativeImGui.Selectable(deployment.value, settings.gameManagedDeployment == deployment)) + if (!Ui.Selectable(deployment.value, settings.gameManagedDeployment == deployment)) continue; settings.gameManagedDeployment = deployment; changed = true; @@ -216,8 +211,9 @@ BuildPipeline buildPipeline } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } + return changed; } @@ -226,10 +222,8 @@ private static bool DrawTarget( BuildPipeline buildPipeline ) { bool changed = false; - NativeImGui.SetNextItemWidth(-1f); - if (!EditorWidget.BeginBoundedCombo( - "##value", - GetTargetLabel(settings.gameTarget, buildPipeline))) + Ui.SetNextItemWidth(-1f); + if (!EditorUI.current.widgets.BeginBoundedCombo("##value", GetTargetLabel(settings.gameTarget, buildPipeline))) return false; try { @@ -238,17 +232,17 @@ BuildPipeline buildPipeline } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } + return changed; } private static bool DrawIncludeDependencies(BuildSettings settings) { bool value = settings.includePluginDependencies; - if (!NativeImGui.Checkbox("##value", ref value)) + if (!Ui.Checkbox("##value", ref value)) return false; - settings.includePluginDependencies = value; return true; } @@ -259,84 +253,68 @@ private static bool DrawTargetChoice( BuildPipeline buildPipeline ) { bool selected = settings.gameTarget == target; - bool changed = NativeImGui.Selectable( - GetTargetLabel(target, buildPipeline), - selected); + bool changed = Ui.Selectable(GetTargetLabel(target, buildPipeline), selected); if (changed) settings.gameTarget = target; if (selected) - NativeImGui.SetItemDefaultFocus(); + Ui.SetItemDefaultFocus(); return changed; } private static string GetTargetLabel( BuildTargetId target, BuildPipeline buildPipeline - ) - => buildPipeline.TryGetGameTargetDisplayName(target, out string displayName) - ? displayName - : $"Missing ({target})"; - + ) => buildPipeline.TryGetGameTargetDisplayName(target, out string displayName) ? displayName : $"Missing ({target})"; } internal static class BuildSettingsPresentation { - internal static IReadOnlyList fields { get; } = - [ - new BuildSettingsField( - BuildSettingsKey.GameProductName, - "Build/Game/Product Name", - "Game Export Defaults", - "Player-facing product name copied into each game export."), + internal static IReadOnlyList fields { get; } = [ + new BuildSettingsField(BuildSettingsKey.GameProductName, "Build/Game/Product Name", "Game Export Defaults", "Player-facing product name copied into each game export."), new BuildSettingsField( BuildSettingsKey.GamePersistentDataPath, "Build/Game/Persistent Data Folder", "Game Export Defaults", - "Folder below local application data. Empty uses the Project ID; use portable names separated by '/'."), + "Folder below local application data. Empty uses the Project ID; use portable names separated by '/'." + ), new BuildSettingsField( BuildSettingsKey.GameStartupScene, "Build/Game/Startup Scene", "Game Export Defaults", - "Mount-qualified Scene path loaded when the exported Player starts."), - new BuildSettingsField( - BuildSettingsKey.GameWindowWidth, - "Build/Game/Initial Window Width", - "Game Export Defaults", - "Initial logical Player window width."), - new BuildSettingsField( - BuildSettingsKey.GameWindowHeight, - "Build/Game/Initial Window Height", - "Game Export Defaults", - "Initial logical Player window height."), - new BuildSettingsField( - BuildSettingsKey.GameTarget, - "Build/Game/Target", - "Game Export Defaults", - "Platform target selected when the game export window opens."), + "Mount-qualified Scene path loaded when the exported Player starts." + ), + new BuildSettingsField(BuildSettingsKey.GameWindowWidth, "Build/Game/Initial Window Width", "Game Export Defaults", "Initial logical Player window width."), + new BuildSettingsField(BuildSettingsKey.GameWindowHeight, "Build/Game/Initial Window Height", "Game Export Defaults", "Initial logical Player window height."), + new BuildSettingsField(BuildSettingsKey.GameTarget, "Build/Game/Target", "Game Export Defaults", "Platform target selected when the game export window opens."), new BuildSettingsField( BuildSettingsKey.GameManagedDeployment, "Build/Game/Managed Deployment", "Game Export Defaults", - "Selects the managed publisher independently of the platform. AOT compiles code before execution."), + "Selects the managed publisher independently of the platform. AOT compiles code before execution." + ), new BuildSettingsField( BuildSettingsKey.GameOutputDirectory, "Build/Game/Output Directory", "Game Export Defaults", - "Game output directory. Relative paths resolve from the project root."), + "Game output directory. Relative paths resolve from the project root." + ), new BuildSettingsField( BuildSettingsKey.PluginDisplayName, "Build/Plugin/Display Name", "Plugin Export Defaults", - "User-facing Plugin name copied into each Plugin export."), + "User-facing Plugin name copied into each Plugin export." + ), new BuildSettingsField( BuildSettingsKey.PluginOutputPath, "Build/Plugin/Destination IPlugin", "Plugin Export Defaults", - "Package destination ending in .iplugin. Relative paths resolve from the project root."), + "Package destination ending in .iplugin. Relative paths resolve from the project root." + ), new BuildSettingsField( BuildSettingsKey.IncludePluginDependencies, "Build/Plugin/Include Dependencies", "Plugin Export Defaults", - "Whether dependency Plugin packages are embedded by default. Export changes remain temporary.") + "Whether dependency Plugin packages are embedded by default. Export changes remain temporary." + ) ]; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/ProjectIdentitySettingsEditor.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/ProjectIdentitySettingsEditor.cs index 56412cbd0..97ebdacc5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/ProjectIdentitySettingsEditor.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/ProjectIdentitySettingsEditor.cs @@ -1,24 +1,20 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - using Inno.Core.Settings; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; using EditorImGui = Inno.Editor.ImGui.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; - [ProjectSettingPath("Project/Identity/Project ID")] internal sealed class ProjectIdentitySettingsEditor : ProjectSettingEditor { private string m_error = string.Empty; - /// /// Gets the presentation section that groups this setting. /// public override string section => "Identity"; - /// /// Gets the user-facing explanation of this feature or setting. /// @@ -45,7 +41,8 @@ protected override void OnDraw(ProjectIdentitySettings setting) m_error = exception.Message; } } + if (!string.IsNullOrEmpty(m_error)) - ImGuiWidget.ColoredText(EditorPalette.error, m_error); + EditorUI.current.widgets.ColoredText(EditorPalette.error, m_error); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs index 3806e6b07..d5989d427 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs @@ -1,20 +1,18 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Settings; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; - [EditorModal("editor.settings", "Settings", order: 100)] -internal sealed unsafe class SettingsModal( +internal sealed class SettingsModal( EditorSettings editorSettings, ProjectSettingsEditor projectSettings, SettingsWindowModule window @@ -22,36 +20,29 @@ SettingsWindowModule window { private readonly SettingsNavigation m_navigation = new(); private readonly SettingsTree m_tree = new(); - private SettingsEditSession? m_navigationSession; private string m_query = string.Empty; private float m_treePaneRatio = 0.25f; - /// /// Gets whether this implementation is visible. /// public override bool isVisible => window.isVisible; - /// /// Gets whether this implementation can move. /// public override bool canMove => true; - /// /// Gets whether this implementation can resize. /// public override bool canResize => true; - /// /// Lets the navigation tree and current settings page own their scrolling. /// public override bool allowScrolling => false; - /// /// Gets the preferred initial window size in logical editor units. /// public override Vector2 initialSize => new(1050f, 700f); - /// /// Gets the smallest editor window size that keeps every setting control usable. /// @@ -68,8 +59,7 @@ protected override void OnDraw(EditorContext context) SettingsEditSession? session = window.session; if (session is null) return; - if (session.editorCatalogRevision != editorSettings.catalogRevision || - session.projectCatalogRevision != projectSettings.catalogRevision) + if (session.editorCatalogRevision != editorSettings.catalogRevision || session.projectCatalogRevision != projectSettings.catalogRevision) { window.Refresh(); session = window.session; @@ -83,15 +73,10 @@ protected override void OnDraw(EditorContext context) selected ??= SettingsTree.FindFirstMatch(pages, m_query); if (selected is not null) m_navigation.Replace(selected.path); - - float footerHeight = NativeImGui.GetFrameHeightWithSpacing() + - NativeImGui.Raw.GetStyle()->ItemSpacing.Y; - Vector2 available = NativeImGui.GetContentRegionAvail(); - DrawBody( - session, - pages, - new Vector2(available.X, MathF.Max(1f, available.Y - footerHeight))); - NativeImGui.Separator(); + float footerHeight = Ui.GetFrameHeightWithSpacing() + UiDrawing.GetLayout().itemSpacing.Y; + Vector2 available = Ui.GetContentRegionAvailable(); + DrawBody(session, pages, new Vector2(available.X, MathF.Max(1f, available.Y - footerHeight))); + Ui.Separator(); DrawButtons(session); } @@ -100,46 +85,33 @@ private void DrawBody( IReadOnlyList pages, Vector2 size ) { - ImGuiTableFlags flags = ImGuiTableFlags.NoPadOuterX | - ImGuiTableFlags.NoKeepColumnsVisible | - ImGuiTableFlags.SizingFixedFit | - ImGuiTableFlags.NoSavedSettings; - float splitterWidth = MathF.Max( - 5f * EditorWidget.style.zoom, - NativeImGui.Raw.GetStyle()->DockingSeparatorSize); + ImGuiTableFlags flags = ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoKeepColumnsVisible | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings; + float splitterWidth = MathF.Max(5f * EditorUI.current.widgets.style.zoom, UiDrawing.GetLayout().dockingSeparatorSize); float treeWidth = ResolveTreeWidth(size.X, splitterWidth); - NativeImGui.PushStyleVar((int)ImGuiStyleVar.CellPadding, Vector2.Zero); + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, Vector2.Zero); bool tableStarted = false; try { - tableStarted = NativeImGui.BeginTable("##settings_split", 3, (int)flags, size); + tableStarted = Ui.BeginTable("##settings_split", 3, flags, size); if (tableStarted) { - NativeImGui.TableSetupColumn( - "##settings_tree", - (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), - treeWidth); - NativeImGui.TableSetupColumn( - "##settings_splitter", - (int)(ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), - splitterWidth); - NativeImGui.TableSetupColumn( - "##settings_page", - (int)ImGuiTableColumnFlags.WidthStretch); - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); + Ui.TableSetupColumn("##settings_tree", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), treeWidth); + Ui.TableSetupColumn("##settings_splitter", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), splitterWidth); + Ui.TableSetupColumn("##settings_page", ImGuiTableColumnFlags.WidthStretch); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); DrawTreePane(pages, size.Y); - _ = NativeImGui.TableSetColumnIndex(1); + _ = Ui.TableSetColumnIndex(1); DrawSplitter(splitterWidth, size.X, treeWidth, size.Y); - _ = NativeImGui.TableSetColumnIndex(2); + _ = Ui.TableSetColumnIndex(2); DrawPagePane(session, pages, size.Y); } } finally { if (tableStarted) - NativeImGui.EndTable(); - NativeImGui.PopStyleVar(); + Ui.EndTable(); + Ui.PopStyleVar(); } } @@ -147,24 +119,21 @@ private void DrawTreePane( IReadOnlyList pages, float height ) { - bool visible = NativeImGui.BeginChild( + bool visible = Ui.BeginChild( "##settings_tree_pane", new Vector2(0f, height), - (int)(ImGuiChildFlags.Borders | ImGuiChildFlags.AlwaysUseWindowPadding), - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); + (ImGuiChildFlags.Borders | ImGuiChildFlags.AlwaysUseWindowPadding), + (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse) + ); try { if (!visible) return; DrawNavigationButtons(); - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarSectionSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSectionSpacing); bool wasSearching = !string.IsNullOrWhiteSpace(m_query); - NativeImGui.SetNextItemWidth(-1f); - bool queryChanged = EditorWidget.SearchInput( - "settings", - "Search settings", - ref m_query, - width: -1f); + Ui.SetNextItemWidth(-1f); + bool queryChanged = EditorUI.current.widgets.SearchInput("settings", "Search settings", ref m_query, width: -1f); if (queryChanged) { SettingsPage? first = SettingsTree.FindFirstMatch(pages, m_query); @@ -180,27 +149,24 @@ float height m_navigation.Replace(string.Empty); } } - NativeImGui.Separator(); - bool scrollVisible = NativeImGui.BeginChild("##settings_tree_scroll", Vector2.Zero, 0, 0); + + Ui.Separator(); + bool scrollVisible = Ui.BeginChild("##settings_tree_scroll", Vector2.Zero, 0, 0); try { if (scrollVisible) { - m_tree.Draw( - pages, - m_query, - m_navigation.currentPath, - page => m_navigation.NavigateTo(page.path)); + m_tree.Draw(pages, m_query, m_navigation.currentPath, page => m_navigation.NavigateTo(page.path)); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } @@ -209,10 +175,7 @@ private void DrawPagePane( IReadOnlyList pages, float height ) { - bool visible = NativeImGui.BeginChild( - "##settings_page_pane", - new Vector2(0f, height), - (int)(ImGuiChildFlags.Borders | ImGuiChildFlags.AlwaysUseWindowPadding)); + bool visible = Ui.BeginChild("##settings_page_pane", new Vector2(0f, height), (ImGuiChildFlags.Borders | ImGuiChildFlags.AlwaysUseWindowPadding)); try { if (!visible) @@ -229,12 +192,12 @@ float height } else { - NativeImGui.TextUnformatted("No matching settings"); + Ui.Text("No matching settings"); } } finally { - NativeImGui.EndChild(); + Ui.EndChild(); } } @@ -244,41 +207,28 @@ private void DrawSplitter( float treeWidth, float height ) { - _ = NativeImGui.InvisibleButton( - "##settings_tree_splitter_grip", - new Vector2(width, MathF.Max(1f, height))); - bool hovered = NativeImGui.IsItemHovered(); - bool active = NativeImGui.IsItemActive(); + _ = Ui.InvisibleButton("##settings_tree_splitter_grip", new Vector2(width, MathF.Max(1f, height))); + bool hovered = Ui.IsItemHovered(); + bool active = Ui.IsItemActive(); if (hovered || active) - NativeImGui.SetMouseCursor((int)ImGuiMouseCursor.ResizeEw); + Ui.SetMouseCursor(ImGuiMouseCursor.ResizeEw); if (active) { - Vector2 delta = NativeImGui.GetMouseDragDelta((int)ImGuiMouseButton.Left); + Vector2 delta = Ui.GetMouseDragDelta(ImGuiMouseButton.Left); if (MathF.Abs(delta.X) > 0f) { float usable = MathF.Max(0f, availableWidth - width); float requested = ClampTreeWidth(treeWidth + delta.X, usable); - m_treePaneRatio = usable > float.Epsilon - ? Math.Clamp(requested / usable, 0f, 1f) - : 0.25f; - NativeImGui.ResetMouseDragDelta((int)ImGuiMouseButton.Left); + m_treePaneRatio = usable > float.Epsilon ? Math.Clamp(requested / usable, 0f, 1f) : 0.25f; + Ui.ResetMouseDragDelta(ImGuiMouseButton.Left); } } - Vector2 minimum = NativeImGui.GetItemRectMin(); - Vector2 maximum = NativeImGui.GetItemRectMax(); - System.Numerics.Vector4 color = active - ? EditorPalette.assetAccent - : hovered - ? EditorPalette.assetBorder - : EditorPalette.assetBorderSoft; + Vector2 minimum = Ui.GetItemMinimum(); + Vector2 maximum = Ui.GetItemMaximum(); + System.Numerics.Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; float centerX = (minimum.X + maximum.X) * 0.5f; - EditorImGui.DrawLine( - new Vector2(centerX, minimum.Y), - new Vector2(centerX, maximum.Y), - NativeImGui.ColorConvertFloat4ToU32(color), - EditorWidget.style.borderSize - ); + EditorImGui.DrawLine(new Vector2(centerX, minimum.Y), new Vector2(centerX, maximum.Y), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); } private float ResolveTreeWidth( @@ -293,63 +243,53 @@ private static float ClampTreeWidth( float requested, float usable ) { - float minimum = MathF.Min( - EditorWidget.style.assetPaneMinimumVisibleWidth, - usable * 0.5f); + float minimum = MathF.Min(EditorUI.current.widgets.style.assetPaneMinimumVisibleWidth, usable * 0.5f); float maximum = MathF.Max(minimum, usable - minimum); return Math.Clamp(requested, minimum, maximum); } private void DrawButtons(SettingsEditSession session) { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); + EditorUiLayout style = UiDrawing.GetLayout(); if (!session.isDirty) { - float okWidth = NativeImGui.CalcTextSize("OK").X + style->FramePadding.X * 2f; - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + - MathF.Max(0f, NativeImGui.GetContentRegionAvail().X - okWidth)); - if (NativeImGui.Button("OK")) + float okWidth = Ui.MeasureText("OK").X + style.framePadding.X * 2f; + Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - okWidth)); + if (Ui.Button("OK")) window.Close(); return; } - float cancelWidth = NativeImGui.CalcTextSize("Cancel").X + style->FramePadding.X * 2f; - float applyWidth = NativeImGui.CalcTextSize("Apply").X + style->FramePadding.X * 2f; - float totalWidth = cancelWidth + style->ItemSpacing.X + applyWidth; - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + - MathF.Max(0f, NativeImGui.GetContentRegionAvail().X - totalWidth)); - if (NativeImGui.Button("Cancel")) + float cancelWidth = Ui.MeasureText("Cancel").X + style.framePadding.X * 2f; + float applyWidth = Ui.MeasureText("Apply").X + style.framePadding.X * 2f; + float totalWidth = cancelWidth + style.itemSpacing.X + applyWidth; + Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - totalWidth)); + if (Ui.Button("Cancel")) window.Close(); - - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.Button("Apply")) + Ui.SameLine(0f, -1f); + if (Ui.Button("Apply")) _ = session.Apply(); } private void DrawNavigationButtons() { - float frameHeight = NativeImGui.GetFrameHeight(); + float frameHeight = Ui.GetFrameHeight(); var buttonSize = new Vector2(frameHeight, frameHeight); - NativeImGui.BeginDisabled(!m_navigation.canGoBack); - if (NativeImGui.Button( - $"{Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleLeft}##SettingsBack", - buttonSize)) + Ui.BeginDisabled(!m_navigation.canGoBack); + if (Ui.Button($"{Inno.Editor.ImGui.ImGuiIcon.AngleLeft}##SettingsBack", buttonSize)) { m_navigation.GoBack(); } - NativeImGui.EndDisabled(); - NativeImGui.SameLine(0f, EditorWidget.style.assetToolbarTightSpacing); - NativeImGui.BeginDisabled(!m_navigation.canGoForward); - if (NativeImGui.Button( - $"{Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleRight}##SettingsForward", - buttonSize)) + Ui.EndDisabled(); + Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarTightSpacing); + Ui.BeginDisabled(!m_navigation.canGoForward); + if (Ui.Button($"{Inno.Editor.ImGui.ImGuiIcon.AngleRight}##SettingsForward", buttonSize)) { m_navigation.GoForward(); } - NativeImGui.EndDisabled(); + + Ui.EndDisabled(); } private void EnsureNavigationSession( diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs index 7da2b004d..6b9ccb792 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs @@ -1,42 +1,31 @@ +using System.Numerics; +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using EditorImGui = Inno.Editor.ImGui.ImGui; -using System.Runtime.CompilerServices; -using NumericsVector2 = System.Numerics.Vector2; -using NativeVector2 = Inno.Native.ImGui.ImVec2C; -using NumericsVector4 = System.Numerics.Vector4; -using NativeVector4 = Inno.Native.ImGui.ImVec4C; using System; using System.Collections.Generic; using System.Linq; - using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; - -internal sealed unsafe class SettingsPageView(SettingsEditSession session) +internal sealed class SettingsPageView(SettingsEditSession session) { internal void Draw( SettingsPage page, Action navigate ) { DrawPageHeader(page); - NativeImGui.Spacing(); + Ui.Spacing(); PushDisabledText(); - EditorWidget.WrappedText(page.description); - NativeImGui.PopStyleColor(); - NativeImGui.Dummy(new System.Numerics.Vector2( - 0f, - 16f * EditorWidget.style.zoom)); - + EditorUI.current.widgets.WrappedText(page.description); + Ui.PopStyleColor(); + Ui.Dummy(new System.Numerics.Vector2(0f, 16f * EditorUI.current.widgets.style.zoom)); if (page.children.Count != 0) { DrawOverview(page.children, navigate); - NativeImGui.Dummy(new System.Numerics.Vector2( - 0f, - 8f * EditorWidget.style.zoom)); + Ui.Dummy(new System.Numerics.Vector2(0f, 8f * EditorUI.current.widgets.style.zoom)); } if (!page.hasSettings) @@ -47,40 +36,30 @@ Action navigate } int fieldIndex = 0; - foreach (IGrouping group in page.settings.GroupBy( - static setting => setting.section ?? string.Empty, - StringComparer.OrdinalIgnoreCase)) + foreach (IGrouping group in page.settings.GroupBy(static setting => setting.section ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(group.Key)) - NativeImGui.SeparatorText(group.Key); + Ui.SeparatorText(group.Key); DrawSection(group, ref fieldIndex); - NativeImGui.Spacing(); + Ui.Spacing(); } } private void DrawPageHeader(SettingsPage page) { - ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoSavedSettings | - ImGuiTableFlags.NoPadOuterX; - if (!NativeImGui.BeginTable("##settings_page_header", 2, (int)flags, default(System.Numerics.Vector2), 0f)) + ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.NoPadOuterX; + if (!Ui.BeginTable("##settings_page_header", 2, flags, default(System.Numerics.Vector2), 0f)) return; try { - NativeImGui.TableSetupColumn( - "##settings_page_title", - (int)ImGuiTableColumnFlags.WidthStretch, - 1f); - NativeImGui.TableSetupColumn( - "##settings_page_reset", - (int)ImGuiTableColumnFlags.WidthFixed, - GetResetColumnWidth()); - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); - NativeImGui.AlignTextToFramePadding(); - NativeImGui.TextUnformatted(page.label); - _ = NativeImGui.TableSetColumnIndex(1); - NativeImGui.PushID(page.path); + Ui.TableSetupColumn("##settings_page_title", ImGuiTableColumnFlags.WidthStretch, 1f); + Ui.TableSetupColumn("##settings_page_reset", ImGuiTableColumnFlags.WidthFixed, GetResetColumnWidth()); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); + Ui.AlignTextToFramePadding(); + Ui.Text(page.label); + _ = Ui.TableSetColumnIndex(1); + Ui.PushId(page.path); try { bool canReset = session.CanReset(page); @@ -89,12 +68,12 @@ private void DrawPageHeader(SettingsPage page) } finally { - NativeImGui.PopID(); + Ui.PopId(); } } finally { - NativeImGui.EndTable(); + Ui.EndTable(); } } @@ -105,21 +84,19 @@ Action navigate if (children.Count == 0) { PushDisabledText(); - NativeImGui.TextUnformatted("No settings are currently registered on this page."); - NativeImGui.PopStyleColor(); + Ui.Text("No settings are currently registered on this page."); + Ui.PopStyleColor(); return; } for (int i = 0; i < children.Count; i++) { SettingsPage child = children[i]; - bool clicked = EditorWidget.HoverText( - $"settings_overview_{child.path}", - child.label); + bool clicked = EditorUI.current.widgets.HoverText($"settings_overview_{child.path}", child.label); if (clicked) navigate(child); - EditorWidget.DrawItemTooltip(child.description); - NativeImGui.Spacing(); + EditorUI.current.widgets.DrawItemTooltip(child.description); + Ui.Spacing(); } } @@ -127,37 +104,25 @@ private void DrawSection( IEnumerable settings, ref int fieldIndex ) { - ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoSavedSettings | - ImGuiTableFlags.NoPadOuterX; - float tableWidth = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); - NativeImGui.PushStyleVar( - (int)ImGuiStyleVar.CellPadding, - EditorWidget.style.settingsFieldPadding); + ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.NoPadOuterX; + float tableWidth = MathF.Max(1f, Ui.GetContentRegionAvailable().X); + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, EditorUI.current.widgets.style.settingsFieldPadding); bool tableStarted = false; try { - tableStarted = NativeImGui.BeginTable( - "##settings_section", - 3, - (int)flags, - new System.Numerics.Vector2(tableWidth, 0f)); + tableStarted = Ui.BeginTable("##settings_section", 3, flags, new System.Numerics.Vector2(tableWidth, 0f)); if (!tableStarted) return; - - EditorWidget.SetupPropertyColumns(); - NativeImGui.TableSetupColumn( - "##settings_reset", - (int)ImGuiTableColumnFlags.WidthFixed, - GetResetColumnWidth()); + EditorUI.current.widgets.SetupPropertyColumns(); + Ui.TableSetupColumn("##settings_reset", ImGuiTableColumnFlags.WidthFixed, GetResetColumnWidth()); foreach (SettingsField setting in settings) DrawFieldRow(setting, fieldIndex++); } finally { if (tableStarted) - NativeImGui.EndTable(); - NativeImGui.PopStyleVar(); + Ui.EndTable(); + Ui.PopStyleVar(); } } @@ -165,33 +130,26 @@ private void DrawFieldRow( SettingsField setting, int fieldIndex ) { - NativeImGui.TableNextRow(); - uint background = NativeImGui.ColorConvertFloat4ToU32( - fieldIndex % 2 == 0 - ? EditorPalette.settingsField - : EditorPalette.settingsFieldAlternate); - NativeImGui.TableSetBgColor((int)ImGuiTableBgTarget.RowBg0, background); - NativeImGui.TableSetBgColor((int)ImGuiTableBgTarget.RowBg1, background); - - NativeImGui.PushID(setting.path); + Ui.TableNextRow(); + uint background = Ui.ToPackedColor(fieldIndex % 2 == 0 ? EditorPalette.settingsField : EditorPalette.settingsFieldAlternate); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, background); + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, background); + Ui.PushId(setting.path); try { - _ = NativeImGui.TableSetColumnIndex(0); - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + NativeImGui.Raw.GetStyle()->WindowPadding.X); - EditorWidget.PropertyLabel(setting.label, setting.description); - - _ = NativeImGui.TableSetColumnIndex(1); + _ = Ui.TableSetColumnIndex(0); + Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().windowPadding.X); + EditorUI.current.widgets.PropertyLabel(setting.label, setting.description); + _ = Ui.TableSetColumnIndex(1); bool groupStarted = false; try { - NativeImGui.BeginGroup(); + Ui.BeginGroup(); groupStarted = true; session.UpdateDirty(setting, session.Draw(setting)); - NativeImGui.EndGroup(); + Ui.EndGroup(); groupStarted = false; - - _ = NativeImGui.TableSetColumnIndex(2); + _ = Ui.TableSetColumnIndex(2); bool canReset = session.CanReset(setting); if (DrawResetButton(canReset)) session.Reset(setting); @@ -199,92 +157,69 @@ int fieldIndex finally { if (groupStarted) - NativeImGui.EndGroup(); + Ui.EndGroup(); } } finally { - NativeImGui.PopID(); + Ui.PopId(); } + DrawFieldBackgroundGutters(background); } private static void DrawFieldBackgroundGutters(uint background) { - ImGuiTable* table = NativeImGui.Raw.GetCurrentTable(); - ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); - float tableMinimumX = table->OuterRect.Min.X; - float tableMaximumX = table->OuterRect.Max.X; - float rowMinimumY = table->RowPosY1; - float rowMaximumY = table->RowPosY2; - float windowMinimumX = window->InnerRect.Min.X; - float windowMaximumX = window->InnerRect.Max.X; + var table = UiDrawing.GetTableBounds(); + var window = UiDrawing.GetWindowBounds(); + float tableMinimumX = table.minimumX; + float tableMaximumX = table.maximumX; + float rowMinimumY = table.rowMinimum; + float rowMaximumY = table.rowMaximum; + float windowMinimumX = window.minimumX; + float windowMaximumX = window.maximumX; if (rowMaximumY <= rowMinimumY) return; - - ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - NativeVector2 clipMinimum = Unsafe.BitCast(new System.Numerics.Vector2(windowMinimumX, rowMinimumY)); - NativeVector2 clipMaximum = Unsafe.BitCast(new System.Numerics.Vector2(windowMaximumX, rowMaximumY)); - NativeImGui.Raw.PushClipRect( - drawList, - &clipMinimum, - &clipMaximum, - 0 - ); + EditorDrawList drawList = EditorImGui.GetDrawList(); + drawList.PushClip(new Vector2(windowMinimumX, rowMinimumY), new Vector2(windowMaximumX, rowMaximumY), false); try { if (tableMinimumX > windowMinimumX) { - EditorImGui.DrawFilledRectangle( - new System.Numerics.Vector2(windowMinimumX, rowMinimumY), - new System.Numerics.Vector2(tableMinimumX, rowMaximumY), - background, - 0 - ); + EditorImGui.DrawFilledRectangle(new System.Numerics.Vector2(windowMinimumX, rowMinimumY), new System.Numerics.Vector2(tableMinimumX, rowMaximumY), background, 0); } + if (tableMaximumX < windowMaximumX) { - EditorImGui.DrawFilledRectangle( - new System.Numerics.Vector2(tableMaximumX, rowMinimumY), - new System.Numerics.Vector2(windowMaximumX, rowMaximumY), - background, - 0 - ); + EditorImGui.DrawFilledRectangle(new System.Numerics.Vector2(tableMaximumX, rowMinimumY), new System.Numerics.Vector2(windowMaximumX, rowMaximumY), background, 0); } } finally { - NativeImGui.Raw.PopClipRect(drawList); + drawList.PopClip(); } } private static float GetResetColumnWidth() { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - return style->ItemSpacing.X + - NativeImGui.CalcTextSize("Reset").X + - style->FramePadding.X * 2f + - style->WindowPadding.X; + EditorUiLayout style = UiDrawing.GetLayout(); + return style.itemSpacing.X + Ui.MeasureText("Reset").X + style.framePadding.X * 2f + style.windowPadding.X; } private static bool DrawResetButton(bool enabled) { - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - NativeImGui.SetCursorPosX(NativeImGui.GetCursorPosX() + style->ItemSpacing.X); - NativeImGui.BeginDisabled(!enabled); + EditorUiLayout style = UiDrawing.GetLayout(); + Ui.SetCursorPosX(Ui.GetCursorPosX() + style.itemSpacing.X); + Ui.BeginDisabled(!enabled); try { - return NativeImGui.SmallButton("Reset"); + return Ui.SmallButton("Reset"); } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } } - private static void PushDisabledText() - => NativeImGui.PushStyleColor( - (int)ImGuiCol.Text, - Unsafe.BitCast(NativeImGui.Raw.GetStyle()->Colors[(int)ImGuiCol.TextDisabled])); - + private static void PushDisabledText() => Ui.PushStyleColor(ImGuiCol.Text, UiDrawing.GetStyleColorValue(ImGuiCol.TextDisabled)); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsTree.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsTree.cs index 2e93de8dc..53a35da01 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsTree.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsTree.cs @@ -1,17 +1,14 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using Inno.Native.ImGui; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; - internal sealed class SettingsTree { private string? m_revealPath; - internal void Reveal(string path) { ArgumentException.ThrowIfNullOrWhiteSpace(path); @@ -54,6 +51,7 @@ string path if (nested is not null) return nested; } + return null; } @@ -74,6 +72,7 @@ string query if (nested is not null) return nested; } + return null; } @@ -87,19 +86,13 @@ Action select bool hasVisibleChildren = false; for (int i = 0; i < page.children.Count; i++) hasVisibleChildren |= Matches(page.children[i], query); - if ((!string.IsNullOrWhiteSpace(query) && hasVisibleChildren) || - (hasVisibleChildren && IsRevealed(page.path, revealPath))) - EditorWidget.SetNextTreeNodeOpen(true); - - TreeNodeResult result = EditorWidget.TreeNode( + if ((!string.IsNullOrWhiteSpace(query) && hasVisibleChildren) || (hasVisibleChildren && IsRevealed(page.path, revealPath))) + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( $"settings_tree_{page.path}", - _ => NativeImGui.TextUnformatted(page.label), - new TreeNodeOptions - { - selected = string.Equals(page.path, selectedPath, StringComparison.Ordinal), - isLeaf = !hasVisibleChildren, - hideGuideLines = false - }); + _ => Ui.Text(page.label), + new TreeNodeOptions { selected = string.Equals(page.path, selectedPath, StringComparison.Ordinal), isLeaf = !hasVisibleChildren, hideGuideLines = false } + ); if (result.isClicked) select(page); if (!result.isOpen) @@ -109,17 +102,14 @@ Action select if (Matches(page.children[i], query)) DrawPage(page.children[i], query, selectedPath, revealPath, select); } - NativeImGui.TreePop(); + + Ui.TreePop(); } private static bool IsRevealed( string pagePath, string? revealPath - ) - => revealPath is not null && - (string.Equals(pagePath, revealPath, StringComparison.Ordinal) || - revealPath.StartsWith(pagePath + "/", StringComparison.Ordinal)); - + ) => revealPath is not null && (string.Equals(pagePath, revealPath, StringComparison.Ordinal) || revealPath.StartsWith(pagePath + "/", StringComparison.Ordinal)); private static bool Matches( SettingsPage page, string query @@ -133,6 +123,7 @@ string query if (Matches(page.children[i], query)) return true; } + return false; } @@ -140,22 +131,42 @@ private static bool MatchesSelf( SettingsPage page, string query ) { - if (page.label.Contains(query, StringComparison.OrdinalIgnoreCase) || - page.path.Contains(query, StringComparison.OrdinalIgnoreCase) || - page.description.Contains(query, StringComparison.OrdinalIgnoreCase)) + if (page.label.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) + || page.path.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) + || page.description.Contains( + query, + StringComparison.OrdinalIgnoreCase + )) { return true; } + for (int i = 0; i < page.settings.Count; i++) { SettingsField setting = page.settings[i]; - if (setting.label.Contains(query, StringComparison.OrdinalIgnoreCase) || - (setting.section?.Contains(query, StringComparison.OrdinalIgnoreCase) ?? false) || - setting.description.Contains(query, StringComparison.OrdinalIgnoreCase)) + if (setting.label.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) + || (setting.section?.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) ?? false) + || setting.description.Contains( + query, + StringComparison.OrdinalIgnoreCase + )) { return true; } } + return false; } @@ -169,17 +180,28 @@ string query for (int settingIndex = 0; settingIndex < page.settings.Count; settingIndex++) { SettingsField setting = page.settings[settingIndex]; - if (setting.label.Contains(query, StringComparison.OrdinalIgnoreCase) || - (setting.section?.Contains(query, StringComparison.OrdinalIgnoreCase) ?? false) || - setting.description.Contains(query, StringComparison.OrdinalIgnoreCase)) + if (setting.label.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) + || (setting.section?.Contains( + query, + StringComparison.OrdinalIgnoreCase + ) ?? false) + || setting.description.Contains( + query, + StringComparison.OrdinalIgnoreCase + )) { return page; } } + SettingsPage? nested = FindFirstFieldMatch(page.children, query); if (nested is not null) return nested; } + return null; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs index bec0932d0..2c55a7877 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs @@ -1,29 +1,28 @@ +using Inno.Editor.Core; +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Linq; using Inno.Assets.Pipeline; using Inno.Core.Graphs; -using Inno.Native.ImGui; using Inno.Rendering; using Inno.Rendering.Assets; using Inno.Rendering.Shaders; using EditorImGui = Inno.Editor.ImGui.ImGui; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void Controls(GraphNodeRecord node) { - if (owner.drawers?.TryDraw(node.definitionId, new(node, owner.serialization, owner.context, - ( - key, - value, - continuous - ) => SetEncoded(node, key, value, continuous), owner.previews, m_inspection!, draft.readOnly)) == true) - return; + if (owner.drawers?.TryDraw(node.definitionId, new(node, owner.serialization, owner.context, ( + key, + value, + continuous + ) => SetEncoded(node, key, value, continuous), owner.previews, m_inspection!, draft.readOnly)) == true) + return; switch (node.definitionId) { case "inno.shader.constant": @@ -40,9 +39,15 @@ private void Controls(GraphNodeRecord node) Choice(node, "type", "float", ["float", "float2", "float3", "float4", "int", "uint", "bool"]); break; case "inno.shader.reroute": - InspectorRow("reroute.type", "Type", () => - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, Read(node, "valueType", new ShaderGraphType { id = "float" }).CreateType().id)); - Widget.Hint("Forwards the complete port type without changing the value."); + InspectorRow( + "reroute.type", + "Type", + () => EditorUI.current.widgets.ColoredText( + Inno.Editor.ImGui.EditorPalette.textDisabled, + Read(node, "valueType", new ShaderGraphType { id = "float" }).CreateType().id + ) + ); + EditorUI.current.widgets.Hint("Forwards the complete port type without changing the value."); break; case "inno.shader.extract": Choice(node, "type", "float4", ["float2", "float3", "float4", "float3x3", "float4x4"]); @@ -83,8 +88,11 @@ private void Controls(GraphNodeRecord node) case "inno.shader.storage-load": case "inno.shader.storage-store": case "inno.shader.storage-atomic-add": - ShaderGraphType resource = Read(node, "resource", new ShaderGraphType { isStorage = true, - storageElement = new() { id = "float4" }, access = RenderStorageAccess.ReadWrite }); + ShaderGraphType resource = Read( + node, + "resource", + new ShaderGraphType { isStorage = true, storageElement = new() { id = "float4" }, access = RenderStorageAccess.ReadWrite } + ); InspectorRow("storage.contract", "Contract", () => { if (ImGuiApi.Button("Storage Contract…")) @@ -92,13 +100,13 @@ private void Controls(GraphNodeRecord node) }); if (StoragePopup(ref resource)) Set(node, "resource", resource); - Widget.WrappedText("Connect then → after to order memory effects."); + EditorUI.current.widgets.WrappedText("Connect then → after to order memory effects."); break; case "inno.shader.discard": - Widget.WrappedText("Discard fragments where condition is true."); + EditorUI.current.widgets.WrappedText("Discard fragments where condition is true."); break; default: - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Extension node"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Extension node"); break; } } @@ -110,13 +118,14 @@ private void GraphCall(GraphNodeRecord node) ShaderGraphNodeInterface nodeInterface = Read(node, ShaderGraphNodes.interfaceKey, new ShaderGraphNodeInterface()); InspectorRow("graph-node.asset", "Node Graph", () => { - if (!Widget.BeginBoundedCombo("##graph-node", selected == Guid.Empty ? "Choose Shader node…" : System.IO.Path.GetFileName(path))) + if (!EditorUI.current.widgets.BeginBoundedCombo("##graph-node", selected == Guid.Empty ? "Choose Shader node…" : System.IO.Path.GetFileName(path))) return; try { - foreach (AssetFileEntry entry in owner.assets.GetFileSystemEntries(includeDirectories: false) - .Where(static entry => entry.extension == ".ishader") - .OrderBy(static entry => entry.assetPath.ToString(), StringComparer.Ordinal)) + foreach (AssetFileEntry entry in owner.assets.GetFileSystemEntries(includeDirectories: false).Where(static entry => entry.extension == ".ishader").OrderBy( + static entry => entry.assetPath.ToString(), + StringComparer.Ordinal + )) { Guid id = owner.AssetId(entry); if (id == draft.id) @@ -128,9 +137,10 @@ private void GraphCall(GraphNodeRecord node) } catch (Exception failure) when ((failure is InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) - { - continue; - } + { + continue; + } + if (!ImGuiApi.Selectable(candidate.displayName + " · " + entry.assetPath.localPath, id == selected)) continue; using var transaction = owner.interactions.history.BeginTransaction("Assign Graph Node"); @@ -145,14 +155,14 @@ private void GraphCall(GraphNodeRecord node) } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); - Widget.DrawItemTooltip(path); - InspectorRow("graph-node.kind", "Kind", () => Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.kind.ToString())); - InspectorRow("graph-node.effect", "Effect", () => Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.effect.ToString())); + EditorUI.current.widgets.DrawItemTooltip(path); + InspectorRow("graph-node.kind", "Kind", () => EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.kind.ToString())); + InspectorRow("graph-node.effect", "Effect", () => EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.effect.ToString())); if (nodeInterface.kind == ShaderGraphNodeKind.DomainOutput) - InspectorRow("graph-node.role", "Target Role", () => Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.role)); + InspectorRow("graph-node.role", "Target Role", () => EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, nodeInterface.role)); RepairPorts(node); } @@ -180,8 +190,7 @@ private void GraphOutputs(GraphNodeRecord node) private void GraphInterfaceMetadata() { - ShaderGraphNodeSettings settings = ShaderGraphNodes.ReadSettings( - Controller.document, owner.serialization, owner.context); + ShaderGraphNodeSettings settings = ShaderGraphNodes.ReadSettings(Controller.document, owner.serialization, owner.context); string displayName = settings.displayName; InspectorRow("node-interface.name", "Node Name", () => { @@ -217,7 +226,7 @@ private void GraphInterfaceMetadata() }); InspectorRow("node-interface.kind", "Kind", () => { - if (!Widget.BeginBoundedCombo("##kind", settings.kind.ToString())) + if (!EditorUI.current.widgets.BeginBoundedCombo("##kind", settings.kind.ToString())) return; try { @@ -230,14 +239,14 @@ private void GraphInterfaceMetadata() } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); if (settings.kind == ShaderGraphNodeKind.Function) { InspectorRow("node-interface.effect", "Effect", () => { - if (!Widget.BeginBoundedCombo("##effect", settings.effect.ToString())) + if (!EditorUI.current.widgets.BeginBoundedCombo("##effect", settings.effect.ToString())) return; try { @@ -250,10 +259,11 @@ private void GraphInterfaceMetadata() } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); } + if (settings.kind == ShaderGraphNodeKind.DomainOutput) { string role = settings.role; @@ -289,7 +299,7 @@ Action apply ImGuiApi.SeparatorText(inputs ? "Inputs" : "Outputs"); for (int index = 0; index < ports.Length; index++) { - ImGuiApi.PushID(index); + ImGuiApi.PushId(index); try { ShaderGraphNodePortDefinition port = ports[index]; @@ -301,27 +311,63 @@ Action apply if (changed) { ShaderGraphNodePortDefinition[] changedPorts = ports.ToArray(); - changedPorts[index] = new() { id = id, type = port.type, required = port.required }; + changedPorts[index] = new() + { + id = id, + type = port.type, + required = port.required + }; apply(changedPorts); } }); InspectorRow("node-port.type", "Type", () => { - if (!Widget.BeginBoundedCombo("##type", port.type.id)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##type", port.type.id)) return; try { - foreach (string type in new[] { "float", "float2", "float3", "float4", "int", "int2", "int3", "int4", "uint", "uint2", "uint3", "uint4", "bool", "float3x3", "float4x4", "sampled-texture2d", "sampled-texture2d-array", "sampled-texture3d", "sampled-texture-cube" }) + foreach (string type in new[] + { + "float", + "float2", + "float3", + "float4", + "int", + "int2", + "int3", + "int4", + "uint", + "uint2", + "uint3", + "uint4", + "bool", + "float3x3", + "float4x4", + "sampled-texture2d", + "sampled-texture2d-array", + "sampled-texture3d", + "sampled-texture-cube" + } + + ) if (ImGuiApi.Selectable(type, port.type.id == type)) { ShaderGraphNodePortDefinition[] changedPorts = ports.ToArray(); - changedPorts[index] = new() { id = port.id, type = new() { id = type }, required = port.required }; + changedPorts[index] = new() + { + id = port.id, + type = new() + { + id = type + }, + required = port.required + }; apply(changedPorts); } } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); if (inputs) @@ -332,10 +378,16 @@ Action apply if (!ImGuiApi.Checkbox("##required", ref required)) return; ShaderGraphNodePortDefinition[] changedPorts = ports.ToArray(); - changedPorts[index] = new() { id = port.id, type = port.type, required = required }; + changedPorts[index] = new() + { + id = port.id, + type = port.type, + required = required + }; apply(changedPorts); }); } + InspectorRow("node-port.remove", "", () => { if (ImGuiApi.SmallButton("Remove")) @@ -347,17 +399,19 @@ Action apply } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } + if (CenteredAddButton(inputs ? "Add Input" : "Add Output")) { string prefix = inputs ? "input" : "output"; string id = prefix; for (int suffix = 2; ports.Any(port => port.id == id); suffix++) id = prefix + suffix; - apply([.. ports, new() { id = id, type = new() { id = "float" }, required = false }]); + apply([..ports, new() { id = id, type = new() { id = "float" }, required = false }]); } + RepairPorts(node); } @@ -366,29 +420,46 @@ private void Scalar(GraphNodeRecord node) string type = Read(node, "type", "float"); InspectorRow("constant.type", "Type", () => { - if (!Widget.BeginBoundedCombo("##constant-type", type)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##constant-type", type)) return; try { - foreach (string candidate in new[] { "float", "int", "uint", "bool" }) + foreach (string candidate in new[] + { + "float", + "int", + "uint", + "bool" + } + + ) if (ImGuiApi.Selectable(candidate, type == candidate)) { using var transaction = owner.interactions.history.BeginTransaction("Change Constant Type"); Set(node, "type", candidate); switch (candidate) { - case "float": Set(node, "value", 0f); break; - case "int": Set(node, "value", 0); break; - case "uint": Set(node, "value", 0u); break; - case "bool": Set(node, "value", false); break; + case "float": + Set(node, "value", 0f); + break; + case "int": + Set(node, "value", 0); + break; + case "uint": + Set(node, "value", 0u); + break; + case "bool": + Set(node, "value", false); + break; } + transaction.Commit(); type = candidate; } } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); switch (type) @@ -397,7 +468,7 @@ private void Scalar(GraphNodeRecord node) float number = Read(node, "value", 0f); InspectorRow("constant.value", "Value", () => { - bool changed = Widget.CompactDragFloat("##value", ref number, 0.01f); + bool changed = EditorUI.current.widgets.CompactDragFloat("##value", ref number, 0.01f); Gesture(); if (changed) Set(node, "value", number, true); @@ -441,12 +512,11 @@ private void Source(GraphNodeRecord node) string selectedFunction = Read(node, "function", ""); InspectorRow("source.library", "Library", () => { - if (!Widget.BeginBoundedCombo("##library", selected == Guid.Empty ? "Choose source library…" : System.IO.Path.GetFileName(path))) + if (!EditorUI.current.widgets.BeginBoundedCombo("##library", selected == Guid.Empty ? "Choose source library…" : System.IO.Path.GetFileName(path))) return; try { - foreach (AssetFileEntry entry in owner.assets.GetFileSystemEntries(includeDirectories: false) - .Where(static entry => entry.assetPath.localPath.EndsWith(".ishadersource", StringComparison.OrdinalIgnoreCase))) + foreach (AssetFileEntry entry in owner.assets.GetFileSystemEntries(includeDirectories: false).Where(static entry => entry.assetPath.localPath.EndsWith(".ishadersource", StringComparison.OrdinalIgnoreCase))) if (ImGuiApi.Selectable(entry.assetPath.ToString(), owner.AssetId(entry) == selected)) { using var transaction = owner.interactions.history.BeginTransaction("Assign Shader Function"); @@ -459,19 +529,19 @@ private void Source(GraphNodeRecord node) } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); - Widget.DrawItemTooltip(path); + EditorUI.current.widgets.DrawItemTooltip(path); if (selected == Guid.Empty) - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Choose a library and one of its explicitly exported functions."); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Choose a library and one of its explicitly exported functions."); else { if (owner.assets.TryLoad(selected, out ShaderFunctionAsset? library) && library is not null && !library.isMissing) { InspectorRow("source.function", "Function", () => { - if (!Widget.BeginBoundedCombo("##function", selectedFunction.Length == 0 ? "Choose exported function…" : selectedFunction)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##function", selectedFunction.Length == 0 ? "Choose exported function…" : selectedFunction)) return; try { @@ -481,12 +551,14 @@ private void Source(GraphNodeRecord node) } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); } + SourceSettings(selected); } + RepairPorts(node); } @@ -502,9 +574,10 @@ private void Input(GraphNodeRecord node) input.id = id; Set(node, "settings", input, true); } + InspectorRow("input.source", "Source", () => { - if (!Widget.BeginBoundedCombo("##source", input.kind.ToString())) + if (!EditorUI.current.widgets.BeginBoundedCombo("##source", input.kind.ToString())) return; try { @@ -513,11 +586,25 @@ private void Input(GraphNodeRecord node) { input.kind = kind; if (kind == ShaderIrInputKind.Storage) - input.type = new() { isStorage = true, storageElement = new() { id = "float4" }, access = RenderStorageAccess.ReadWrite }; + input.type = new() + { + isStorage = true, + storageElement = new() + { + id = "float4" + }, + access = RenderStorageAccess.ReadWrite + }; else if (kind == ShaderIrInputKind.SampledTexture) - input.type = new() { id = "sampled-texture2d" }; + input.type = new() + { + id = "sampled-texture2d" + }; else if (input.type.isStorage || input.type.id.StartsWith("sampled-texture", StringComparison.Ordinal)) - input.type = new() { id = "float4" }; + input.type = new() + { + id = "float4" + }; if (kind == ShaderIrInputKind.Builtin) { string stageId = Read(node, "stage", ""); @@ -525,15 +612,19 @@ private void Input(GraphNodeRecord node) if (output is not null && Read(output, "settings", new ShaderGraphStageSettings()).stage == ShaderStage.Compute) { input.semantic = "global-invocation-id"; - input.type = new() { id = "uint3" }; + input.type = new() + { + id = "uint3" + }; } } + Set(node, "settings", input); } } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); string type = input.type.id; @@ -553,23 +644,49 @@ private void Input(GraphNodeRecord node) } else InspectorRow("input.type", "Type", () => - { - if (!Widget.BeginBoundedCombo("##type", type)) - return; - try { - foreach (string candidate in new[] { "float", "float2", "float3", "float4", "int", "int2", "int3", "int4", "uint", "uint2", "uint3", "uint4", "bool", "float3x3", "float4x4", "sampled-texture2d", "sampled-texture2d-array", "sampled-texture3d", "sampled-texture-cube" }) - if (ImGuiApi.Selectable(candidate, type == candidate)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##type", type)) + return; + try + { + foreach (string candidate in new[] { - input.type = new() { id = candidate }; - Set(node, "settings", input); + "float", + "float2", + "float3", + "float4", + "int", + "int2", + "int3", + "int4", + "uint", + "uint2", + "uint3", + "uint4", + "bool", + "float3x3", + "float4x4", + "sampled-texture2d", + "sampled-texture2d-array", + "sampled-texture3d", + "sampled-texture-cube" } - } - finally - { - Widget.EndBoundedCombo(); - } - }); + + ) + if (ImGuiApi.Selectable(candidate, type == candidate)) + { + input.type = new() + { + id = candidate + }; + Set(node, "settings", input); + } + } + finally + { + EditorUI.current.widgets.EndBoundedCombo(); + } + }); string semantic = input.semantic; bool semanticChanged = false; InspectorRow("input.semantic", "Semantic", () => semanticChanged = EditorImGui.InputText("##semantic", ref semantic, 256)); @@ -579,7 +696,8 @@ private void Input(GraphNodeRecord node) input.semantic = semantic; Set(node, "settings", input, true); } - Widget.DrawItemTooltip("Stage semantic, not a native expression. Builtins must be supported by the selected stage and target; compute invocation IDs use uint3."); + + EditorUI.current.widgets.DrawItemTooltip("Stage semantic, not a native expression. Builtins must be supported by the selected stage and target; compute invocation IDs use uint3."); int location = input.location; bool locationChanged = false; InspectorRow("input.location", "Location", () => locationChanged = ImGuiApi.InputInt("##location", ref location, 1, 100, 0)); @@ -589,10 +707,12 @@ private void Input(GraphNodeRecord node) input.location = location; Set(node, "settings", input, true); } + if (input.kind is ShaderIrInputKind.Uniform or ShaderIrInputKind.SampledTexture or ShaderIrInputKind.Storage) { DrawParameter(input); } + if (input.kind == ShaderIrInputKind.SampledTexture) { bool expanded = draft.expandedPreviews.Contains(node.id); @@ -613,7 +733,7 @@ private void Input(GraphNodeRecord node) if (texture is not null && owner.previews.TryGetTexture(texture, out var preview)) owner.previews.Draw(preview, new(120 * Canvas.zoom, 120 * Canvas.zoom)); else - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, texture is null ? "No default texture" : "Preparing preview…"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, texture is null ? "No default texture" : "Preparing preview…"); } } } @@ -621,9 +741,21 @@ private void Input(GraphNodeRecord node) private void Output(GraphNodeRecord node) { ShaderGraphStageSettings stage = Read(node, "settings", new ShaderGraphStageSettings()); - InspectorRow("output.passes", "Passes", () => Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, string.Join(", ", - ShaderGraphPrograms.Read(Controller.document, owner.serialization, owner.context) - .Where(program => program.stages.Contains(node.id.value, StringComparer.Ordinal)).Select(static program => program.pass)))); + InspectorRow( + "output.passes", + "Passes", + () => EditorUI.current.widgets.ColoredText( + Inno.Editor.ImGui.EditorPalette.textDisabled, + string.Join( + ", ", + ShaderGraphPrograms.Read( + Controller.document, + owner.serialization, + owner.context + ).Where(program => program.stages.Contains(node.id.value, StringComparer.Ordinal)).Select(static program => program.pass) + ) + ) + ); InspectorRow("output.settings", "Settings", () => { if (ImGuiApi.Button("Stage & Pass Settings…")) @@ -667,9 +799,9 @@ private void Choice( string[] values ) { string current = Read(node, key, defaultValue); - InspectorRow("choice." + key, Widget.NicifyName(key), () => + InspectorRow("choice." + key, EditorDisplayNames.Nicify(key), () => { - if (!Widget.BeginBoundedCombo("##" + key, current)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##" + key, current)) return; try { @@ -679,7 +811,7 @@ string[] values } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Drawing.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Drawing.cs deleted file mode 100644 index c5d7a2d55..000000000 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Drawing.cs +++ /dev/null @@ -1,146 +0,0 @@ -using Inno.Native.ImGui; -using ImGuiApi = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Panel.ShaderEditor; - -// Carrier addresses remain stack-owned for the duration of each synchronous native call. -internal sealed unsafe partial class ShaderEditorCanvas -{ - private static void DrawNativeFilledRectangle( - ImDrawList* drawList, - ImVec2C minimum, - ImVec2C maximum, - uint color, - float rounding, - int flags - ) { - ImGuiApi.Raw.AddRectFilled( - drawList, - &minimum, - &maximum, - color, - rounding, - flags); - } - - private static void DrawNativeText( - ImDrawList* drawList, - ImVec2C position, - uint color, - sbyte* textBegin, - sbyte* textEnd - ) { - ImGuiApi.Raw.AddText( - drawList, - &position, - color, - textBegin, - textEnd); - } - - private static void DrawNativeText( - ImDrawList* drawList, - ImFont* font, - float fontSize, - ImVec2C position, - uint color, - sbyte* textBegin, - sbyte* textEnd, - float wrapWidth, - ImVec4C* clipRectangle - ) { - ImGuiApi.Raw.AddText( - drawList, - font, - fontSize, - &position, - color, - textBegin, - textEnd, - wrapWidth, - clipRectangle); - } - - private static void DrawNativeFilledCircle( - ImDrawList* drawList, - ImVec2C center, - float radius, - uint color, - int segments - ) { - ImGuiApi.Raw.AddCircleFilled( - drawList, - ¢er, - radius, - color, - segments); - } - - private static void PushNativeClipRectangle( - ImDrawList* drawList, - ImVec2C minimum, - ImVec2C maximum, - byte intersect - ) { - ImGuiApi.Raw.PushClipRect( - drawList, - &minimum, - &maximum, - intersect); - } - - private static void DrawNativeRectangle( - ImDrawList* drawList, - ImVec2C minimum, - ImVec2C maximum, - uint color, - float rounding, - int flags, - float thickness - ) { - ImGuiApi.Raw.AddRect( - drawList, - &minimum, - &maximum, - color, - rounding, - flags, - thickness); - } - - private static void DrawNativeCurve( - ImDrawList* drawList, - ImVec2C p1, - ImVec2C p2, - ImVec2C p3, - ImVec2C p4, - uint color, - float thickness, - int segments - ) { - ImGuiApi.Raw.AddBezierCubic( - drawList, - &p1, - &p2, - &p3, - &p4, - color, - thickness, - segments); - } - - private static void DrawNativeLine( - ImDrawList* drawList, - ImVec2C p1, - ImVec2C p2, - uint color, - float thickness - ) { - ImGuiApi.Raw.AddLine( - drawList, - &p1, - &p2, - color, - thickness); - } -} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.InputDefaults.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.InputDefaults.cs index 7c655e121..efa53e84a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.InputDefaults.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.InputDefaults.cs @@ -1,12 +1,11 @@ +using Inno.Editor.Core; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Linq; - using Inno.Core.Graphs; using Inno.Rendering.Shaders; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void DrawInputDefault( @@ -15,44 +14,33 @@ ShaderNodePort port ) { var endpoint = new GraphEndpoint(node.id, new(port.id)); GraphEdgeRecord? edge = Controller.document.edges.FirstOrDefault(value => value.input == endpoint); - ImGuiApi.PushID(port.id); + ImGuiApi.PushId(port.id); try { if (edge is not null) { - InspectorPortStatusRow( - "port." + port.id, - port, - null, - "From " + edge.output.nodeId.value + "." + edge.output.portId.value); + InspectorPortStatusRow("port." + port.id, port, null, "From " + edge.output.nodeId.value + "." + edge.output.portId.value); return; } + if (node.definitionId == ShaderGraphDocument.outputDefinitionId || draft.missingPorts.Contains(endpoint)) { InspectorPortStatusRow("port." + port.id, port, null, "Connect an available output"); return; } - if (m_inspectionNodes is { Length: > 1 } selected && selected.Any(value => - !draft.ports[value.id].Any(candidate => - candidate.id == port.id && candidate.type.IsEquivalentTo(port.type)) || - Controller.document.edges.Any(connection => - connection.input == new GraphEndpoint(value.id, new(port.id))))) + + if (m_inspectionNodes is { Length: > 1 } selected + && selected.Any(value => !draft.ports[value.id].Any(candidate => candidate.id == port.id && candidate.type.IsEquivalentTo(port.type)) || Controller.document.edges.Any(connection => connection.input == new GraphEndpoint(value.id, new(port.id))))) { InspectorPortStatusRow("port." + port.id, port, null, "Mixed connections or types"); return; } - InspectorPortStatusRow( - "port." + port.id, - port, - null, - port.required - ? "Required · Connect a Constant or compatible output" - : "Optional · Zero when unconnected"); + InspectorPortStatusRow("port." + port.id, port, null, port.required ? "Required · Connect a Constant or compatible output" : "Optional · Zero when unconnected"); } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } @@ -61,19 +49,14 @@ private static void InspectorPortStatusRow( ShaderNodePort port, string? component, string status - ) - => Widget.PropertyRow( - "shader." + id, - PortLabel(port, component), - () => Widget.MetadataValue( - DisplayType(port.type.id), - status, - "Shader value type · " + port.type.id)); - + ) => EditorUI.current.widgets.PropertyRow( + "shader." + id, + PortLabel(port, component), + () => EditorUI.current.widgets.MetadataValue(DisplayType(port.type.id), status, "Shader value type · " + port.type.id) + ); private static string PortLabel( ShaderNodePort port, string? component ) => component is null ? port.id : port.id + " " + component; - - private static string DisplayType(string typeId) => Widget.NicifyName(typeId); + private static string DisplayType(string typeId) => EditorDisplayNames.Nicify(typeId); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs index 0277c1db0..9dc988164 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs @@ -1,31 +1,25 @@ -using System.Runtime.CompilerServices; -using NumericsVector2 = System.Numerics.Vector2; -using NativeVector2 = Inno.Native.ImGui.ImVec2C; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; - using Inno.Core.Graphs; using Inno.Editor.ImGui; -using Inno.Native.ImGui; -using ImGuiApi = Inno.Native.ImGui.ImGui; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using ImGuiApi = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.ShaderEditor; - -internal sealed unsafe partial class ShaderEditorCanvas +internal sealed partial class ShaderEditorCanvas { private const float C_MINI_MAP_WIDTH = 220f; private const float C_MINI_MAP_HEIGHT = 145f; private const float C_MINI_MAP_MARGIN = 14f; private const float C_MINI_MAP_PADDING = 9f; private readonly Dictionary m_miniMapGeometry = []; - private readonly record struct MiniMapNodeGeometry( Vector2 minimum, Vector2 maximum - ) { + ) + { internal Vector2 center => (minimum + maximum) * 0.5f; } @@ -33,9 +27,9 @@ private readonly record struct MiniMapLayout( Vector2 worldMinimum, Vector2 screenOrigin, float scale - ) { + ) + { internal Vector2 ToScreen(Vector2 position) => screenOrigin + (position - worldMinimum) * scale; - internal Vector2 ToWorld(Vector2 position) => worldMinimum + (position - screenOrigin) / scale; } @@ -43,8 +37,7 @@ private bool MiniMapContains(Vector2 point) { if (!TryGetMiniMapBounds(out Vector2 minimum, out Vector2 maximum)) return false; - return point.X >= minimum.X && point.X <= maximum.X && - point.Y >= minimum.Y && point.Y <= maximum.Y; + return point.X >= minimum.X && point.X <= maximum.X && point.Y >= minimum.Y && point.Y <= maximum.Y; } private bool TryGetMiniMapBounds( @@ -54,11 +47,8 @@ out Vector2 maximum minimum = maximum = default; if (m_size.X < 320f || m_size.Y < 220f) return false; - - float zoom = Widget.style.zoom; - Vector2 size = new( - MathF.Min(C_MINI_MAP_WIDTH * zoom, m_size.X * 0.30f), - MathF.Min(C_MINI_MAP_HEIGHT * zoom, m_size.Y * 0.30f)); + float zoom = EditorUI.current.widgets.style.zoom; + Vector2 size = new(MathF.Min(C_MINI_MAP_WIDTH * zoom, m_size.X * 0.30f), MathF.Min(C_MINI_MAP_HEIGHT * zoom, m_size.Y * 0.30f)); maximum = m_origin + new Vector2(m_size.X - C_MINI_MAP_MARGIN * zoom, C_MINI_MAP_MARGIN * zoom + size.Y); minimum = maximum - size; return true; @@ -68,39 +58,32 @@ private void DrawMiniMap() { if (!TryGetMiniMapBounds(out Vector2 minimum, out Vector2 maximum)) return; - Vector2 size = maximum - minimum; - Vector2 cursor = ImGuiApi.GetCursorScreenPos(); - ImGuiApi.SetCursorScreenPos(minimum); - ImGuiApi.PushStyleVar((int)ImGuiStyleVar.WindowPadding, Vector2.Zero); - ImGuiApi.PushStyleColor((int)ImGuiCol.ChildBg, new Vector4(0.085f, 0.092f, 0.11f, 0.95f)); + Vector2 cursor = ImGuiApi.GetCursorScreenPosition(); + ImGuiApi.SetCursorScreenPosition(minimum); + ImGuiApi.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); + ImGuiApi.PushStyleColor(ImGuiCol.ChildBg, new Vector4(0.085f, 0.092f, 0.11f, 0.95f)); bool began = false; try { bool visible = ImGuiApi.BeginChild( "##shader-mini-map", size, - (int)ImGuiChildFlags.Borders, - (int)(ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings)); + ImGuiChildFlags.Borders, + (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings) + ); began = true; if (!visible) return; - MiniMapLayout layout = CreateMiniMapLayout(minimum, maximum); - ImGuiApi.InvisibleButton( - "##mini-map-navigation", - Vector2.Max(ImGuiApi.GetContentRegionAvail(), Vector2.One), - (int)ImGuiButtonFlags.MouseButtonLeft); - if (ImGuiApi.IsItemActive() && ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Left)) + ImGuiApi.InvisibleButton("##mini-map-navigation", Vector2.Max(ImGuiApi.GetContentRegionAvailable(), Vector2.One), ImGuiButtonFlags.MouseButtonLeft); + if (ImGuiApi.IsItemActive() && ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) { Vector2 graphPoint = layout.ToWorld(Vector2.Clamp(ImGuiApi.GetMousePos(), minimum, maximum)); - Canvas.SetViewport( - new(m_size.X * 0.5f - graphPoint.X * Canvas.zoom, - m_size.Y * 0.5f - graphPoint.Y * Canvas.zoom), - Canvas.zoom); + Canvas.SetViewport(new(m_size.X * 0.5f - graphPoint.X * Canvas.zoom, m_size.Y * 0.5f - graphPoint.Y * Canvas.zoom), Canvas.zoom); } - DrawMiniMapContents(ImGuiApi.Raw.GetWindowDrawList(), layout); + DrawMiniMapContents(Inno.Editor.ImGui.ImGui.GetDrawList(), layout); } finally { @@ -108,7 +91,7 @@ private void DrawMiniMap() ImGuiApi.EndChild(); ImGuiApi.PopStyleColor(); ImGuiApi.PopStyleVar(); - ImGuiApi.SetCursorScreenPos(cursor); + ImGuiApi.SetCursorScreenPosition(cursor); } } @@ -125,16 +108,14 @@ Vector2 maximum { GraphPosition position = draft.dragPreview.GetValueOrDefault(node.id, node.position); Vector2 nodeMinimum = new(position.x, position.y); - Vector2 nodeMaximum = nodeMinimum + new Vector2( - NodeWidth(node), - C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20f); + Vector2 nodeMaximum = nodeMinimum + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20f); m_miniMapGeometry.Add(node.id, new(nodeMinimum, nodeMaximum)); worldMinimum = Vector2.Min(worldMinimum, nodeMinimum); worldMaximum = Vector2.Max(worldMaximum, nodeMaximum); } - Vector2 innerMinimum = minimum + new Vector2(C_MINI_MAP_PADDING * Widget.style.zoom); - Vector2 innerSize = maximum - minimum - new Vector2(C_MINI_MAP_PADDING * Widget.style.zoom * 2f); + Vector2 innerMinimum = minimum + new Vector2(C_MINI_MAP_PADDING * EditorUI.current.widgets.style.zoom); + Vector2 innerSize = maximum - minimum - new Vector2(C_MINI_MAP_PADDING * EditorUI.current.widgets.style.zoom * 2f); Vector2 worldSize = Vector2.Max(worldMaximum - worldMinimum, Vector2.One); float scale = MathF.Min(innerSize.X / worldSize.X, innerSize.Y / worldSize.Y); Vector2 screenOrigin = innerMinimum + (innerSize - worldSize * scale) * 0.5f; @@ -142,60 +123,35 @@ Vector2 maximum } private void DrawMiniMapContents( - ImDrawList* draw, + EditorDrawList draw, MiniMapLayout layout ) { foreach (GraphEdgeRecord edge in Controller.document.edges) { - if (!m_miniMapGeometry.TryGetValue(edge.output.nodeId, out MiniMapNodeGeometry source) || - !m_miniMapGeometry.TryGetValue(edge.input.nodeId, out MiniMapNodeGeometry target)) + if (!m_miniMapGeometry.TryGetValue( + edge.output.nodeId, + out MiniMapNodeGeometry source + ) + || !m_miniMapGeometry.TryGetValue( + edge.input.nodeId, + out MiniMapNodeGeometry target + )) continue; - DrawNativeLine( - draw, - Unsafe.BitCast(layout.ToScreen(source.center)), - Unsafe.BitCast(layout.ToScreen(target.center)), - Color(0.55f, 0.42f, 0.76f, 0.65f), - 1f - ); + draw.Line(layout.ToScreen(source.center), layout.ToScreen(target.center), Color(0.55f, 0.42f, 0.76f, 0.65f), 1f); } foreach (GraphNodeRecord node in Controller.document.nodes) { MiniMapNodeGeometry geometry = m_miniMapGeometry[node.id]; - uint fill = Canvas.selectedNodes.Contains(node.id) - ? Color(0.60f, 0.44f, 0.83f) - : Color(0.34f, 0.37f, 0.44f); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(layout.ToScreen(geometry.minimum)), - Unsafe.BitCast(layout.ToScreen(geometry.maximum)), - fill, - 2f, - 0 - ); + uint fill = Canvas.selectedNodes.Contains(node.id) ? Color(0.60f, 0.44f, 0.83f) : Color(0.34f, 0.37f, 0.44f); + draw.FilledRectangle(layout.ToScreen(geometry.minimum), layout.ToScreen(geometry.maximum), fill, 2f, false); } Vector2 viewMinimum = -new Vector2(Canvas.pan.x, Canvas.pan.y) / Canvas.zoom; Vector2 viewMaximum = viewMinimum + m_size / Canvas.zoom; Vector2 screenMinimum = layout.ToScreen(viewMinimum); Vector2 screenMaximum = layout.ToScreen(viewMaximum); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(screenMinimum), - Unsafe.BitCast(screenMaximum), - Color(0.65f, 0.47f, 0.88f, 0.10f), - 0, - 0 - ); - DrawNativeRectangle( - draw, - Unsafe.BitCast(screenMinimum), - Unsafe.BitCast(screenMaximum), - Color(0.76f, 0.62f, 0.95f), - 0f, - (int)ImDrawFlags.None, - 1.5f - ); + draw.FilledRectangle(screenMinimum, screenMaximum, Color(0.65f, 0.47f, 0.88f, 0.10f), 0, false); + draw.Rectangle(screenMinimum, screenMaximum, Color(0.76f, 0.62f, 0.95f), 0f, 1.5f); } - } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs index 665e3481d..169050c8d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs @@ -1,22 +1,22 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Linq; using Inno.Core.Graphs; -using Inno.Native.ImGui; using Inno.Rendering; using Inno.Rendering.Shaders; using Inno.Editor.Shaders; +using Inno.Editor.Shaders.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private bool StoragePopup(ref ShaderGraphType type) { - ImGuiApi.SetNextWindowSize(new(490, 390), (int)(ImGuiCond.Appearing)); + ImGuiApi.SetNextWindowSize(new(490, 390), (ImGuiCond.Appearing)); if (!ImGuiApi.BeginPopup("##storage")) return false; bool changed = false; @@ -29,16 +29,21 @@ private bool StoragePopup(ref ShaderGraphType type) { type.isImage = image; type.isStorage = true; - type.storageElement = image ? null : new() { id = "float4" }; + type.storageElement = image ? null : new() + { + id = "float4" + }; type.format = RenderTextureFormat.RGBA16Float; changed = true; } + RenderStorageAccess access = type.access; if (EnumControl("Access", ref access)) { type.access = access; changed = true; } + if (type.isImage) { RenderTextureFormat format = type.format; @@ -47,12 +52,14 @@ private bool StoragePopup(ref ShaderGraphType type) type.format = format; changed = true; } + RenderTextureDimension dimension = type.dimension; if (EnumControl("Dimension", ref dimension)) { type.dimension = dimension; changed = true; } + bool array = type.isArray; bool arrayChanged = false; InspectorRow("storage.array", "Array Layers", () => arrayChanged = ImGuiApi.Checkbox("##array", ref array)); @@ -68,31 +75,52 @@ private bool StoragePopup(ref ShaderGraphType type) string selected = current; InspectorRow("storage.element", "Element", () => { - if (!Widget.BeginBoundedCombo("##element", current)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##element", current)) return; try { - foreach (string candidate in new[] { "float", "float2", "float3", "float4", "int", "int2", "int3", "int4", "uint", "uint2", "uint3", "uint4" }) + foreach (string candidate in new[] + { + "float", + "float2", + "float3", + "float4", + "int", + "int2", + "int3", + "int4", + "uint", + "uint2", + "uint3", + "uint4" + } + + ) if (ImGuiApi.Selectable(candidate, current == candidate)) selected = candidate; } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); if (selected != current) { - type.storageElement = new() { id = selected }; + type.storageElement = new() + { + id = selected + }; changed = true; } } - Widget.WrappedText("Storage resources are bound by the render pass. The graph declares types and access; the Render Graph owns resource lifetime and synchronization."); + + EditorUI.current.widgets.WrappedText("Storage resources are bound by the render pass. The graph declares types and access; the Render Graph owns resource lifetime and synchronization."); } finally { ImGuiApi.EndPopup(); } + return changed; } @@ -104,11 +132,15 @@ private void DrawParameter(ShaderGraphInputSettings input) int index = Array.FindIndex(definition.properties, value => value.id.value == input.id); if (index < 0) { - Widget.Hint("Enter a binding name and supported type to declare this parameter."); + EditorUI.current.widgets.Hint("Enter a binding name and supported type to declare this parameter."); return; } + ShaderPropertyDefinition property = definition.properties[index]; - bool parameterOpen = Widget.SectionHeader("Parameter", "The stable binding ID identifies overrides. Shader defaults and Material overrides are edited independently."); + bool parameterOpen = EditorUI.current.widgets.SectionHeader( + "Parameter", + "The stable binding ID identifies overrides. Shader defaults and Material overrides are edited independently." + ); if (parameterOpen) { string displayName = property.displayName; @@ -120,12 +152,14 @@ private void DrawParameter(ShaderGraphInputSettings input) property.displayName = displayName; Save(property, true); } + ShaderPropertyBindingOwner bindingOwner = property.bindingOwner; if (EnumControl("Bound By", ref bindingOwner)) { property.bindingOwner = bindingOwner; Save(property, false); } + if (property.type is ShaderPropertyType.Vector4 or ShaderPropertyType.Color) { bool color = property.type == ShaderPropertyType.Color; @@ -142,20 +176,26 @@ private void DrawParameter(ShaderGraphInputSettings input) }); } } + if (property.bindingOwner != ShaderPropertyBindingOwner.Material) { if (parameterOpen) - Widget.Hint("Supplied by the Render Pass. This binding is read-only in Material Inspectors."); + EditorUI.current.widgets.Hint("Supplied by the Render Pass. This binding is read-only in Material Inspectors."); return; } + if (property.bindingKind is not (ShaderPropertyBindingKind.Uniform or ShaderPropertyBindingKind.SampledTexture)) { if (parameterOpen) - Widget.Hint("Storage resources require a Render Pass owner."); + EditorUI.current.widgets.Hint("Storage resources require a Render Pass owner."); return; } + ShaderParameterPresentation presentation = ShaderParameterPresentation.Read(Controller.document, property.id, owner.serialization, owner.context); - if (!Widget.SectionHeader("Material Inspector", "Presentation belongs only to this Shader's authoring graph. It does not modify existing Material values or enter the Player.")) + if (!EditorUI.current.widgets.SectionHeader( + "Material Inspector", + "Presentation belongs only to this Shader's authoring graph. It does not modify existing Material values or enter the Player." + )) return; string group = presentation.group; bool groupChanged = false; @@ -165,6 +205,7 @@ private void DrawParameter(ShaderGraphInputSettings input) presentation.group = group; SavePresentation(true); } + Gesture(); string description = presentation.description; bool descriptionChanged = false; @@ -174,6 +215,7 @@ private void DrawParameter(ShaderGraphInputSettings input) presentation.description = description; SavePresentation(true); } + Gesture(); bool visible = presentation.visible; InspectorRow("parameter.visible", "Visible in Material", () => @@ -197,41 +239,40 @@ private void DrawParameter(ShaderGraphInputSettings input) }); if (range) { - m_inspection.properties.DrawValue(m_inspection.editorContext, draft, "shader.parameter." + property.id.value + ".minimum", - "Minimum", typeof(double), () => presentation.minimum, value => - { - if (!double.IsFinite((double)value!)) - return; - presentation.minimum = Math.Clamp((double)value!, -float.MaxValue, presentation.maximum); - SavePresentation(true); - }, new ParameterEdits(this), draft.readOnly, minimum: -float.MaxValue, maximum: presentation.maximum); - m_inspection.properties.DrawValue(m_inspection.editorContext, draft, "shader.parameter." + property.id.value + ".maximum", - "Maximum", typeof(double), () => presentation.maximum, value => - { - if (!double.IsFinite((double)value!)) - return; - presentation.maximum = Math.Clamp((double)value!, presentation.minimum, float.MaxValue); - SavePresentation(true); - }, new ParameterEdits(this), draft.readOnly, minimum: presentation.minimum, maximum: float.MaxValue); - Widget.Hint("Editing bounds only · existing defaults and overrides are not clamped on display"); + m_inspection.properties.DrawValue(m_inspection.editorContext, draft, "shader.parameter." + property.id.value + ".minimum", "Minimum", typeof(double), () => presentation.minimum, value => + { + if (!double.IsFinite((double)value!)) + return; + presentation.minimum = Math.Clamp((double)value!, -float.MaxValue, presentation.maximum); + SavePresentation(true); + }, new ParameterEdits(this), draft.readOnly, minimum: -float.MaxValue, maximum: presentation.maximum); + m_inspection.properties.DrawValue(m_inspection.editorContext, draft, "shader.parameter." + property.id.value + ".maximum", "Maximum", typeof(double), () => presentation.maximum, value => + { + if (!double.IsFinite((double)value!)) + return; + presentation.maximum = Math.Clamp((double)value!, presentation.minimum, float.MaxValue); + SavePresentation(true); + }, new ParameterEdits(this), draft.readOnly, minimum: presentation.minimum, maximum: float.MaxValue); + EditorUI.current.widgets.Hint("Editing bounds only · existing defaults and overrides are not clamped on display"); } } + ShaderPropertyDefinition shown = property; shown.displayName = "Default"; - ShaderPropertyInspector.Draw(m_inspection, draft, "shader.parameter." + property.id.value, shown, property.defaultValue, - value => - { - property.defaultValue = value; - Save(property, true); - }, new ParameterEdits(this), draft.readOnly, presentation); + ShaderPropertyInspector.Draw(m_inspection, draft, "shader.parameter." + property.id.value, shown, property.defaultValue, value => + { + property.defaultValue = value; + Save(property, true); + }, new ParameterEdits(this), draft.readOnly, presentation); void SavePresentation(bool continuous) { Gesture(); GraphDocument candidate = Controller.document.Clone(); ShaderParameterPresentation.Write(candidate, property.id, presentation, owner.serialization, owner.context); - Controller.ReplaceDocument(candidate, "Edit Parameter Presentation", continuous && ImGuiApi.IsAnyItemActive() ? draft.valueGesture : null); + Controller.ReplaceDocument(candidate, "Edit Parameter Presentation", continuous ? EditorUI.current.widgets.editMergeKey : null); owner.Changed(draft); } + void Save( ShaderPropertyDefinition value, bool continuous @@ -243,30 +284,15 @@ bool continuous private sealed class ParameterEdits(ShaderEditorCanvas canvas) : Inno.Editor.Inspection.IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) { + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) { canvas.Gesture(); mutation(); return true; @@ -279,8 +305,11 @@ private void CommitDefinition( bool continuous = false ) { GraphDocument candidate = Controller.document.Clone(); - candidate.SetMetadata(ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(owner.serialization.Serialize(definition, owner.context), owner.serialization, owner.context)); - Controller.ReplaceDocument(candidate, label, continuous ? draft.valueGesture : null); + candidate.SetMetadata( + ShaderGraphDocument.definitionKey, + ShaderGraphDocument.Encode(owner.serialization.Serialize(definition, owner.context), owner.serialization, owner.context) + ); + Controller.ReplaceDocument(candidate, label, continuous ? EditorUI.current.widgets.editMergeKey : null); owner.Changed(draft); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs index 7f7928da3..975fb4812 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs @@ -1,22 +1,22 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Linq; using Inno.Core.Graphs; -using Inno.Native.ImGui; using Inno.Rendering; using Inno.Rendering.Shaders; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void StageSettingsPopup( GraphNodeRecord node, ShaderGraphStageSettings stage ) { - ImGuiApi.SetNextWindowSize(new(540, 600), (int)(ImGuiCond.Appearing)); + ImGuiApi.SetNextWindowSize(new(540, 600), (ImGuiCond.Appearing)); if (!ImGuiApi.BeginPopup("##stage-settings")) return; try @@ -32,16 +32,16 @@ ShaderGraphStageSettings stage definition.name = name; definitionChanged = true; } + Gesture(); ImGuiApi.SeparatorText(stage.stage + " Interface"); ShaderGraphPassProgram[] programs = ShaderGraphPrograms.Read(Controller.document, owner.serialization, owner.context); string activePass = draft.inspectedPass; if (!definition.passes.Any(pass => pass.name == activePass)) - activePass = programs.FirstOrDefault(program => program.stages.Contains(node.id.value, StringComparer.Ordinal)).pass - ?? definition.passes.FirstOrDefault().name ?? ""; + activePass = programs.FirstOrDefault(program => program.stages.Contains(node.id.value, StringComparer.Ordinal)).pass ?? definition.passes.FirstOrDefault().name ?? ""; InspectorRow("settings.pass", "Pass State", () => { - if (!Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##pass-state", activePass)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##pass-state", activePass)) return; try { @@ -54,7 +54,7 @@ ShaderGraphStageSettings stage } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); ShaderGraphPassProgram assignment = programs.FirstOrDefault(program => program.pass == activePass); @@ -67,16 +67,17 @@ ShaderGraphStageSettings stage var stageIds = (assignment.stages ?? []).Where(id => id != node.id.value).Select(static id => new GraphNodeId(id)).ToList(); if (assigned) { - stageIds.RemoveAll(id => Controller.document.FindNode(id) is GraphNodeRecord existing - && Read(existing, "settings", new ShaderGraphStageSettings()).stage == stage.stage); + stageIds.RemoveAll(id => Controller.document.FindNode(id) is GraphNodeRecord existing && Read(existing, "settings", new ShaderGraphStageSettings()).stage == stage.stage); stageIds.Add(node.id); } + Controller.ReplaceDocument(ShaderGraphPrograms.Bind(Controller.document, activePass, stageIds, owner.serialization, owner.context), "Assign Shared Shader Stage"); owner.Changed(draft); } + for (int i = 0; i < stage.outputs.Length; i++) { - ImGuiApi.PushID(i); + ImGuiApi.PushId(i); ShaderGraphOutput output = stage.outputs[i]; bool changed = false; string id = output.id; @@ -87,6 +88,7 @@ ShaderGraphStageSettings stage output.id = id; changed = true; } + Gesture(); ShaderIrOutputKind kind = output.kind; if (EnumControl("Destination", ref kind)) @@ -94,6 +96,7 @@ ShaderGraphStageSettings stage output.kind = kind; changed = true; } + string semantic = output.semantic ?? ""; bool semanticChanged = false; InspectorRow("output.semantic", "Semantic", () => semanticChanged = EditorImGui.InputText("##semantic", ref semantic, 128)); @@ -102,6 +105,7 @@ ShaderGraphStageSettings stage output.semantic = semantic; changed = true; } + Gesture(); int location = output.location; bool locationChanged = false; @@ -111,12 +115,14 @@ ShaderGraphStageSettings stage output.location = location; changed = true; } + Gesture(); if (changed) { stage.outputs[i] = output; Set(node, "settings", stage, true); } + if (ImGuiApi.SmallButton("Remove Output")) { stage.outputs = stage.outputs.Where(( @@ -124,19 +130,28 @@ ShaderGraphStageSettings stage index ) => index != i).ToArray(); Set(node, "settings", stage); - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } + ImGuiApi.Separator(); - ImGuiApi.PopID(); + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Output")) { - stage.outputs = [.. stage.outputs, new() { id = "output-" + Guid.NewGuid().ToString("N"), + stage.outputs = [..stage.outputs, new() + { + id = "output-" + Guid.NewGuid().ToString("N"), kind = stage.stage == ShaderStage.Vertex ? ShaderIrOutputKind.Varying : ShaderIrOutputKind.Color, - semantic = "texcoord", location = stage.outputs.Length }]; + semantic = "texcoord", + location = stage.outputs.Length + } + + ]; Set(node, "settings", stage); } + int passIndex = Array.FindIndex(definition.passes, pass => pass.name == activePass); if (passIndex >= 0 && ImGuiApi.CollapsingHeader("Pass State")) { @@ -149,24 +164,28 @@ ShaderGraphStageSettings stage state.topology = topology; changed = true; } + ShaderCullMode cull = state.cull; if (EnumControl("Cull", ref cull)) { state.cull = cull; changed = true; } + RenderFrontFace front = state.frontFace; if (EnumControl("Front Face", ref front)) { state.frontFace = front; changed = true; } + ShaderCompareFunction depth = state.depthCompare; if (EnumControl("Depth Test", ref depth)) { state.depthCompare = depth; changed = true; } + bool depthWrite = state.depthWrite, multisampling = state.multisampling; bool depthWriteChanged = false, multisamplingChanged = false; InspectorRow("state.depth-write", "Depth Write", () => depthWriteChanged = ImGuiApi.Checkbox("##depth-write", ref depthWrite)); @@ -176,14 +195,16 @@ ShaderGraphStageSettings stage state.depthWrite = depthWrite; changed = true; } + if (multisamplingChanged) { state.multisampling = multisampling; changed = true; } + InspectorRow("state.blend-preset", "Blend Preset", () => { - if (!Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##blend-preset", state.blend.enabled ? "Blended" : "Opaque")) + if (!EditorUI.current.widgets.BeginBoundedCombo("##blend-preset", state.blend.enabled ? "Blended" : "Opaque")) return; try { @@ -192,16 +213,19 @@ ShaderGraphStageSettings stage state.blend = RenderBlendState.opaque; changed = true; } + if (ImGuiApi.Selectable("Alpha")) { state.blend = RenderBlendState.alpha; changed = true; } + if (ImGuiApi.Selectable("Premultiplied")) { state.blend = RenderBlendState.premultiplied; changed = true; } + if (ImGuiApi.Selectable("Additive")) { state.blend = RenderBlendState.additive; @@ -210,7 +234,7 @@ ShaderGraphStageSettings stage } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); int mask = state.colorWriteMask; @@ -221,8 +245,9 @@ ShaderGraphStageSettings stage state.colorWriteMask = (byte)Math.Clamp(mask, 0, 15); changed = true; } + RenderBlendState blend = state.blend; - if (ImGuiApi.TreeNodeEx("Custom Blending", 0)) + if (ImGuiApi.TreeNode("Custom Blending", 0)) { bool enabled = blend.enabled; bool enabledChanged = false; @@ -232,41 +257,48 @@ ShaderGraphStageSettings stage blend.enabled = enabled; changed = true; } - RenderBlendFactor colorSource = blend.colorSource, colorDestination = blend.colorDestination, - alphaSource = blend.alphaSource, alphaDestination = blend.alphaDestination; + + RenderBlendFactor colorSource = blend.colorSource, colorDestination = blend.colorDestination, alphaSource = blend.alphaSource, alphaDestination = blend.alphaDestination; RenderBlendEquation colorEquation = blend.colorEquation, alphaEquation = blend.alphaEquation; if (EnumControl("RGB Source", ref colorSource)) { blend.colorSource = colorSource; changed = true; } + if (EnumControl("RGB Destination", ref colorDestination)) { blend.colorDestination = colorDestination; changed = true; } + if (EnumControl("RGB Equation", ref colorEquation)) { blend.colorEquation = colorEquation; changed = true; } + if (EnumControl("Alpha Source", ref alphaSource)) { blend.alphaSource = alphaSource; changed = true; } + if (EnumControl("Alpha Destination", ref alphaDestination)) { blend.alphaDestination = alphaDestination; changed = true; } + if (EnumControl("Alpha Equation", ref alphaEquation)) { blend.alphaEquation = alphaEquation; changed = true; } + ImGuiApi.TreePop(); } + state.blend = blend; GraphicsCapability features = pass.requiredFeatures; if (FeatureControls(ref features)) @@ -274,6 +306,7 @@ ShaderGraphStageSettings stage pass.requiredFeatures = features; changed = true; } + if (changed) { pass.renderState = state; @@ -281,12 +314,13 @@ ShaderGraphStageSettings stage definitionChanged = true; } } + definitionChanged |= TechniqueControls(definition); if (ImGuiApi.CollapsingHeader("Variants")) { for (int i = 0; i < definition.keywords.Length; i++) { - ImGuiApi.PushID("keyword-" + i); + ImGuiApi.PushId("keyword-" + i); ShaderKeywordDefinition keyword = definition.keywords[i]; string id = keyword.id; bool keywordChanged = false; @@ -296,6 +330,7 @@ ShaderGraphStageSettings stage keyword.id = id; definitionChanged = true; } + Gesture(); for (int j = 0; j < keyword.options.Length; j++) { @@ -312,6 +347,7 @@ ShaderGraphStageSettings stage keyword.options[j] = option; definitionChanged = true; } + Gesture(); if (remove) { @@ -323,11 +359,13 @@ ShaderGraphStageSettings stage break; } } + if (CenteredAddButton("Add Option")) { - keyword.options = [.. keyword.options, "Option" + keyword.options.Length]; + keyword.options = [..keyword.options, "Option" + keyword.options.Length]; definitionChanged = true; } + definition.keywords[i] = keyword; if (ImGuiApi.SmallButton("Remove Keyword")) { @@ -336,21 +374,27 @@ ShaderGraphStageSettings stage index ) => index != i).ToArray(); definitionChanged = true; - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } - ImGuiApi.PopID(); + + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Keyword")) { - definition.keywords = [.. definition.keywords, new("Keyword" + definition.keywords.Length, ["Off", "On"])]; + definition.keywords = [..definition.keywords, new("Keyword" + definition.keywords.Length, ["Off", "On"])]; definitionChanged = true; } } + if (definitionChanged) { GraphDocument candidate = Controller.document.Clone(); - candidate.SetMetadata(ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(owner.serialization.Serialize(definition, owner.context), owner.serialization, owner.context)); + candidate.SetMetadata( + ShaderGraphDocument.definitionKey, + ShaderGraphDocument.Encode(owner.serialization.Serialize(definition, owner.context), owner.serialization, owner.context) + ); Controller.ReplaceDocument(candidate, "Edit Shader Settings", ImGuiApi.IsAnyItemActive() ? draft.valueGesture : null); owner.Changed(draft); } @@ -364,13 +408,14 @@ ShaderGraphStageSettings stage private bool EnumControl( string label, ref T value - ) where T : struct, Enum + ) + where T : struct, Enum { bool changed = false; T current = value; InspectorRow("enum." + label, label, () => { - if (!Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##enum", current.ToString())) + if (!EditorUI.current.widgets.BeginBoundedCombo("##enum", current.ToString())) return; try { @@ -383,7 +428,7 @@ ref T value } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); value = current; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PortRepair.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PortRepair.cs index f6fd36213..54cb928bc 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PortRepair.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PortRepair.cs @@ -1,12 +1,11 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Linq; using Inno.Core.Graphs; using Inno.Rendering.Shaders; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void RepairPorts(GraphNodeRecord node) @@ -20,15 +19,15 @@ private void RepairPorts(GraphNodeRecord node) return; try { - Widget.WrappedText("Removed ports retain their connections. Choose a replacement explicitly; occupied inputs are not overwritten."); + EditorUI.current.widgets.WrappedText("Removed ports retain their connections. Choose a replacement explicitly; occupied inputs are not overwritten."); foreach (GraphEndpoint endpoint in missing) { ShaderNodePort old = Port(endpoint); - ImGuiApi.PushID(endpoint.portId.value); + ImGuiApi.PushId(endpoint.portId.value); try { - ImGuiApi.TextUnformatted(endpoint.portId.value + " · " + old.type.id); - if (!Widget.BeginBoundedCombo("##replacement", "Reconnect to…")) + ImGuiApi.Text(endpoint.portId.value + " · " + old.type.id); + if (!EditorUI.current.widgets.BeginBoundedCombo("##replacement", "Reconnect to…")) continue; foreach (ShaderNodePort replacement in draft.ports[node.id]) { @@ -48,18 +47,22 @@ private void RepairPorts(GraphNodeRecord node) candidate.RemoveEdge(edge.id); candidate.AddEdge(new(edge.id, edge.output == endpoint ? target : edge.output, edge.input == endpoint ? target : edge.input)); } + // Keep the retired declaration in metadata for a future source restoration and Undo. - candidate.FindNode(node.id)!.SetValue(ShaderEditorDocuments.C_PORT_SNAPSHOT, - ShaderGraphDocument.Encode(draft.portSnapshots[node.id], owner.serialization, owner.context)); + candidate.FindNode(node.id)!.SetValue( + ShaderEditorDocuments.C_PORT_SNAPSHOT, + ShaderGraphDocument.Encode(draft.portSnapshots[node.id], owner.serialization, owner.context) + ); Controller.ReplaceDocument(candidate, "Reconnect Shader Port"); owner.Changed(draft); break; } - Widget.EndBoundedCombo(); + + EditorUI.current.widgets.EndBoundedCombo(); } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs index ea8115590..6ba83640c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs @@ -1,17 +1,16 @@ -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.IO; using System.Linq; using Inno.Assets; using Inno.Assets.Pipeline; -using Inno.Native.ImGui; using Inno.Rendering.Assets; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void SourceSettings(Guid sourceId) @@ -21,7 +20,8 @@ private void SourceSettings(Guid sourceId) InspectorRow("source.actions", "Source", () => { if (ImGuiApi.SmallButton("Show in File Browser") - && owner.interactions.TryGetModule(out var browser) && browser is not null) + && owner.interactions.TryGetModule(out var browser) + && browser is not null) RevealShaderFunction.Reveal(owner, browser, sourceId); ImGuiApi.SameLine(0f, -1f); if (ImGuiApi.SmallButton("Import Settings…")) @@ -30,12 +30,12 @@ private void SourceSettings(Guid sourceId) ImGuiApi.OpenPopup("##source-settings"); } }); - ImGuiApi.SetNextWindowSize(new(580, 470), (int)(ImGuiCond.Appearing)); + ImGuiApi.SetNextWindowSize(new(580, 470), (ImGuiCond.Appearing)); if (!ImGuiApi.BeginPopup("##source-settings")) return; try { - ImGuiApi.TextUnformatted(info.assetPath.ToString()); + ImGuiApi.Text(info.assetPath.ToString()); ImGuiApi.Separator(); if (draft.settingsSource != sourceId) LoadSourceSettings(info.assetPath, sourceId); @@ -47,7 +47,7 @@ private void SourceSettings(Guid sourceId) bool changed = false; InspectorRow("source.language", "Language", () => { - if (!Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##language", settings.languageId)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##language", settings.languageId)) return; try { @@ -60,7 +60,7 @@ private void SourceSettings(Guid sourceId) } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); string implementation = settings.implementationId; @@ -71,6 +71,7 @@ private void SourceSettings(Guid sourceId) settings.implementationId = implementation; changed = true; } + string catalog = settings.catalogPath; bool catalogChanged = false; InspectorRow("source.catalog", "Catalog Path", () => catalogChanged = EditorImGui.InputText("##catalog", ref catalog, 256)); @@ -79,6 +80,7 @@ private void SourceSettings(Guid sourceId) settings.catalogPath = catalog; changed = true; } + int catalogOrder = settings.catalogOrder; bool orderChanged = false; InspectorRow("source.catalog-order", "Catalog Order", () => orderChanged = ImGuiApi.InputInt("##catalog-order", ref catalogOrder, 1, 100, 0)); @@ -87,11 +89,12 @@ private void SourceSettings(Guid sourceId) settings.catalogOrder = catalogOrder; changed = true; } - EditorWidget.WrappedText("Catalog paths are declared by the source library and group its exported functions in Shader creation tools. They do not affect compilation or runtime assets."); + + EditorUI.current.widgets.WrappedText("Catalog paths are declared by the source library and group its exported functions in Shader creation tools. They do not affect compilation or runtime assets."); ImGuiApi.SeparatorText("Exported Functions"); for (int i = 0; i < settings.exports.Length; i++) { - ImGuiApi.PushID("export." + i); + ImGuiApi.PushId("export." + i); string function = settings.exports[i]; bool functionChanged = false, remove = false; InspectorRow("source.export", "Function " + (i + 1), () => @@ -105,6 +108,7 @@ private void SourceSettings(Guid sourceId) settings.exports[i] = function; changed = true; } + if (remove) { settings.exports = settings.exports.Where(( @@ -112,33 +116,44 @@ private void SourceSettings(Guid sourceId) index ) => index != i).ToArray(); changed = true; - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } - ImGuiApi.PopID(); + + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Export")) { - settings.exports = [.. settings.exports, "Function"]; + settings.exports = [..settings.exports, "Function"]; changed = true; } - EditorWidget.WrappedText("Every listed name is exported as an independent graph function. All other functions remain private helpers. Ports are parsed from each exported declaration; alternative implementations must expose matching interfaces."); + + EditorUI.current.widgets.WrappedText("Every listed name is exported as an independent graph function. All other functions remain private helpers. Ports are parsed from each exported declaration; alternative implementations must expose matching interfaces."); ImGuiApi.SeparatorText("Alternative Implementations"); for (int i = 0; i < settings.implementations.Length; i++) { - ImGuiApi.PushID(i); + ImGuiApi.PushId(i); ShaderFunctionAsset? current = settings.implementations[i]; bool remove = false; InspectorRow("source.implementation", "Implementation " + (i + 1), () => { - if (Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##implementation", current?.assetPath.ToString() ?? "Missing source")) + if (EditorUI.current.widgets.BeginBoundedCombo("##implementation", current?.assetPath.ToString() ?? "Missing source")) { try { foreach (AssetFileEntry source in owner.assets.GetFileSystemEntries(includeDirectories: false)) - if (source.extension == ".ishadersource" && source.assetPath != info.assetPath - && ImGuiApi.Selectable(source.assetPath.ToString(), current?.assetPath == source.assetPath) - && owner.assets.TryLoad(source.assetPath, out ShaderFunctionAsset? replacement) && replacement is not null) + if (source.extension == ".ishadersource" + && source.assetPath != info.assetPath + && ImGuiApi.Selectable( + source.assetPath.ToString(), + current?.assetPath == source.assetPath + ) + && owner.assets.TryLoad( + source.assetPath, + out ShaderFunctionAsset? replacement + ) + && replacement is not null) { settings.implementations[i] = replacement; changed = true; @@ -146,9 +161,10 @@ private void SourceSettings(Guid sourceId) } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } + ImGuiApi.SameLine(0f, -1f); remove = ImGuiApi.SmallButton("Remove"); }); @@ -159,16 +175,19 @@ private void SourceSettings(Guid sourceId) index ) => index != i).ToArray(); changed = true; - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } - ImGuiApi.PopID(); + + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Implementation")) { - settings.implementations = [.. settings.implementations, null!]; + settings.implementations = [..settings.implementations, null!]; changed = true; } + if (changed) draft.sourceSettings = owner.serialization.Serialize(settings, owner.context); ImGuiApi.Separator(); @@ -184,18 +203,20 @@ private void SourceSettings(Guid sourceId) { ImGuiApi.EndDisabled(); } + ImGuiApi.SameLine(0f, -1f); if (ImGuiApi.Button("Reload")) LoadSourceSettings(info.assetPath, sourceId); if (readOnly) - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Installed source settings are read-only."); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Installed source settings are read-only."); if (draft.sourceSettingsStatus.Length != 0) - EditorWidget.WrappedText(draft.sourceSettingsStatus); + EditorUI.current.widgets.WrappedText(draft.sourceSettingsStatus); } - catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.sourceSettingsStatus = failure.Message; - EditorWidget.WrappedText(draft.sourceSettingsStatus); + EditorUI.current.widgets.WrappedText(draft.sourceSettingsStatus); } finally { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Target.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Target.cs index fee269c56..90888483c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Target.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Target.cs @@ -1,10 +1,9 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Core.Graphs; using Inno.Rendering.Shaders; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private void DrawTarget() @@ -15,7 +14,7 @@ private void DrawTarget() { InspectorRow("target", "Target", () => { - if (!Widget.BeginBoundedCombo("##target", current.Length == 0 ? "Explicit Stages" : current)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##target", current.Length == 0 ? "Explicit Stages" : current)) return; try { @@ -28,7 +27,7 @@ private void DrawTarget() } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); } @@ -36,6 +35,7 @@ private void DrawTarget() { ImGuiApi.EndDisabled(); } + void Assign(string id) { if (id == current) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs index a0a21d3b9..ca052718f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs @@ -1,41 +1,48 @@ -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Linq; using Inno.Rendering; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - internal sealed partial class ShaderEditorCanvas { private bool TechniqueControls(ShaderDefinition definition) { if (!ImGuiApi.CollapsingHeader("Techniques & Roles")) return false; - EditorWidget.WrappedText("Techniques map pipeline-defined roles to this shader's passes. Contract and role identities are supplied by the rendering pipeline."); + EditorUI.current.widgets.WrappedText("Techniques map pipeline-defined roles to this shader's passes. Contract and role identities are supplied by the rendering pipeline."); bool changed = false; for (int i = 0; i < definition.techniques.Length; i++) { - ImGuiApi.PushID("technique-" + i); + ImGuiApi.PushId("technique-" + i); ShaderTechniqueDefinition technique = definition.techniques[i]; string id = technique.id.value ?? "", contract = technique.contract.value ?? ""; bool idChanged = false; InspectorRow("technique.id", "ID", () => idChanged = EditorImGui.InputText("##id", ref id, 128)); if (idChanged) { - technique.id = new() { value = id }; + technique.id = new() + { + value = id + }; changed = true; } + Gesture(); bool contractChanged = false; InspectorRow("technique.contract", "Contract", () => contractChanged = EditorImGui.InputText("##contract", ref contract, 256)); if (contractChanged) { - technique.contract = new() { value = contract }; + technique.contract = new() + { + value = contract + }; changed = true; } + Gesture(); GraphicsCapability features = technique.requiredFeatures; if (FeatureControls(ref features)) @@ -43,23 +50,28 @@ private bool TechniqueControls(ShaderDefinition definition) technique.requiredFeatures = features; changed = true; } + for (int j = 0; j < technique.passes.Length; j++) { - ImGuiApi.PushID(j); + ImGuiApi.PushId(j); ShaderTechniquePass mapping = technique.passes[j]; string role = mapping.role.value ?? ""; bool roleChanged = false; InspectorRow("technique.role", "Role", () => roleChanged = EditorImGui.InputText("##role", ref role, 128)); if (roleChanged) { - mapping.role = new() { value = role }; + mapping.role = new() + { + value = role + }; changed = true; } + Gesture(); bool passChanged = false; InspectorRow("technique.pass", "Pass", () => { - if (!Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo("##pass", mapping.passName)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##pass", mapping.passName)) return; try { @@ -72,7 +84,7 @@ private bool TechniqueControls(ShaderDefinition definition) } finally { - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } }); changed |= passChanged; @@ -84,16 +96,25 @@ private bool TechniqueControls(ShaderDefinition definition) index ) => index != j).ToArray(); changed = true; - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } - ImGuiApi.PopID(); + + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Role")) { - technique.passes = [.. technique.passes, new() { role = new("role-" + technique.passes.Length), passName = definition.passes.FirstOrDefault().name ?? "" }]; + technique.passes = [..technique.passes, new() + { + role = new("role-" + technique.passes.Length), + passName = definition.passes.FirstOrDefault().name ?? "" + } + + ]; changed = true; } + definition.techniques[i] = technique; if (ImGuiApi.SmallButton("Remove Technique")) { @@ -102,23 +123,33 @@ private bool TechniqueControls(ShaderDefinition definition) index ) => index != i).ToArray(); changed = true; - ImGuiApi.PopID(); + ImGuiApi.PopId(); break; } + ImGuiApi.Separator(); - ImGuiApi.PopID(); + ImGuiApi.PopId(); } + if (CenteredAddButton("Add Technique")) { - definition.techniques = [.. definition.techniques, new() { id = new("technique-" + Guid.NewGuid().ToString("N")), contract = default, passes = [] }]; + definition.techniques = [..definition.techniques, new() + { + id = new("technique-" + Guid.NewGuid().ToString("N")), + contract = default, + passes = [] + } + + ]; changed = true; } + return changed; } private static bool FeatureControls(ref GraphicsCapability features) { - if (!ImGuiApi.TreeNodeEx("Required Capabilities", 0)) + if (!ImGuiApi.TreeNode("Required Capabilities", 0)) return false; bool changed = false; GraphicsCapability updated = features; @@ -136,6 +167,7 @@ private static bool FeatureControls(ref GraphicsCapability features) changed = true; } } + features = updated; ImGuiApi.TreePop(); return changed; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs index 9e63f7ee0..935a1a1d5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs @@ -1,7 +1,7 @@ -using System.Runtime.CompilerServices; -using NumericsVector2 = System.Numerics.Vector2; -using NativeVector2 = Inno.Native.ImGui.ImVec2C; -using BGCS.Runtime; +using Inno.Editor.Core; +using UiDrawing = Inno.Editor.ImGui.ImGui; +using Inno.Editor.Graph.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Linq; @@ -13,23 +13,21 @@ using Inno.Editor.Interactions; using Inno.Editor.Rendering; using Inno.Editor.Shaders; -using Inno.Native.ImGui; +using Inno.Editor.Shaders.ImGui; using Inno.Rendering.Assets; using Inno.Rendering.Shaders; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.ShaderEditor; - -internal sealed unsafe partial class ShaderEditorCanvas( +internal sealed partial class ShaderEditorCanvas( ShaderEditorDocuments owner, ShaderEditorDocuments.Draft draft -) { +) +{ private readonly record struct PortHandle( GraphEndpoint endpoint, GraphPortDirection direction ); - internal const string C_AREA = "panel/rendering.shader-editor"; private const string C_NODE_WIDTH = "editor.width"; private const float C_DEFAULT_WIDTH = 270f; @@ -40,14 +38,15 @@ GraphPortDirection direction private const float C_RESIZE_HANDLE = 14f; private GraphDocumentController Controller => owner.Controller(draft); private GraphCanvasState Canvas => draft.canvas; + private GraphCanvasView View => new(Canvas, m_origin, m_size); + private Vector2 m_origin; private Vector2 m_size; - internal void Draw() { DrawHeader(owner, draft); - m_origin = ImGuiApi.GetCursorScreenPos(); - m_size = Vector2.Max(ImGuiApi.GetContentRegionAvail(), Vector2.One); + m_origin = ImGuiApi.GetCursorScreenPosition(); + m_size = Vector2.Max(ImGuiApi.GetContentRegionAvailable(), Vector2.One); RefreshPorts(); owner.RefreshCompilation(draft); if (draft.frameRequested) @@ -55,13 +54,14 @@ internal void Draw() Frame(); draft.frameRequested = false; } + Dictionary points = PortPositions(); ImGuiApi.SetNextItemAllowOverlap(); - _ = ImGuiApi.InvisibleButton("##shader-canvas", m_size, (int)(ImGuiButtonFlags.MouseButtonLeft | ImGuiButtonFlags.MouseButtonMiddle | ImGuiButtonFlags.MouseButtonRight)); - bool hovered = ImGuiApi.IsItemHovered((int)ImGuiHoveredFlags.AllowWhenBlockedByActiveItem); + _ = ImGuiApi.InvisibleButton("##shader-canvas", m_size, (ImGuiButtonFlags.MouseButtonLeft | ImGuiButtonFlags.MouseButtonMiddle | ImGuiButtonFlags.MouseButtonRight)); + bool hovered = ImGuiApi.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem); Vector2 mouse = ImGuiApi.GetMousePos(); bool miniMapHovered = MiniMapContains(mouse); - if (hovered && !miniMapHovered && ImGuiApi.IsMouseClicked((int)ImGuiMouseButton.Right)) + if (hovered && !miniMapHovered && ImGuiApi.IsMouseClicked(ImGuiMouseButton.Right)) { draft.menuPosition = ToGraph(mouse); draft.createFromPort = null; @@ -71,6 +71,7 @@ internal void Draw() draft.selectedGroupId = group.id; Canvas.SelectNodes(group.nodes.Select(static id => new GraphNodeId(id))); } + GraphNodeRecord? hit = hitGroup is null ? HitNode(mouse) : null; draft.selectedEdge = hit is null ? HitEdge(mouse, points) : null; if (draft.selectedEdge is not null) @@ -79,31 +80,21 @@ internal void Draw() Canvas.SelectNodes([hit.id]); SetStage(hit); } + if (owner.assets.TryGetFileSystemEntry(draft.path, out AssetFileEntry entry)) { EditorInteraction interaction = owner.interactions.For(C_AREA, entry); - if (ImGuiApi.IsWindowFocused((int)ImGuiFocusedFlags.RootAndChildWindows)) + if (ImGuiApi.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows)) interaction.Focus(); - _ = EditorMenuRenderer.ContextMenu("##shader-menu", interaction); + _ = EditorUI.current.menus.ContextMenu("##shader-menu", interaction); } + Navigate(hovered && !miniMapHovered); - ImDrawList* draw = ImGuiApi.Raw.GetWindowDrawList(); - PushNativeClipRectangle( - draw, - Unsafe.BitCast(m_origin), - Unsafe.BitCast(m_origin + m_size), - (true ? (byte)1 : (byte)0) - ); + EditorDrawList draw = Inno.Editor.ImGui.ImGui.GetDrawList(); + draw.PushClip(m_origin, m_origin + m_size, true); try { - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(m_origin), - Unsafe.BitCast(m_origin + m_size), - Color(0.065f, 0.071f, 0.083f), - 0, - 0 - ); + draw.FilledRectangle(m_origin, m_origin + m_size, Color(0.065f, 0.071f, 0.083f), 0, false); Grid(draw); Groups(draw); Edges(draw, points); @@ -113,58 +104,32 @@ internal void Draw() if (draft.boxSelecting) { Vector2 min = Vector2.Min(draft.pointerStart, mouse), max = Vector2.Max(draft.pointerStart, mouse); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(min), - Unsafe.BitCast(max), - Color(0.52f, 0.37f, 0.78f, EditorPalette.opacitySubtle), - 0, - 0 - ); - DrawNativeRectangle( - draw, - Unsafe.BitCast(min), - Unsafe.BitCast(max), - Color(0.65f, 0.47f, 0.88f), - 0, - 0, - 1f - ); + draw.FilledRectangle(min, max, Color(0.52f, 0.37f, 0.78f, EditorPalette.opacitySubtle), 0, false); + draw.Rectangle(min, max, Color(0.65f, 0.47f, 0.88f), 0, 1f); } + string status = draft.readOnly ? "Read-only · copy to project to edit" : Controller.isDirty ? "Unsaved changes · Save to apply" : draft.status; status += " · Saved asset: " + draft.compilationStatus; - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(status, allowsNull: false)) { - DrawNativeText( - draw, - Unsafe.BitCast(m_origin + new Vector2(12, 10)), - ImGuiApi.GetColorU32((int)ImGuiCol.TextDisabled), - (sbyte*)nativeText.pointer, - null - ); + draw.Text(m_origin + new Vector2(12, 10), ImGuiApi.GetStyleColor(ImGuiCol.TextDisabled), status, 1f); } + if (draft.compilationDiagnostics.Length != 0 && mouse.Y < m_origin.Y + 32 && hovered) - Widget.DrawTooltip(draft.compilationDiagnostics); + EditorUI.current.widgets.DrawTooltip(draft.compilationDiagnostics); if (draft.error.Length != 0) - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(draft.error, allowsNull: false)) - { - DrawNativeText( - draw, - Unsafe.BitCast(m_origin + new Vector2(12, 34)), - Color(1f, 0.47f, 0.44f), - (sbyte*)nativeText.pointer, - null - ); - } + { + draw.Text(m_origin + new Vector2(12, 34), Color(1f, 0.47f, 0.44f), draft.error, 1f); + } } finally { - ImGuiApi.Raw.PopClipRect(draw); + draw.PopClip(); } + DrawMiniMap(); // Re-submit the canvas footprint after absolutely positioned node controls. A cursor // move alone can exceed ImGui's pixel-rounded item bounds at fractional UI scales. - ImGuiApi.SetCursorScreenPos(m_origin); + ImGuiApi.SetCursorScreenPosition(m_origin); ImGuiApi.Dummy(m_size); SynchronizeInspection(); } @@ -174,7 +139,8 @@ private void SynchronizeInspection() if (Canvas.selectedNodes.SequenceEqual(draft.inspectedNodes)) return; draft.inspectedNodes = Canvas.selectedNodes.ToArray(); - owner.interactions.SetSelection(new ShaderInspectionSelection(draft.id, draft.inspectedNodes)); + draft.inspectionSelection = new ShaderInspectionSelection(draft.id, draft.inspectedNodes); + owner.interactions.SetSelection(draft.inspectionSelection); } internal void DrawInspector( @@ -198,47 +164,61 @@ private void DrawInspectorContents(IReadOnlyList selected) bool graphNode = ShaderGraphNodes.IsNodeGraph(Controller.document); if (!graphNode) { - if (Widget.SectionHeader("Preview", "The preview uses the current draft without publishing it to Scene or Game views.")) + if (EditorUI.current.widgets.SectionHeader("Preview", "The preview uses the current draft without publishing it to Scene or Game views.")) { var preview = owner.Preview(draft); if (preview is null) { - Widget.Hint("Run Check to build a preview for the current draft."); + EditorUI.current.widgets.Hint("Run Check to build a preview for the current draft."); } else if (preview.state == EditorShaderCompilationState.Failed) { - Widget.Hint("Shader check failed. See Console."); + EditorUI.current.widgets.Hint("Shader check failed. See Console."); } - else if (preview.state == EditorShaderCompilationState.Succeeded && preview.artifact is not null - && owner.interactions.TryGetModule(out var images) && images is not null) + else if (preview.state == EditorShaderCompilationState.Succeeded + && preview.artifact is not null + && owner.interactions.TryGetModule(out var images) + && images is not null) { var shader = owner.assets.Load(draft.path); - images.Draw(draft.id, new Inno.Rendering.Assets.MaterialAsset { shader = shader }, preview, - MathF.Max(1f, MathF.Min(256f, ImGuiApi.GetContentRegionAvail().X))); + images.Draw( + draft.id, + new Inno.Rendering.Assets.MaterialAsset { shader = shader }, + preview, + MathF.Max(1f, MathF.Min(256f, ImGuiApi.GetContentRegionAvailable().X)) + ); } } } else { - if (Widget.SectionHeader("Graph Node", "Function Inputs and Function Outputs independently define the reusable typed interface. Check validates the interface; callers validate the inlined computation.")) + if (EditorUI.current.widgets.SectionHeader( + "Graph Node", + "Function Inputs and Function Outputs independently define the reusable typed interface. Check validates the interface; callers validate the inlined computation." + )) { try { ShaderGraphNodeInterface nodeInterface = ShaderGraphNodes.ReadInterface(Controller.document, owner.serialization, owner.context); - InspectorRow("graph-node.summary", "Interface", () => Widget.WrappedText( - $"{nodeInterface.inputs.Length} input(s) · {nodeInterface.outputs.Length} output(s) · {nodeInterface.kind}")); + InspectorRow( + "graph-node.summary", + "Interface", + () => EditorUI.current.widgets.WrappedText($"{nodeInterface.inputs.Length} input(s) · {nodeInterface.outputs.Length} output(s) · {nodeInterface.kind}") + ); } catch (Exception failure) when (failure is InvalidOperationException or ArgumentException or FormatException or NotSupportedException) { - Widget.Hint(failure.Message); + EditorUI.current.widgets.Hint(failure.Message); } } } + if (selected.Count == 0) { - if (Widget.SectionHeader(graphNode ? "Node Definition" : "Shader", - graphNode ? "Select Function Inputs or Function Outputs to edit this reusable node's public interface." - : "Target and public interface belong to this Shader, not to any Material override.")) + if (EditorUI.current.widgets.SectionHeader( + graphNode ? "Node Definition" : "Shader", + graphNode ? "Select Function Inputs or Function Outputs to edit this reusable node's public interface." : "Target and public interface belong to this Shader, not to any Material override." + )) { if (!graphNode) DrawTarget(); @@ -246,38 +226,53 @@ private void DrawInspectorContents(IReadOnlyList selected) { var definition = ShaderGraphDocument.ReadDefinition(Controller.document, owner.serialization, owner.context); foreach (var property in definition.properties) - InspectorRow("definition." + property.id.value, property.displayName, - () => Widget.WrappedText(property.type + " · " + property.bindingOwner)); + InspectorRow( + "definition." + property.id.value, + property.displayName, + () => EditorUI.current.widgets.WrappedText(property.type + " · " + property.bindingOwner) + ); } + owner.RefreshCompilation(draft); - InspectorRow("compilation", "Compilation", () => Widget.WrappedText(draft.compilationStatus)); + InspectorRow("compilation", "Compilation", () => EditorUI.current.widgets.WrappedText(draft.compilationStatus)); } + return; } + GraphNodeRecord[] nodes = selected.Select(Controller.document.FindNode).OfType().ToArray(); if (nodes.Length != selected.Count) - Widget.Hint("Some selected nodes are unavailable. Stable identities are retained."); + EditorUI.current.widgets.Hint("Some selected nodes are unavailable. Stable identities are retained."); if (nodes.Length == 0) return; if (nodes.Any(node => node.definitionId != nodes[0].definitionId)) { - Widget.Hint("Select nodes of the same kind to edit common settings."); + EditorUI.current.widgets.Hint("Select nodes of the same kind to edit common settings."); return; } + m_inspectionNodes = nodes; - ImGuiApi.PushID(draft.id.ToString("N")); - ImGuiApi.PushID(nodes[0].id.value); + ImGuiApi.PushId(draft.id.ToString("N")); + ImGuiApi.PushId(nodes[0].id.value); ImGuiApi.BeginDisabled(draft.readOnly); try { - if (Widget.SectionHeader(Title(nodes[0]), "Inputs, defaults and output configuration are edited here. Changes remain in the Shader draft until Save.")) + if (EditorUI.current.widgets.SectionHeader( + Title(nodes[0]), + "Inputs, defaults and output configuration are edited here. Changes remain in the Shader draft until Save." + )) { if (nodes.Length > 1) - Widget.Hint("Editing " + nodes.Length + " nodes · differing values are replaced together"); + EditorUI.current.widgets.Hint("Editing " + nodes.Length + " nodes · differing values are replaced together"); Controls(nodes[0]); } + ShaderNodePort[] inputs = draft.ports[nodes[0].id].Where(port => port.direction == GraphPortDirection.Input).ToArray(); - if (inputs.Length != 0 && Widget.SectionHeader("Inputs", "Connected inputs show their source. Optional inputs use an automatic zero; required inputs must be connected in the graph.")) + if (inputs.Length != 0 + && EditorUI.current.widgets.SectionHeader( + "Inputs", + "Connected inputs show their source. Optional inputs use an automatic zero; required inputs must be connected in the graph." + )) { foreach (ShaderNodePort port in inputs) DrawInputDefault(nodes[0], port); @@ -286,39 +281,36 @@ private void DrawInspectorContents(IReadOnlyList selected) finally { ImGuiApi.EndDisabled(); - ImGuiApi.PopID(); - ImGuiApi.PopID(); + ImGuiApi.PopId(); + ImGuiApi.PopId(); m_inspectionNodes = null; } } private GraphNodeRecord[]? m_inspectionNodes; private Inno.Editor.Inspection.InspectionDrawContext? m_inspection; - private static void InspectorRow( string id, string label, Action draw - ) => Widget.PropertyRow("shader." + id, label, draw); - + ) => EditorUI.current.widgets.PropertyRow("shader." + id, label, draw); internal static void DrawHeader( ShaderEditorDocuments owner, ShaderEditorDocuments.Draft? draft ) { ArgumentNullException.ThrowIfNull(owner); - Widget.HeaderSurface("##shader-header", () => + EditorUI.current.widgets.HeaderSurface("##shader-header", () => { ImGuiApi.SetNextItemWidth(-1f); - string title = draft is null - ? "Select Shader" - : System.IO.Path.GetFileName(draft.path.localPath) + (owner.Controller(draft).isDirty ? " *" : ""); - if (Widget.BeginBoundedCombo("##shader-header-select", title)) + string title = draft is null ? "Select Shader" : System.IO.Path.GetFileName(draft.path.localPath) + (owner.Controller(draft).isDirty ? " *" : ""); + if (EditorUI.current.widgets.BeginBoundedCombo("##shader-header-select", title)) { try { - foreach (AssetFileEntry candidate in owner.assets.GetFileSystemEntries(includeDirectories: false) - .Where(static candidate => candidate.extension.Equals(".ishader", StringComparison.OrdinalIgnoreCase)) - .OrderBy(static candidate => candidate.assetPath.ToString(), StringComparer.Ordinal)) + foreach (AssetFileEntry candidate in owner.assets.GetFileSystemEntries(includeDirectories: false).Where(static candidate => candidate.extension.Equals(".ishader", StringComparison.OrdinalIgnoreCase)).OrderBy( + static candidate => candidate.assetPath.ToString(), + StringComparer.Ordinal + )) { bool selected = draft is not null && candidate.assetPath == draft.path; if (ImGuiApi.Selectable(candidate.assetPath.ToString(), selected)) @@ -326,34 +318,36 @@ internal static void DrawHeader( owner.interactions.SetSelection(candidate); _ = owner.Open(candidate); } + if (selected) ImGuiApi.SetItemDefaultFocus(); } } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } + bool noDocument = draft is null; ImGuiApi.BeginDisabled(noDocument || draft!.readOnly); if (ImGuiApi.Button("Save") && draft is not null && owner.assets.TryGetFileSystemEntry(draft.path, out AssetFileEntry entry)) _ = owner.interactions.For(C_AREA, entry).Execute("shader/save"); - Widget.DrawItemTooltip("Save this shader and apply its changes (Command/Ctrl + S). Invalid graphs can be saved; rendering retains the last successful programs."); + EditorUI.current.widgets.DrawItemTooltip("Save this shader and apply its changes (Command/Ctrl + S). Invalid graphs can be saved; rendering retains the last successful programs."); ImGuiApi.SameLine(0f, -1f); if (ImGuiApi.Button("Revert") && draft is not null) _ = owner.interactions.documents.Revert(draft.documentId); - Widget.DrawItemTooltip("Restore the saved shader. This draft change can be undone."); + EditorUI.current.widgets.DrawItemTooltip("Restore the saved shader. This draft change can be undone."); ImGuiApi.EndDisabled(); ImGuiApi.SameLine(0f, -1f); ImGuiApi.BeginDisabled(noDocument); if (ImGuiApi.Button("Format") && draft is not null && owner.assets.TryGetFileSystemEntry(draft.path, out AssetFileEntry formatEntry)) _ = owner.interactions.For(C_AREA, formatEntry).Execute("shader/format"); - Widget.DrawItemTooltip("Arrange the graph from inputs on the left to outputs on the right. This changes only authoring positions and is undoable."); + EditorUI.current.widgets.DrawItemTooltip("Arrange the graph from inputs on the left to outputs on the right. This changes only authoring positions and is undoable."); ImGuiApi.SameLine(0f, -1f); if (ImGuiApi.Button("Check") && draft is not null && owner.assets.TryGetFileSystemEntry(draft.path, out AssetFileEntry checkEntry)) _ = owner.interactions.For(C_AREA, checkEntry).Execute("shader/check"); - Widget.DrawItemTooltip("Compile-check the current draft without saving or publishing it, and show diagnostics with source locations."); + EditorUI.current.widgets.DrawItemTooltip("Compile-check the current draft without saving or publishing it, and show diagnostics with source locations."); ImGuiApi.EndDisabled(); }, spanWindowPadding: true); } @@ -375,23 +369,24 @@ private void RefreshPorts() if (node.definitionId == ShaderGraphDocument.outputDefinitionId) { ShaderGraphStageSettings settings = Read(node, "settings", new ShaderGraphStageSettings()); - ports.AddRange(settings.outputs.Select(static output => new ShaderNodePort(output.id, - ShaderSourceType.Atomic(output.kind == ShaderIrOutputKind.Depth ? "float" : output.kind == ShaderIrOutputKind.Varying ? "any" : "float4"), - GraphPortDirection.Input))); + ports.AddRange(settings.outputs.Select(static output => new ShaderNodePort(output.id, ShaderSourceType.Atomic(output.kind == ShaderIrOutputKind.Depth ? "float" : output.kind == ShaderIrOutputKind.Varying ? "any" : "float4"), GraphPortDirection.Input))); } else ports.AddRange(owner.Describe(node)); } - catch (Exception failure) when ((failure is InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.nodeErrors[node.id] = failure.Message; } + foreach (ShaderPortSnapshot old in draft.portSnapshots[node.id]) if (!ports.Any(port => port.id == old.id)) { ports.Add(new(old.id, old.type.CreateType(), old.direction, old.required)); draft.missingPorts.Add(new(node.id, new(old.id))); } + foreach (GraphEdgeRecord edge in Controller.document.edges) { if (edge.input.nodeId == node.id) @@ -399,8 +394,8 @@ private void RefreshPorts() if (edge.output.nodeId == node.id) KeepEndpoint(edge.output, GraphPortDirection.Output); } - draft.portSnapshots[node.id] = ports.Select(static port => new ShaderPortSnapshot - { id = port.id, type = ShaderGraphType.Capture(port.type), direction = port.direction, required = port.required }).ToArray(); + + draft.portSnapshots[node.id] = ports.Select(static port => new ShaderPortSnapshot { id = port.id, type = ShaderGraphType.Capture(port.type), direction = port.direction, required = port.required }).ToArray(); draft.ports.Add(node.id, ports.ToArray()); void KeepEndpoint( GraphEndpoint endpoint, @@ -412,6 +407,7 @@ GraphPortDirection direction draft.missingPorts.Add(endpoint); } } + draft.portRevision = Controller.revision; draft.groups = GroupShaderNodes.Read(owner, Controller.document); draft.assetRevision = owner.assets.revision; @@ -421,22 +417,30 @@ GraphPortDirection direction private void Navigate(bool hovered) { - ImGuiIO* io = ImGuiApi.Raw.GetIO(); - draft.navigation.Update(hovered, ImGuiApi.IsMouseClicked((int)ImGuiMouseButton.Left), ImGuiApi.IsMouseClicked((int)ImGuiMouseButton.Middle), - ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Left), ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Middle), io->KeyAlt != 0); + EditorUiInput io = UiDrawing.GetInput(); + draft.navigation.Update( + hovered, + ImGuiApi.IsMouseClicked(ImGuiMouseButton.Left), + ImGuiApi.IsMouseClicked(ImGuiMouseButton.Middle), + ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left), + ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Middle), + io.alt + ); if (draft.navigation.isPanning) { - Canvas.PanBy(io->MouseDelta.X, io->MouseDelta.Y); - ImGuiApi.SetMouseCursor((int)ImGuiMouseCursor.ResizeAll); + Canvas.PanBy(io.mouseDelta.X, io.mouseDelta.Y); + ImGuiApi.SetMouseCursor(ImGuiMouseCursor.ResizeAll); } - if (hovered && io->MouseWheel != 0) + + if (hovered && io.wheel != 0) { Vector2 pivot = ImGuiApi.GetMousePos() - m_origin; - Canvas.ZoomAt(EditorPlanarNavigation.WheelFactor(io->MouseWheel), pivot.X, pivot.Y); + Canvas.ZoomAt(EditorPlanarNavigation.WheelFactor(io.wheel), pivot.X, pivot.Y); } - if (hovered && io->WantTextInput == 0 && ImGuiApi.IsKeyPressed(ImGuiKey.F, false)) + + if (hovered && !io.wantsText && ImGuiApi.IsKeyPressed(ImGuiKey.F, false)) Frame(); - if (io->WantTextInput == 0 && ImGuiApi.IsKeyPressed(ImGuiKey.Escape, false)) + if (!io.wantsText && ImGuiApi.IsKeyPressed(ImGuiKey.Escape, false)) { draft.dragging = draft.boxSelecting = false; draft.resizingNode = null; @@ -451,18 +455,15 @@ private void Pointer( bool hovered, Dictionary points ) { - if (draft.navigation.isPanning || ImGuiApi.Raw.GetIO()->KeyAlt != 0) + if (draft.navigation.isPanning || UiDrawing.GetInput().alt) return; Vector2 mouse = ImGuiApi.GetMousePos(); if (draft.resizingNode is GraphNodeId resizing) { - ImGuiApi.SetMouseCursor((int)ImGuiMouseCursor.ResizeNwse); - if (ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Left)) + ImGuiApi.SetMouseCursor(ImGuiMouseCursor.ResizeNwse); + if (ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) { - draft.resizePreviewWidth = Math.Clamp( - draft.resizeStartWidth + (mouse.X - draft.pointerStart.X) / Canvas.zoom, - C_MINIMUM_WIDTH, - C_MAXIMUM_WIDTH); + draft.resizePreviewWidth = Math.Clamp(draft.resizeStartWidth + (mouse.X - draft.pointerStart.X) / Canvas.zoom, C_MINIMUM_WIDTH, C_MAXIMUM_WIDTH); } else { @@ -470,20 +471,21 @@ Dictionary points if (resizedNode is not null && MathF.Abs(NodeWidth(resizedNode, includePreview: false) - draft.resizePreviewWidth) > 0.01f) { GraphDocument resizedGraph = Controller.document.Clone(); - resizedGraph.FindNode(resizing)!.SetValue( - C_NODE_WIDTH, - ShaderGraphDocument.Encode(draft.resizePreviewWidth, owner.serialization, owner.context)); + resizedGraph.FindNode(resizing)!.SetValue(C_NODE_WIDTH, ShaderGraphDocument.Encode(draft.resizePreviewWidth, owner.serialization, owner.context)); Controller.ReplaceDocument(resizedGraph, "Resize Shader Node"); owner.Changed(draft); } + draft.resizingNode = null; } + return; } + GraphNodeRecord? resizeTarget = !draft.readOnly && hovered ? HitResizeHandle(mouse) : null; if (resizeTarget is not null) - ImGuiApi.SetMouseCursor((int)ImGuiMouseCursor.ResizeNwse); - if (hovered && ImGuiApi.IsMouseClicked((int)ImGuiMouseButton.Left)) + ImGuiApi.SetMouseCursor(ImGuiMouseCursor.ResizeNwse); + if (hovered && ImGuiApi.IsMouseClicked(ImGuiMouseButton.Left)) { if (resizeTarget is not null) { @@ -496,12 +498,14 @@ Dictionary points draft.pointerStart = mouse; return; } + PortHandle? hit = HitPort(mouse, points); if (!draft.readOnly && hit is PortHandle handle && !draft.missingPorts.Contains(handle.endpoint)) { Canvas.BeginConnection(handle.endpoint, handle.direction); return; } + GraphNodeRecord? node = HitNode(mouse); ShaderCanvasGroup? group = node is null ? HitGroupHeader(mouse) : null; if (group is ShaderCanvasGroup selectedGroup) @@ -518,13 +522,15 @@ Dictionary points draft.dragging = draft.dragStart.Count != 0; draft.pointerStart = mouse; } + return; } + if (node is not null && mouse.Y <= Rect(node).min.Y + C_HEADER * Canvas.zoom) { draft.selectedGroupId = ""; draft.selectedEdge = null; - if (ImGuiApi.Raw.GetIO()->KeyShift != 0) + if (UiDrawing.GetInput().shift) Canvas.ToggleNode(node.id); else if (!Canvas.selectedNodes.Contains(node.id)) Canvas.SelectNodes([node.id]); @@ -540,7 +546,7 @@ Dictionary points else if (node is null) { draft.selectedGroupId = ""; - if (ImGuiApi.Raw.GetIO()->KeyShift == 0) + if (!UiDrawing.GetInput().shift) Canvas.ClearSelection(); draft.selectedEdge = HitEdge(mouse, points); if (draft.selectedEdge is not null) @@ -549,19 +555,22 @@ Dictionary points draft.pointerStart = mouse; } } - if (draft.dragging && ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Left)) + + if (draft.dragging && ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) { Vector2 delta = (mouse - draft.pointerStart) / Canvas.zoom; - foreach ((GraphNodeId id, GraphPosition position) in draft.dragStart) + foreach ((GraphNodeId id, GraphPosition position)in draft.dragStart) draft.dragPreview[id] = new(position.x + delta.X, position.y + delta.Y); } - if (!ImGuiApi.IsMouseDown((int)ImGuiMouseButton.Left)) + + if (!ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) { if (draft.dragging && draft.dragPreview.Count != 0) { Controller.MoveNodes(draft.dragPreview); owner.Changed(draft); } + if (draft.boxSelecting) { Vector2 min = Vector2.Min(draft.pointerStart, mouse), max = Vector2.Max(draft.pointerStart, mouse); @@ -571,14 +580,12 @@ Dictionary points return rect.max.X >= min.X && rect.min.X <= max.X && rect.max.Y >= min.Y && rect.min.Y <= max.Y; }).Select(static node => node.id)); } - if (Canvas.pendingConnection is GraphEndpoint sourceEndpoint - && Canvas.pendingConnectionDirection is GraphPortDirection sourceDirection) + + if (Canvas.pendingConnection is GraphEndpoint sourceEndpoint && Canvas.pendingConnectionDirection is GraphPortDirection sourceDirection) { var source = new PortHandle(sourceEndpoint, sourceDirection); PortHandle? target = HitPort(mouse, points); - if (target is PortHandle destination - && destination.direction != source.direction - && !draft.missingPorts.Contains(destination.endpoint)) + if (target is PortHandle destination && destination.direction != source.direction && !draft.missingPorts.Contains(destination.endpoint)) { PortHandle output = source.direction == GraphPortDirection.Output ? source : destination; PortHandle input = source.direction == GraphPortDirection.Input ? source : destination; @@ -590,22 +597,23 @@ Dictionary points owner.Changed(draft); } } - else if (source.direction == GraphPortDirection.Output - && target is null && hovered && HitNode(mouse) is null) + else if (source.direction == GraphPortDirection.Output && target is null && hovered && HitNode(mouse) is null) { draft.createFromPort = source.endpoint; draft.menuPosition = ToGraph(mouse); ImGuiApi.OpenPopup("##shader-menu"); } + Canvas.CancelConnection(); } + draft.dragging = draft.boxSelecting = false; draft.dragPreview.Clear(); } } - private unsafe void Node( - ImDrawList* draw, + private void Node( + EditorDrawList draw, GraphNodeRecord node, Dictionary points ) { @@ -614,54 +622,15 @@ Dictionary points return; float zoom = Canvas.zoom; bool selected = Canvas.selectedNodes.Contains(node.id); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(rect.min + new Vector2(3, 5)), - Unsafe.BitCast(rect.max + new Vector2(3, 5)), - Color(0, 0, 0, EditorPalette.opacityMuted), - 6 * zoom, - 0 - ); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(rect.min), - Unsafe.BitCast(rect.max), - Color(0.115f, 0.12f, 0.14f), - 6 * zoom, - 0 - ); + draw.FilledRectangle(rect.min + new Vector2(3, 5), rect.max + new Vector2(3, 5), Color(0, 0, 0, EditorPalette.opacityMuted), 6 * zoom, false); + draw.FilledRectangle(rect.min, rect.max, Color(0.115f, 0.12f, 0.14f), 6 * zoom, false); Vector4 headerColor = NodeHeaderColor(node); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(rect.min), - Unsafe.BitCast(new(rect.max.X, rect.min.Y + C_HEADER * zoom)), - ImGuiApi.ColorConvertFloat4ToU32(headerColor), - 6 * zoom, - (int)ImDrawFlags.RoundCornersTop - ); - DrawNativeRectangle( - draw, - Unsafe.BitCast(rect.min), - Unsafe.BitCast(rect.max), - selected ? Color(0.65f, 0.47f, 0.88f) : Color(0.26f, 0.27f, 0.31f), - 6 * zoom, - (int)ImDrawFlags.None, - selected ? 2 : 1 - ); - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(Title(node), allowsNull: false)) + draw.FilledRectangle(rect.min, new(rect.max.X, rect.min.Y + C_HEADER * zoom), ImGuiApi.ToPackedColor(headerColor), 6 * zoom, true); + draw.Rectangle(rect.min, rect.max, selected ? Color(0.65f, 0.47f, 0.88f) : Color(0.26f, 0.27f, 0.31f), 6 * zoom, selected ? 2 : 1); { - DrawNativeText( - draw, - ImGuiApi.Raw.GetFont(), - ImGuiApi.GetFontSize() * zoom, - Unsafe.BitCast(rect.min + new Vector2(10, 7) * zoom), - ImGuiApi.GetColorU32((int)ImGuiCol.Text), - (sbyte*)nativeText.pointer, - null, - 0f, - null - ); + draw.Text(rect.min + new Vector2(10, 7) * zoom, ImGuiApi.GetStyleColor(ImGuiCol.Text), Title(node), (ImGuiApi.GetFontSize() * zoom) / ImGuiApi.GetFontSize()); } + ShaderNodePort[] nodePorts = draft.ports[node.id]; bool splitPorts = UsesSplitPortLayout(nodePorts); float divider = (rect.min.X + rect.max.X) * 0.5f; @@ -669,14 +638,9 @@ Dictionary points if (splitPorts) { float bodyPadding = 7f * zoom; - DrawNativeLine( - draw, - Unsafe.BitCast(new Vector2(divider, bodyTop + bodyPadding)), - Unsafe.BitCast(new Vector2(divider, rect.max.Y - bodyPadding)), - Color(0.62f, 0.63f, 0.69f, EditorPalette.opacityFaint), - 1f - ); + draw.Line(new Vector2(divider, bodyTop + bodyPadding), new Vector2(divider, rect.max.Y - bodyPadding), Color(0.62f, 0.63f, 0.69f, EditorPalette.opacityFaint), 1f); } + foreach (ShaderNodePort port in nodePorts) { var handle = new PortHandle(new(node.id, new(port.id)), port.direction); @@ -685,133 +649,68 @@ Dictionary points bool optional = port.direction == GraphPortDirection.Input && !port.required; uint portColor = missing ? Color(0.95f, 0.35f, 0.3f) : Color(0.60f, 0.48f, 0.84f); if (missing || !optional) - DrawNativeFilledCircle(draw, Unsafe.BitCast(point), 5 * zoom, portColor, 0); + draw.FilledCircle(point, 5 * zoom, portColor); else { - DrawNativeFilledCircle(draw, Unsafe.BitCast(point), 5 * zoom, portColor, 0); - DrawNativeFilledCircle( - draw, - Unsafe.BitCast(point), - 2.75f * zoom, - Color(0.115f, 0.12f, 0.14f), - 0 - ); + draw.FilledCircle(point, 5 * zoom, portColor); + draw.FilledCircle(point, 2.75f * zoom, Color(0.115f, 0.12f, 0.14f)); } + float halfPadding = 12f * zoom; - float portAreaWidth = splitPorts - ? (rect.max.X - rect.min.X) * 0.5f - : rect.max.X - rect.min.X; + float portAreaWidth = splitPorts ? (rect.max.X - rect.min.X) * 0.5f : rect.max.X - rect.min.X; float availableWidth = MathF.Max(1f, portAreaWidth - halfPadding * 2f); string label = FitPortLabel(port.id, availableWidth, zoom); - float labelWidth = ImGuiApi.CalcTextSize(label).X * zoom; + float labelWidth = ImGuiApi.MeasureText(label).X * zoom; bool input = port.direction == GraphPortDirection.Input; float x = input ? rect.min.X + halfPadding : rect.max.X - labelWidth - halfPadding; float clipLeft = splitPorts && !input ? divider + 1f : rect.min.X + 1f; float clipRight = splitPorts && input ? divider - 1f : rect.max.X - 1f; Vector2 clipMin = new(clipLeft, bodyTop); Vector2 clipMax = new(clipRight, rect.max.Y); - PushNativeClipRectangle( - draw, - Unsafe.BitCast(clipMin), - Unsafe.BitCast(clipMax), - (true ? (byte)1 : (byte)0) - ); - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(label, allowsNull: false)) + draw.PushClip(clipMin, clipMax, true); { - DrawNativeText( - draw, - ImGuiApi.Raw.GetFont(), - ImGuiApi.GetFontSize() * zoom, - Unsafe.BitCast(new(x, point.Y - 8 * zoom)), - ImGuiApi.ColorConvertFloat4ToU32(optional ? EditorPalette.textDisabled : EditorPalette.text), - (sbyte*)nativeText.pointer, - null, - 0f, - null + draw.Text( + new(x, point.Y - 8 * zoom), + ImGuiApi.ToPackedColor(optional ? EditorPalette.textDisabled : EditorPalette.text), + label, + (ImGuiApi.GetFontSize() * zoom) / ImGuiApi.GetFontSize() ); } - ImGuiApi.Raw.PopClipRect(draw); + + draw.PopClip(); if (Vector2.DistanceSquared(point, ImGuiApi.GetMousePos()) <= 64) - Widget.DrawTooltip(port.type.id + (missing - ? " · missing port; reconnect explicitly" - : optional ? " · optional input" : port.direction == GraphPortDirection.Input ? " · required input" : "")); + EditorUI.current.widgets.DrawTooltip(port.type.id + (missing ? " · missing port; reconnect explicitly" : optional ? " · optional input" : port.direction == GraphPortDirection.Input ? " · required input" : "")); } + if (!draft.readOnly) { bool resizeHovered = ContainsResizeHandle(rect, ImGuiApi.GetMousePos()); - uint gripColor = Color( - 0.62f, - 0.63f, - 0.69f, - resizeHovered || draft.resizingNode == node.id - ? EditorPalette.opacityEmphasized - : EditorPalette.opacityMedium); + uint gripColor = Color(0.62f, 0.63f, 0.69f, resizeHovered || draft.resizingNode == node.id ? EditorPalette.opacityEmphasized : EditorPalette.opacityMedium); const float inset = 4f; - DrawNativeLine( - draw, - Unsafe.BitCast(rect.max - new Vector2(C_RESIZE_HANDLE, inset) * zoom), - Unsafe.BitCast(rect.max - new Vector2(inset, C_RESIZE_HANDLE) * zoom), - gripColor, - 1f - ); - DrawNativeLine( - draw, - Unsafe.BitCast(rect.max - new Vector2(C_RESIZE_HANDLE * 0.62f, inset) * zoom), - Unsafe.BitCast(rect.max - new Vector2(inset, C_RESIZE_HANDLE * 0.62f) * zoom), - gripColor, - 1f - ); + draw.Line(rect.max - new Vector2(C_RESIZE_HANDLE, inset) * zoom, rect.max - new Vector2(inset, C_RESIZE_HANDLE) * zoom, gripColor, 1f); + draw.Line(rect.max - new Vector2(C_RESIZE_HANDLE * 0.62f, inset) * zoom, rect.max - new Vector2(inset, C_RESIZE_HANDLE * 0.62f) * zoom, gripColor, 1f); } + if (draft.ports[node.id].Length == 0 && node.definitionId == ShaderGraphNodes.inputDefinitionId) - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated("No caller inputs", allowsNull: false)) - { - DrawNativeText( - draw, - ImGuiApi.Raw.GetFont(), - ImGuiApi.GetFontSize() * zoom, - Unsafe.BitCast(rect.min + new Vector2(12, C_HEADER + 8) * zoom), - ImGuiApi.GetColorU32((int)ImGuiCol.TextDisabled), - (sbyte*)nativeText.pointer, - null, - 0f, - null - ); - } - if (draft.nodeErrors.TryGetValue(node.id, out string? error)) { - DrawNativeFilledCircle( - draw, - Unsafe.BitCast(new(rect.max.X - 14 * zoom, rect.min.Y + 16 * zoom)), - 4 * zoom, - Color(1, 0.4f, 0.35f), - 0 + draw.Text( + rect.min + new Vector2(12, C_HEADER + 8) * zoom, + ImGuiApi.GetStyleColor(ImGuiCol.TextDisabled), + "No caller inputs", + (ImGuiApi.GetFontSize() * zoom) / ImGuiApi.GetFontSize() ); + } + + if (draft.nodeErrors.TryGetValue(node.id, out string? error)) + { + draw.FilledCircle(new(rect.max.X - 14 * zoom, rect.min.Y + 16 * zoom), 4 * zoom, Color(1, 0.4f, 0.35f)); if (Contains(rect, ImGuiApi.GetMousePos())) - Widget.DrawTooltip(error); + EditorUI.current.widgets.DrawTooltip(error); } } - private void Grid(ImDrawList* draw) - { - float spacing = 32 * Canvas.zoom; - for (float x = ((Canvas.pan.x % spacing) + spacing) % spacing; x < m_size.X; x += spacing) - DrawNativeLine( - draw, - Unsafe.BitCast(m_origin + new Vector2(x, 0)), - Unsafe.BitCast(m_origin + new Vector2(x, m_size.Y)), - Color(0.105f, 0.115f, 0.135f), - 1f - ); - for (float y = ((Canvas.pan.y % spacing) + spacing) % spacing; y < m_size.Y; y += spacing) - DrawNativeLine( - draw, - Unsafe.BitCast(m_origin + new Vector2(0, y)), - Unsafe.BitCast(m_origin + new Vector2(m_size.X, y)), - Color(0.105f, 0.115f, 0.135f), - 1f - ); - } - private unsafe void Groups(ImDrawList* draw) + private void Grid(EditorDrawList draw) => View.DrawGrid(Color(0.105f, 0.115f, 0.135f)); + private void Groups(EditorDrawList draw) { foreach (ShaderCanvasGroup group in draft.groups) { @@ -821,70 +720,41 @@ private unsafe void Groups(ImDrawList* draw) bool selected = draft.selectedGroupId == group.id; Vector4 headerColor = GroupHeaderColor(group); Vector4 bodyColor = EditorPalette.WithOpacity(headerColor, EditorPalette.opacitySoft); - Vector4 borderColor = selected - ? new(0.65f, 0.47f, 0.88f, EditorPalette.opacityOpaque) - : new( - MathF.Min(1f, headerColor.X * 1.22f), - MathF.Min(1f, headerColor.Y * 1.22f), - MathF.Min(1f, headerColor.Z * 1.22f), - EditorPalette.opacityEmphasized); - float headerHeight = C_HEADER * Canvas.zoom; - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(min), - Unsafe.BitCast(max), - ImGuiApi.ColorConvertFloat4ToU32(bodyColor), - 8 * Canvas.zoom, - 0 - ); - DrawNativeFilledRectangle( - draw, - Unsafe.BitCast(min), - Unsafe.BitCast(new(max.X, min.Y + headerHeight)), - ImGuiApi.ColorConvertFloat4ToU32(headerColor), - 8 * Canvas.zoom, - (int)ImDrawFlags.RoundCornersTop - ); - DrawNativeLine( - draw, - Unsafe.BitCast(new(min.X, min.Y + headerHeight)), - Unsafe.BitCast(new(max.X, min.Y + headerHeight)), - ImGuiApi.ColorConvertFloat4ToU32(borderColor), - 1f + Vector4 borderColor = selected ? new( + 0.65f, + 0.47f, + 0.88f, + EditorPalette.opacityOpaque + ) : new( + MathF.Min(1f, headerColor.X * 1.22f), + MathF.Min(1f, headerColor.Y * 1.22f), + MathF.Min(1f, headerColor.Z * 1.22f), + EditorPalette.opacityEmphasized ); - DrawNativeRectangle( - draw, - Unsafe.BitCast(min), - Unsafe.BitCast(max), - ImGuiApi.ColorConvertFloat4ToU32(borderColor), - 8 * Canvas.zoom, - (int)ImDrawFlags.None, - selected ? 2f : 1f - ); - using (NativeStringLease nativeText = Utf8Codec.EncodeNullTerminated(group.title, allowsNull: false)) + float headerHeight = C_HEADER * Canvas.zoom; + draw.FilledRectangle(min, max, ImGuiApi.ToPackedColor(bodyColor), 8 * Canvas.zoom, false); + draw.FilledRectangle(min, new(max.X, min.Y + headerHeight), ImGuiApi.ToPackedColor(headerColor), 8 * Canvas.zoom, true); + draw.Line(new(min.X, min.Y + headerHeight), new(max.X, min.Y + headerHeight), ImGuiApi.ToPackedColor(borderColor), 1f); + draw.Rectangle(min, max, ImGuiApi.ToPackedColor(borderColor), 8 * Canvas.zoom, selected ? 2f : 1f); { - DrawNativeText( - draw, - ImGuiApi.Raw.GetFont(), - ImGuiApi.GetFontSize() * Canvas.zoom, - Unsafe.BitCast(min + new Vector2(12, 7) * Canvas.zoom), - ImGuiApi.GetColorU32((int)ImGuiCol.Text), - (sbyte*)nativeText.pointer, - null, - 0f, - null + draw.Text( + min + new Vector2(12, 7) * Canvas.zoom, + ImGuiApi.GetStyleColor(ImGuiCol.Text), + group.title, + (ImGuiApi.GetFontSize() * Canvas.zoom) / ImGuiApi.GetFontSize() ); } } } + private ShaderCanvasGroup? HitGroupHeader(Vector2 point) { foreach (ShaderCanvasGroup group in draft.groups.Reverse()) - if (GroupRect(group) is { } bounds && point.X >= bounds.min.X && point.X <= bounds.max.X - && point.Y >= bounds.min.Y && point.Y <= bounds.min.Y + 32f * Canvas.zoom) + if (GroupRect(group) is { } bounds && point.X >= bounds.min.X && point.X <= bounds.max.X && point.Y >= bounds.min.Y && point.Y <= bounds.min.Y + 32f * Canvas.zoom) return group; return null; } + private (Vector2 min, Vector2 max)? GroupRect(ShaderCanvasGroup group) { Vector2 min = new(float.MaxValue), max = new(float.MinValue); @@ -895,10 +765,12 @@ private unsafe void Groups(ImDrawList* draw) min = Vector2.Min(min, rect.min); max = Vector2.Max(max, rect.max); } + if (min.X == float.MaxValue) return null; return (min - new Vector2(20, 40) * Canvas.zoom, max + new Vector2(20) * Canvas.zoom); } + private Dictionary PortPositions() { var points = new Dictionary(); @@ -910,96 +782,85 @@ private Dictionary PortPositions() { int row = port.direction == GraphPortDirection.Input ? left++ : right++; var handle = new PortHandle(new(node.id, new(port.id)), port.direction); - points[handle] = new(port.direction == GraphPortDirection.Input ? rect.min.X : rect.max.X, - rect.min.Y + (C_HEADER + C_ROW * (row + 0.5f)) * Canvas.zoom); + points[handle] = new(port.direction == GraphPortDirection.Input ? rect.min.X : rect.max.X, rect.min.Y + (C_HEADER + C_ROW * (row + 0.5f)) * Canvas.zoom); } } + return points; } + private void Edges( - ImDrawList* draw, + EditorDrawList draw, Dictionary points ) { foreach (GraphEdgeRecord edge in Controller.document.edges) - if (points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) - && points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) + if (points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) && points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) Curve(a, b, edge.id == draft.selectedEdge); if (Canvas.pendingConnection is GraphEndpoint pending && Canvas.pendingConnectionDirection is GraphPortDirection direction - && points.TryGetValue(new(pending, direction), out Vector2 start)) + && points.TryGetValue( + new(pending, direction), + out Vector2 start + )) { if (direction == GraphPortDirection.Output) Curve(start, ImGuiApi.GetMousePos()); else Curve(ImGuiApi.GetMousePos(), start); } + void Curve( Vector2 a, Vector2 b, bool selected = false ) { - float tangent = MathF.Max(36, MathF.Abs(b.X - a.X) * 0.45f); - DrawNativeCurve( - draw, - new ImVec2C { X = a.X, Y = a.Y }, - new ImVec2C { X = a.X + tangent, Y = a.Y }, - new ImVec2C { X = b.X - tangent, Y = b.Y }, - new ImVec2C { X = b.X, Y = b.Y }, - selected ? Color(0.87f, 0.72f, 1f) : Color(0.59f, 0.46f, 0.82f), - selected ? 3f : 2f, - 0); + View.DrawConnection(a, b, selected ? Color(0.87f, 0.72f, 1f) : Color(0.59f, 0.46f, 0.82f), selected ? 3f : 2f); } } + private GraphEdgeId? HitEdge( Vector2 mouse, Dictionary points ) { foreach (GraphEdgeRecord edge in Controller.document.edges) { - if (!points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) - || !points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) - continue; - float tangent = MathF.Max(36, MathF.Abs(b.X - a.X) * 0.45f); - Vector2 c = a + new Vector2(tangent, 0), d = b - new Vector2(tangent, 0), previous = a; - for (int segment = 1; segment <= 32; segment++) - { - float t = segment / 32f, s = 1 - t; - Vector2 next = s * s * s * a + 3 * s * s * t * c + 3 * s * t * t * d + t * t * t * b; - Vector2 line = next - previous; - float projection = Math.Clamp(Vector2.Dot(mouse - previous, line) / MathF.Max(0.001f, line.LengthSquared()), 0, 1); - if (Vector2.DistanceSquared(mouse, previous + projection * line) < 36) - return edge.id; - previous = next; - } + if (!points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) || !points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) + continue; + if (GraphCanvasView.HitConnection(mouse, a, b)) + return edge.id; } + return null; } + private (Vector2 min, Vector2 max) Rect(GraphNodeRecord node) { GraphPosition position = draft.dragPreview.GetValueOrDefault(node.id, node.position); - Vector2 min = m_origin + new Vector2(Canvas.pan.x, Canvas.pan.y) + new Vector2(position.x, position.y) * Canvas.zoom; + Vector2 min = View.ToScreen(position); return (min, min + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20) * Canvas.zoom); } + private static float ControlHeight(GraphNodeRecord node) => 0f; private int Rows(GraphNodeRecord node) { int rows = Math.Max( draft.ports[node.id].Count(static port => port.direction == GraphPortDirection.Input), - draft.ports[node.id].Count(static port => port.direction == GraphPortDirection.Output)); + draft.ports[node.id].Count(static port => port.direction == GraphPortDirection.Output) + ); return rows == 0 && node.definitionId is ShaderGraphNodes.inputDefinitionId or ShaderGraphNodes.outputDefinitionId ? 1 : rows; } - private static bool UsesSplitPortLayout(IEnumerable ports) - => ports.Any(static port => port.direction == GraphPortDirection.Input) - && ports.Any(static port => port.direction == GraphPortDirection.Output); + + private static bool UsesSplitPortLayout(IEnumerable ports) => ports.Any(static port => port.direction == GraphPortDirection.Input) + && ports.Any(static port => port.direction == GraphPortDirection.Output); private GraphNodeRecord? HitNode(Vector2 point) => Controller.document.nodes.Reverse().FirstOrDefault(node => Contains(Rect(node), point)); private PortHandle? HitPort( Vector2 point, Dictionary ports ) { - foreach ((PortHandle handle, Vector2 position) in ports) + foreach ((PortHandle handle, Vector2 position)in ports) if (Vector2.DistanceSquared(point, position) <= MathF.Pow(MathF.Max(8, 7 * Canvas.zoom), 2)) return handle; - foreach ((PortHandle handle, Vector2 position) in ports) + foreach ((PortHandle handle, Vector2 position)in ports) { GraphNodeRecord node = Controller.document.FindNode(handle.endpoint.nodeId)!; (Vector2 min, Vector2 max) rect = Rect(node); @@ -1012,8 +873,10 @@ Dictionary ports if (point.X >= minimumX && point.X <= maximumX && point.Y >= position.Y - halfRow && point.Y <= position.Y + halfRow) return handle; } + return null; } + private GraphNodeRecord? HitResizeHandle(Vector2 point) { foreach (GraphNodeRecord node in Controller.document.nodes.Reverse()) @@ -1022,16 +885,18 @@ Dictionary ports if (ContainsResizeHandle(rect, point)) return node; } + return null; } + private bool ContainsResizeHandle( (Vector2 min, Vector2 max) rect, Vector2 point ) { float extent = MathF.Max(10f, C_RESIZE_HANDLE * Canvas.zoom); - return point.X >= rect.max.X - extent && point.X <= rect.max.X + 2f && - point.Y >= rect.max.Y - extent && point.Y <= rect.max.Y + 2f; + return point.X >= rect.max.X - extent && point.X <= rect.max.X + 2f && point.Y >= rect.max.Y - extent && point.Y <= rect.max.Y + 2f; } + private float NodeWidth( GraphNodeRecord node, bool includePreview = true @@ -1041,15 +906,16 @@ private float NodeWidth( float width = Read(node, C_NODE_WIDTH, C_DEFAULT_WIDTH); return float.IsFinite(width) ? Math.Clamp(width, C_MINIMUM_WIDTH, C_MAXIMUM_WIDTH) : C_DEFAULT_WIDTH; } + private static string FitPortLabel( string label, float availableWidth, float zoom ) { - if (ImGuiApi.CalcTextSize(label).X * zoom <= availableWidth) + if (ImGuiApi.MeasureText(label).X * zoom <= availableWidth) return label; const string ellipsis = "…"; - float ellipsisWidth = ImGuiApi.CalcTextSize(ellipsis).X * zoom; + float ellipsisWidth = ImGuiApi.MeasureText(ellipsis).X * zoom; if (ellipsisWidth >= availableWidth) return string.Empty; int low = 0; @@ -1057,18 +923,18 @@ float zoom while (low < high) { int length = (low + high + 1) / 2; - if (ImGuiApi.CalcTextSize(label[..length]).X * zoom + ellipsisWidth <= availableWidth) + if (ImGuiApi.MeasureText(label[..length]).X * zoom + ellipsisWidth <= availableWidth) low = length; else high = length - 1; } + return label[..low] + ellipsis; } - private ShaderNodePort Port(PortHandle handle) - => draft.ports[handle.endpoint.nodeId].Single(port => - port.id == handle.endpoint.portId.value && port.direction == handle.direction); + + private ShaderNodePort Port(PortHandle handle) => draft.ports[handle.endpoint.nodeId].Single(port => port.id == handle.endpoint.portId.value && port.direction == handle.direction); private ShaderNodePort Port(GraphEndpoint endpoint) => draft.ports[endpoint.nodeId].Single(port => port.id == endpoint.portId.value); - private GraphPosition ToGraph(Vector2 point) => new((point.X - m_origin.X - Canvas.pan.x) / Canvas.zoom, (point.Y - m_origin.Y - Canvas.pan.y) / Canvas.zoom); + private GraphPosition ToGraph(Vector2 point) => View.ToGraph(point); private static bool Contains( (Vector2 min, Vector2 max) rect, Vector2 point @@ -1078,9 +944,7 @@ private static uint Color( float g, float b, float a = EditorPalette.opacityOpaque - ) - => ImGuiApi.ColorConvertFloat4ToU32(new(r, g, b, a)); - + ) => ImGuiApi.ToPackedColor(new(r, g, b, a)); private Vector4 NodeHeaderColor(GraphNodeRecord node) { string key = node.definitionId + "/" + Title(node); @@ -1088,18 +952,14 @@ private Vector4 NodeHeaderColor(GraphNodeRecord node) { ShaderGraphDocument.outputDefinitionId => StableHeaderColor(key, 258f, 34f), ShaderGraphNodes.outputDefinitionId => StableHeaderColor(key, 258f, 34f), - "inno.shader.stage-input" or "inno.shader.constant" or ShaderGraphNodes.inputDefinitionId - => StableHeaderColor(key, 22f, 24f), + "inno.shader.stage-input" or "inno.shader.constant" or ShaderGraphNodes.inputDefinitionId => StableHeaderColor(key, 22f, 24f), "inno.shader.sample" => StableHeaderColor(key, 158f, 30f), "inno.shader.source" => StableHeaderColor(key, 198f, 42f), "inno.shader.binary" => StableHeaderColor(key, 205f, 45f), - "inno.shader.construct" or "inno.shader.extract" or "inno.shader.select" or "inno.shader.reroute" - => StableHeaderColor(key, 178f, 58f), - "inno.shader.storage-load" or "inno.shader.storage-store" or "inno.shader.storage-atomic-add" - => StableHeaderColor(key, 105f, 42f), + "inno.shader.construct" or "inno.shader.extract" or "inno.shader.select" or "inno.shader.reroute" => StableHeaderColor(key, 178f, 58f), + "inno.shader.storage-load" or "inno.shader.storage-store" or "inno.shader.storage-atomic-add" => StableHeaderColor(key, 105f, 42f), "inno.shader.discard" => StableHeaderColor(key, 350f, 22f), - _ => StableHeaderColor(key, 0f, 360f) - }; + _ => StableHeaderColor(key, 0f, 360f)}; } private Vector4 GroupHeaderColor(ShaderCanvasGroup group) @@ -1113,6 +973,7 @@ private Vector4 GroupHeaderColor(ShaderCanvasGroup group) total += NodeHeaderColor(node); count++; } + if (count == 0) return EditorPalette.shaderNodeHeader; Vector4 average = total / count; @@ -1130,6 +991,7 @@ float hueRange hash ^= value; hash *= 16777619; } + float hue = (startHue + hueRange * (hash & 0xffff) / 65535f) % 360f; float saturation = 0.52f + 0.12f * ((hash >> 16) & 0xff) / 255f; float brightness = 0.34f + 0.08f * ((hash >> 24) & 0xff) / 255f; @@ -1143,13 +1005,12 @@ float hueRange < 3f => (0f, chroma, secondary), < 4f => (0f, secondary, chroma), < 5f => (secondary, 0f, chroma), - _ => (chroma, 0f, secondary) - }; + _ => (chroma, 0f, secondary)}; float match = brightness - chroma; return new(red + match, green + match, blue + match, EditorPalette.opacityOpaque); } - private static bool CenteredAddButton(string label) => Widget.CenteredButton(label, Widget.style.inspectorAddButtonTopPadding); + private static bool CenteredAddButton(string label) => EditorUI.current.widgets.CenteredButton(label, EditorUI.current.widgets.style.inspectorAddButtonTopPadding); private T Read( GraphNodeRecord node, string key, @@ -1160,37 +1021,49 @@ private void Set( string key, T value, bool continuous = false - ) - => SetEncoded(node, key, ShaderGraphDocument.Encode(value, owner.serialization, owner.context), continuous); + ) => SetEncoded(node, key, ShaderGraphDocument.Encode(value, owner.serialization, owner.context), continuous ? draft.valueGesture : null); private void SetEncoded( GraphNodeRecord node, string key, GraphSerializedValue value, - bool continuous + string? mergeKey ) { - if (ImGuiApi.IsItemActivated()) - draft.valueGesture = Guid.NewGuid().ToString("N"); if (m_inspectionNodes is { Length: > 1 } selected) { GraphDocument candidate = Controller.document.Clone(); foreach (GraphNodeRecord target in selected) { if (key == "settings" && target.definitionId == "inno.shader.stage-input") - candidate = ShaderGraphBindings.ChangeInput(candidate, target.id, - ShaderGraphDocument.Decode(value, owner.serialization, owner.context), owner.serialization, owner.context); + candidate = ShaderGraphBindings.ChangeInput( + candidate, + target.id, + ShaderGraphDocument.Decode(value, owner.serialization, owner.context), + owner.serialization, + owner.context + ); else candidate.FindNode(target.id)!.SetValue(key, value.Clone()); } - Controller.ReplaceDocument(candidate, "Edit Shader Nodes", continuous ? draft.valueGesture : null); + + Controller.ReplaceDocument(candidate, "Edit Shader Nodes", mergeKey); } else if (key == "settings" && node.definitionId == "inno.shader.stage-input") - Controller.ReplaceDocument(ShaderGraphBindings.ChangeInput(Controller.document, node.id, - ShaderGraphDocument.Decode(value, owner.serialization, owner.context), owner.serialization, owner.context), - "Edit Shader Input", continuous ? draft.valueGesture : null); + Controller.ReplaceDocument( + ShaderGraphBindings.ChangeInput( + Controller.document, + node.id, + ShaderGraphDocument.Decode(value, owner.serialization, owner.context), + owner.serialization, + owner.context + ), + "Edit Shader Input", + mergeKey + ); else - Controller.SetNodeValue(node.id, key, value, continuous ? draft.valueGesture : null); + Controller.SetNodeValue(node.id, key, value, mergeKey); owner.Changed(draft); } + private void SetStage(GraphNodeRecord? node) { if (node is null) @@ -1199,6 +1072,7 @@ private void SetStage(GraphNodeRecord? node) if (stage.Length != 0) draft.activeStage = new(stage); } + private void Frame() { GraphNodeRecord[] selection = Controller.document.nodes.Where(node => Canvas.selectedNodes.Count == 0 || Canvas.selectedNodes.Contains(node.id)).ToArray(); @@ -1210,6 +1084,7 @@ private void Frame() float scale = Math.Clamp(MathF.Min(MathF.Max(1, m_size.X - 80) / MathF.Max(1, maxX - minX), MathF.Max(1, m_size.Y - 80) / MathF.Max(1, maxY - minY)), 0.1f, 1f); Canvas.SetViewport(new(m_size.X / 2 - (minX + maxX) / 2 * scale, m_size.Y / 2 - (minY + maxY) / 2 * scale), scale); } + internal string Title(GraphNodeRecord node) { if (owner.drawers?.GetDisplayName(node.definitionId) is { } displayName) @@ -1217,25 +1092,29 @@ internal string Title(GraphNodeRecord node) if (node.definitionId == ShaderGraphDocument.outputDefinitionId) return Read(node, "settings", new ShaderGraphStageSettings()).stage + " Output"; if (node.definitionId == "inno.shader.binary") - return Widget.NicifyName(Read(node, "operation", "add").Replace('-', ' ')); + return EditorDisplayNames.Nicify(Read(node, "operation", "add").Replace('-', ' ')); if (node.definitionId == "inno.shader.stage-input") { ShaderGraphInputSettings input = Read(node, "settings", new ShaderGraphInputSettings()); - return input.id.Length != 0 ? Widget.NicifyName(input.id) : "Stage Input"; + return input.id.Length != 0 ? EditorDisplayNames.Nicify(input.id) : "Stage Input"; } + if (node.definitionId == "inno.shader.source") { string function = Read(node, "function", ""); return function.Length == 0 ? "Source Function" : function; } + if (node.definitionId == ShaderGraphNodes.callDefinitionId) return Read(node, ShaderGraphNodes.interfaceKey, new ShaderGraphNodeInterface()).displayName; if (node.definitionId == ShaderGraphNodes.inputDefinitionId) - return ShaderGraphNodes.ReadSettings(Controller.document, owner.serialization, owner.context).kind == ShaderGraphNodeKind.DomainOutput - ? "Domain Inputs" - : "Function Inputs"; + return ShaderGraphNodes.ReadSettings( + Controller.document, + owner.serialization, + owner.context + ).kind == ShaderGraphNodeKind.DomainOutput ? "Domain Inputs" : "Function Inputs"; if (node.definitionId == ShaderGraphNodes.outputDefinitionId) return "Function Outputs"; - return Widget.NicifyName(node.definitionId.Replace("inno.shader.", "", StringComparison.Ordinal).Replace('-', ' ')); + return EditorDisplayNames.Nicify(node.definitionId.Replace("inno.shader.", "", StringComparison.Ordinal).Replace('-', ' ')); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs index 88b5e2fa8..25b2ae366 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs @@ -14,7 +14,9 @@ using Inno.Editor.Interactions; using Inno.Editor.Rendering; using Inno.Editor.Shaders; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Shaders.ImGui; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Rendering; using Inno.Extensibility.Types; using Inno.Rendering.Assets; @@ -22,7 +24,6 @@ using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; - [EditorModule("rendering.shader-documents", order: 170)] internal sealed partial class ShaderEditorDocuments : EditorModule { @@ -49,7 +50,6 @@ internal sealed partial class ShaderEditorDocuments : EditorModule internal readonly SerializationRegistry serialization; internal readonly EditorInteractions interactions; internal ShaderClipboardData? clipboard; - internal ShaderEditorDocuments( AssetPipeline assets, SerializationRegistry serialization, @@ -79,9 +79,7 @@ IEditorPreviewService previews internal EditorShaderDraftCompilationSnapshot? Preview(Draft draft) { GraphDocumentController controller = Controller(draft); - return draft.checkedRevision == controller.revision && draft.checkedState is not null - ? draft.preview - : null; + return draft.checkedRevision == controller.revision && draft.checkedState is not null ? draft.preview : null; } internal EditorShaderDraftCompilationSnapshot Check(Draft draft) @@ -98,21 +96,22 @@ internal EditorShaderDraftCompilationSnapshot Check(Draft draft) catch (Exception failure) when ((failure is InvalidOperationException or ArgumentException or FormatException or NotSupportedException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { - nodeSnapshot = new(EditorShaderCompilationState.Failed, false, - [new ShaderDiagnostic("SHADER_GRAPH_NODE_INTERFACE", DiagnosticSeverity.Error, failure.Message)], null); + nodeSnapshot = new( + EditorShaderCompilationState.Failed, + false, + [new ShaderDiagnostic("SHADER_GRAPH_NODE_INTERFACE", DiagnosticSeverity.Error, failure.Message)], + null + ); } + draft.checkedRevision = controller.revision; draft.checkedState = nodeSnapshot.state; return draft.preview = nodeSnapshot; } - EditorShaderDraftCompilationSnapshot snapshot = m_compilation.RequestDraft( - draft.id, - controller.document, - controller.revision, - RenderShaderVariant.empty); + + EditorShaderDraftCompilationSnapshot snapshot = m_compilation.RequestDraft(draft.id, controller.document, controller.revision, RenderShaderVariant.empty); if (snapshot.state == EditorShaderCompilationState.Compiling) return snapshot; - draft.checkedRevision = controller.revision; draft.checkedState = snapshot.state; if (snapshot.state == EditorShaderCompilationState.Failed) @@ -122,11 +121,11 @@ internal EditorShaderDraftCompilationSnapshot Check(Draft draft) m_compilation.ReleaseDraft(draft.id); snapshot = new(EditorShaderCompilationState.Failed, false, snapshot.diagnostics, null); } + return draft.preview = snapshot; } internal void ShowCheck(Draft draft) => m_checkDraftId = draft.id; - internal bool TryGetCheckDraft(out Draft draft) { draft = null!; @@ -134,7 +133,6 @@ internal bool TryGetCheckDraft(out Draft draft) } internal void CloseCheck() => m_checkDraftId = null; - internal void ReleasePreview(Draft draft) { m_compilation.ReleaseDraft(draft.id); @@ -145,20 +143,12 @@ internal void ReleasePreview(Draft draft) internal void PublishNodeDiagnostics(Draft draft) { - Diagnostic[] diagnostics = draft.nodeErrors.Select(pair => new Diagnostic( - "SHADER_NODE_INVALID", - $"Node '{pair.Key.value}' cannot be evaluated: {pair.Value}", - DiagnosticSeverity.Error, - semanticId: pair.Key.value, - objectId: draft.id, - location: new DiagnosticLocation(draft.path.ToString()))).ToArray(); + Diagnostic[] diagnostics = draft.nodeErrors.Select(pair => new Diagnostic("SHADER_NODE_INVALID", $"Node '{pair.Key.value}' cannot be evaluated: {pair.Value}", DiagnosticSeverity.Error, semanticId: pair.Key.value, objectId: draft.id, location: new DiagnosticLocation(draft.path.ToString()))).ToArray(); Diagnostics.Set(draft.id, C_NODE_DIAGNOSTIC_GROUP, diagnostics, draft.path.ToString()); } private static void ClearNodeDiagnostics(Draft draft) => Diagnostics.Clear(draft.id, C_NODE_DIAGNOSTIC_GROUP); - internal Draft Open(AssetFileEntry entry) => Open(entry, AssetId(entry)); - private Draft Open( AssetFileEntry entry, Guid id @@ -180,14 +170,16 @@ out Draft draft return true; } - internal GraphDocumentController Controller(Draft draft) - => m_graphs.TryOpenDocument(draft.id, interactions.history, out GraphDocumentController? controller) - ? controller! : throw new InvalidOperationException("The Shader graph document is not available."); - - internal Guid AssetId(AssetFileEntry entry) - => assets.TryGetInfo(entry.assetPath, out AssetInfo? info) && info is not null ? info.persistentId - : throw new InvalidOperationException("The shader asset identity is not available yet."); - + internal GraphDocumentController Controller(Draft draft) => m_graphs.TryOpenDocument( + draft.id, + interactions.history, + out GraphDocumentController? controller + ) ? controller! : throw new InvalidOperationException("The Shader graph document is not available."); + internal Guid AssetId(AssetFileEntry entry) => assets.TryGetInfo( + entry.assetPath, + out AssetInfo? info + ) + && info is not null ? info.persistentId : throw new InvalidOperationException("The shader asset identity is not available yet."); internal bool TryGet( AssetFileEntry entry, out Draft draft @@ -213,22 +205,26 @@ internal void RefreshCompilation(Draft draft) draft.compilationDiagnostics = ""; return; } + if (!assets.TryGetInfo(draft.id, out AssetInfo? info) || info is null) { draft.compilationStatus = "Source unavailable"; return; } + if (info.status != AssetImportStatus.Imported) { draft.compilationStatus = "Import " + info.status + " · see diagnostics"; draft.compilationDiagnostics = string.Join("\n", info.diagnostics); return; } + if (!assets.TryLoad(draft.id, out ShaderAsset? shader) || shader is null || shader.isMissing) { draft.compilationStatus = "Waiting for import"; return; } + // Compilation observes only the imported asset, never the unsaved document. EditorShaderCompilationSnapshot snapshot = m_compilation.Request(shader, RenderShaderVariant.empty); draft.compilationStatus = snapshot.state switch @@ -240,7 +236,8 @@ internal void RefreshCompilation(Draft draft) draft.compilationDiagnostics = string.Join("\n", snapshot.diagnostics.Select(static value => value.code + ": " + value.message)); draft.diagnostics = snapshot.diagnostics.ToArray(); } - catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or InvalidOperationException or ArgumentException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.compilationStatus = "Compilation unavailable"; draft.compilationDiagnostics = failure.Message; @@ -284,12 +281,14 @@ internal void Changed(Draft draft) File.Delete(recovery); return; } + try { PreserveRecovery(draft, controller); draft.error = ""; } - catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.error = "Recovery could not be written: " + failure.Message; } @@ -318,7 +317,8 @@ internal bool Save(Draft draft) // Import runs at the next editor update, independently of persistence success and watcher timing. return true; } - catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.error = failure.Message; draft.status = "Not saved · recovery retained"; @@ -344,7 +344,8 @@ internal bool Reload(Draft draft) File.Delete(recovery); return true; } - catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.error = failure.Message; return false; @@ -379,6 +380,7 @@ protected override void OnStart(EditorContext editor) { throw new AggregateException(failure, retirement); } + throw; } } @@ -398,7 +400,8 @@ protected override void OnUpdate(EditorContext editor) { _ = assets.Import(path); } - catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { foreach (Draft draft in m_drafts.Values.Where(draft => draft.path == path)) { @@ -407,11 +410,13 @@ protected override void OnUpdate(EditorContext editor) } } } + if (m_sourceRevision != assets.revision) { SynchronizeSources(); m_sourceRevision = assets.revision; } + foreach (Draft draft in m_drafts.Values) { GraphDocumentController controller = Controller(draft); @@ -464,6 +469,7 @@ protected override void OnStop(EditorContext editor) ClearNodeDiagnostics(draft); ReleasePreview(draft); } + m_lifetime?.Dispose(); m_lifetime = null; m_frontends = null; @@ -486,21 +492,26 @@ private void Open(EditorDocumentContext document) if (recovery.assetId != document.assetId) throw new InvalidDataException("Shader recovery identity does not match its document."); } + if (assets.TryGetInfo(document.assetId, out AssetInfo? info) && info is not null && info.status != AssetImportStatus.Missing) { path = info.assetPath; interactions.documents.UpdateAssetPath(document.documentId, path.ToString()); } + ShaderGraphSourceSnapshot? source = null; string missing = ""; try { source = m_sources.Read(path); } - catch (Exception failure) when ((recovery is not null && failure is IOException or InvalidOperationException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((recovery is not null + && failure is IOException or InvalidOperationException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { missing = failure.Message; } + GraphDocumentController controller = m_graphs.OpenDocument(document.assetId, source?.document ?? new GraphDocument(), interactions.history); string staleRecovery = ""; bool canRecover = recovery is not null && (source is null || recovery.hash == source.contentHash); @@ -509,7 +520,9 @@ private void Open(EditorDocumentContext document) else if (recovery is not null) staleRecovery = ArchiveStaleRecovery(document.assetId, recoveryPath); var draft = new Draft(document.assetId, document.documentId, path, recovery?.hash ?? source!.contentHash, source?.isReadOnly ?? true) - { observedRevision = controller.revision }; + { + observedRevision = controller.revision + }; if (m_views.TryGetValue(draft.id, out ViewState view)) draft.canvas.SetViewport(new(view.x, view.y), view.zoom); else @@ -523,6 +536,7 @@ private void Open(EditorDocumentContext document) draft.status = "Source updated · stale recovery was not applied"; draft.error = "An unsaved recovery belonged to an older source revision and was archived at: " + staleRecovery; } + if (source is null) { m_graphs.SetAvailability(draft.id, false); @@ -533,10 +547,10 @@ private void Open(EditorDocumentContext document) private void PreserveRecovery( Draft draft, GraphDocumentController controller - ) - => AtomicFile.WriteAllBytes(RecoveryPath(draft.id), serialization.Serialize(new RecoveryData - { assetId = draft.id, path = draft.path.ToString(), hash = draft.hash, graph = GraphDocumentCodec.Encode(CaptureSource(draft, controller.document), serialization) })); - + ) => AtomicFile.WriteAllBytes( + RecoveryPath(draft.id), + serialization.Serialize(new RecoveryData { assetId = draft.id, path = draft.path.ToString(), hash = draft.hash, graph = GraphDocumentCodec.Encode(CaptureSource(draft, controller.document), serialization) }) + ); private GraphDocument CaptureSource( Draft draft, GraphDocument graph @@ -549,16 +563,12 @@ GraphDocument graph } private string RecoveryPath(Guid id) => Path.Combine(assets.libraryRoot, "Editor", "ShaderRecovery", id.ToString("N") + ".inno"); - private static string ArchiveStaleRecovery( Guid id, string recoveryPath ) { - string directory = Path.GetDirectoryName(recoveryPath) - ?? throw new InvalidOperationException("The Shader recovery directory is unavailable."); - string archive = Path.Combine( - directory, - id.ToString("N") + "." + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + ".stale.inno"); + string directory = Path.GetDirectoryName(recoveryPath) ?? throw new InvalidOperationException("The Shader recovery directory is unavailable."); + string archive = Path.Combine(directory, id.ToString("N") + "." + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + ".stale.inno"); File.Move(recoveryPath, archive); return archive; } @@ -581,6 +591,7 @@ private void SynchronizeSources() draft.error = "Source missing · graph and undo history retained"; continue; } + try { bool wasMissing = !controller.isAvailable; @@ -595,19 +606,22 @@ private void SynchronizeSources() draft.error = ""; continue; } + if (controller.isDirty) { PreserveRecovery(draft, controller); draft.error = "Source changed externally · reload from disk or copy these edits to a project shader"; continue; } + m_graphs.RebindDocument(draft.id, source.document); draft.hash = source.contentHash; draft.observedRevision = controller.revision; draft.error = ""; draft.status = "Source updated · compilation is separate"; } - catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is IOException or UnauthorizedAccessException or InvalidOperationException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { draft.error = failure.Message; } @@ -615,20 +629,19 @@ private void SynchronizeSources() } private void RememberView(Draft draft) => m_views[draft.id] = new(draft.id, draft.canvas.pan.x, draft.canvas.pan.y, draft.canvas.zoom); - private readonly record struct ViewState( Guid assetId, float x, float y, float zoom ); - private sealed class Provider(ShaderEditorDocuments owner) : EditorDocumentProvider { /// /// Gets the stable identity used to reference this value across subsystem boundaries. /// -public override string id => "inno.shader.graph"; + public override string id => "inno.shader.graph"; + /// /// Checks whether this document handler supports the selected asset. /// @@ -638,14 +651,14 @@ private sealed class Provider(ShaderEditorDocuments owner) : EditorDocumentProvi /// /// when the documented condition is satisfied; otherwise, . /// -public override bool CanOpen(string assetPath) => assetPath.EndsWith(".ishader", StringComparison.OrdinalIgnoreCase); + public override bool CanOpen(string assetPath) => assetPath.EndsWith(".ishader", StringComparison.OrdinalIgnoreCase); /// /// Opens the requested resource and establishes its active lifetime. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -public override void Open(EditorDocumentContext context) => owner.Open(context); + public override void Open(EditorDocumentContext context) => owner.Open(context); /// /// Persists the supplied value through the configured storage contract. /// @@ -655,7 +668,7 @@ private sealed class Provider(ShaderEditorDocuments owner) : EditorDocumentProvi /// /// when the documented condition is satisfied; otherwise, . /// -public override bool Save(EditorDocumentContext context) => owner.Save(owner.m_drafts[context.assetId]); + public override bool Save(EditorDocumentContext context) => owner.Save(owner.m_drafts[context.assetId]); /// /// Restores the draft from the latest imported asset state. /// @@ -665,14 +678,14 @@ private sealed class Provider(ShaderEditorDocuments owner) : EditorDocumentProvi /// /// when the documented condition is satisfied; otherwise, . /// -public override bool Revert(EditorDocumentContext context) => owner.Reload(owner.m_drafts[context.assetId]); + public override bool Revert(EditorDocumentContext context) => owner.Reload(owner.m_drafts[context.assetId]); /// /// Closes the active resource and releases its operation-scoped state. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -public override void Close(EditorDocumentContext context) + public override void Close(EditorDocumentContext context) { // The shared host already applied Save/Discard/Cancel policy. Close must never turn Discard into Save. if (!owner.m_drafts.TryGetValue(context.assetId, out Draft? draft)) @@ -695,7 +708,8 @@ internal sealed class Draft( AssetPath path, string hash, bool readOnly - ) { + ) + { internal readonly Guid id = id; internal readonly Guid documentId = documentId; internal AssetPath path = path; @@ -726,6 +740,7 @@ bool readOnly internal GraphNodeId? activeStage; internal string inspectedPass = ""; internal GraphNodeId[] inspectedNodes = []; + internal ShaderInspectionSelection? inspectionSelection; internal string valueGesture = Guid.NewGuid().ToString("N"); internal readonly EditorPlanarNavigation navigation = new(); internal bool dragging; @@ -750,19 +765,26 @@ private sealed class RecoveryData : ISerializable /// /// Gets or sets the persistent asset identity. /// - [SerializableProperty] public Guid assetId { get; set; } + [SerializableProperty] + public Guid assetId { get; set; } + /// /// Gets or sets the diagnostic source path. /// - [SerializableProperty] public string path { get; set; } = ""; + [SerializableProperty] + public string path { get; set; } = ""; + /// /// Gets or sets the expected disk source fingerprint. /// - [SerializableProperty] public string hash { get; set; } = ""; + [SerializableProperty] + public string hash { get; set; } = ""; + /// /// Gets or sets the complete neutral unsaved graph. /// - [SerializableProperty] public byte[] graph { get; set; } = []; + [SerializableProperty] + public byte[] graph { get; set; } = []; } } @@ -771,17 +793,17 @@ internal struct ShaderPortSnapshot /// /// Gets the stable identity used to reference this value across subsystem boundaries. /// -public string id { get; set; } + public string id { get; set; } /// /// Gets the graph port's shader value type. /// -public ShaderGraphType type { get; set; } + public ShaderGraphType type { get; set; } /// /// Gets the graph port's input or output direction. /// -public GraphPortDirection direction { get; set; } + public GraphPortDirection direction { get; set; } /// /// Gets whether required is active for the current instance. /// -public bool required { get; set; } + public bool required { get; set; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inno.Editor.Panel.ShaderEditor.csproj b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inno.Editor.Panel.ShaderEditor.csproj index 3e0433f91..7a2d6958b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inno.Editor.Panel.ShaderEditor.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inno.Editor.Panel.ShaderEditor.csproj @@ -4,61 +4,34 @@ 14.0 disable enable - true true $(WarningsAsErrors);CS1572;CS1573;CS1591 Editor - + + + + - - - - - + + + + - - + + - - - - + + + + - + - + @@ -66,5 +39,4 @@ - diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs index 1087b196c..25cc4d48e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs @@ -1,3 +1,4 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.IO; using Inno.Assets; @@ -7,19 +8,17 @@ using Inno.Editor.Inspection; using Inno.Editor.Shaders; using Inno.Rendering; -using ImGuiApi = Inno.Native.ImGui.ImGui; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - [InspectionDrawer(typeof(AssetFileEntry), priority: 100, conditional: true)] internal sealed class ShaderSourceDrawer(IInspectionIconProvider icons) : InspectionDrawer { /// /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => "S"; + public override string icon => "S"; + /// /// Retrieves the current icon from authoritative state. /// @@ -32,10 +31,10 @@ internal sealed class ShaderSourceDrawer(IInspectionIconProvider /// /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - AssetFileEntry target -) => icons.GetIcon(target); + protected override string GetIcon( + InspectionDrawContext context, + AssetFileEntry target + ) => icons.GetIcon(target); /// /// Checks whether this drawer supports the selected Inspector target. /// @@ -45,8 +44,7 @@ AssetFileEntry target /// /// when the documented condition is satisfied; otherwise, . /// -protected override bool CanInspect(AssetFileEntry target) - => !target.isDirectory && target.extension.Equals(".ishader", StringComparison.OrdinalIgnoreCase); + protected override bool CanInspect(AssetFileEntry target) => !target.isDirectory && target.extension.Equals(".ishader", StringComparison.OrdinalIgnoreCase); /// /// Binds a caller-visible label to the current inspection target. /// @@ -59,11 +57,10 @@ protected override bool CanInspect(AssetFileEntry target) /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - AssetFileEntry target -) - => (target.nameWithoutExtension, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + AssetFileEntry target + ) => (target.nameWithoutExtension, null); /// /// Renders the value presentation for the current editor frame. /// @@ -73,16 +70,16 @@ AssetFileEntry target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - AssetFileEntry target -) { + protected override void Draw( + InspectionDrawContext context, + AssetFileEntry target + ) { if (context.interactions.TryGetModule(out var documents)) { if (documents!.TryOpen(target, out ShaderEditorDocuments.Draft draft)) new ShaderEditorCanvas(documents, draft).DrawInspector(context, []); else - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); } } } @@ -93,7 +90,8 @@ internal sealed class ShaderSelectionDrawer(IInspectionIconProvider /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => "S"; + public override string icon => "S"; + /// /// Retrieves the current icon from authoritative state. /// @@ -106,13 +104,20 @@ internal sealed class ShaderSelectionDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - ShaderInspectionSelection target -) - => context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(target.assetId, out AssetInfo? info) && info is not null - && documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry) ? icons.GetIcon(entry) : icon; + protected override string GetIcon( + InspectionDrawContext context, + ShaderInspectionSelection target + ) => context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + target.assetId, + out AssetInfo? info + ) + && info is not null + && documents.assets.TryGetFileSystemEntry( + info.assetPath, + out AssetFileEntry entry + ) ? icons.GetIcon(entry) : icon; /// /// Binds a caller-visible label to the current inspection target. /// @@ -125,15 +130,21 @@ ShaderInspectionSelection target /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - ShaderInspectionSelection target -) { - if (context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(target.assetId, out AssetInfo? info) && info is not null) + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + ShaderInspectionSelection target + ) { + if (context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + target.assetId, + out AssetInfo? info + ) + && info is not null) return (Path.GetFileName(info.assetPath.localPath), null); return ("Shader", null); } + /// /// Renders the header presentation for the current editor frame. /// @@ -143,27 +154,38 @@ ShaderInspectionSelection target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - ShaderInspectionSelection target -) { - if (!context.interactions.TryGetModule(out var documents) || documents is null - || !documents.assets.TryGetInfo(target.assetId, out AssetInfo? info) || info is null - || !documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry)) + protected override void DrawHeader( + InspectionDrawContext context, + ShaderInspectionSelection target + ) { + if (!context.interactions.TryGetModule(out var documents) + || documents is null + || !documents.assets.TryGetInfo( + target.assetId, + out AssetInfo? info + ) + || info is null + || !documents.assets.TryGetFileSystemEntry( + info.assetPath, + out AssetFileEntry entry + )) { - Widget.ColoredText(EditorPalette.assetBreadcrumbText, "Node selection unavailable"); + EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Node selection unavailable"); return; } + if (!documents.TryOpen(entry, out ShaderEditorDocuments.Draft draft)) { - Widget.ColoredText(EditorPalette.assetBreadcrumbText, "Waiting for Shader asset import…"); + EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Waiting for Shader asset import…"); return; } + if (target.nodes.Count == 1 && documents.Controller(draft).document.FindNode(target.nodes[0]) is GraphNodeRecord node) - Widget.ColoredText(EditorPalette.assetBreadcrumbText, "Node: " + new ShaderEditorCanvas(documents, draft).Title(node)); + EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Node: " + new ShaderEditorCanvas(documents, draft).Title(node)); else - Widget.ColoredText(EditorPalette.assetBreadcrumbText, $"Nodes: {target.nodes.Count} selected"); + EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, $"Nodes: {target.nodes.Count} selected"); } + /// /// Renders the value presentation for the current editor frame. /// @@ -173,22 +195,22 @@ ShaderInspectionSelection target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - ShaderInspectionSelection target -) { + protected override void Draw( + InspectionDrawContext context, + ShaderInspectionSelection target + ) { if (!context.interactions.TryGetModule(out var documents) || documents is null) return; - if (!documents.assets.TryGetInfo(target.assetId, out AssetInfo? info) || info is null - || !documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry)) + if (!documents.assets.TryGetInfo(target.assetId, out AssetInfo? info) || info is null || !documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry)) { - Widget.WrappedText("Shader source unavailable. Selection identities are retained."); + EditorUI.current.widgets.WrappedText("Shader source unavailable. Selection identities are retained."); return; } + if (documents.TryOpen(entry, out ShaderEditorDocuments.Draft draft)) new ShaderEditorCanvas(documents, draft).DrawInspector(context, target.nodes); else - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); } } @@ -198,7 +220,8 @@ internal sealed class ShaderAssetDrawer(IInspectionIconProvider /// /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => "S"; + public override string icon => "S"; + /// /// Retrieves the current icon from authoritative state. /// @@ -211,12 +234,15 @@ internal sealed class ShaderAssetDrawer(IInspectionIconProvider /// /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - ShaderAsset target -) - => context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetFileSystemEntry(target.assetPath, out AssetFileEntry entry) ? icons.GetIcon(entry) : icon; + protected override string GetIcon( + InspectionDrawContext context, + ShaderAsset target + ) => context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetFileSystemEntry( + target.assetPath, + out AssetFileEntry entry + ) ? icons.GetIcon(entry) : icon; /// /// Binds a caller-visible label to the current inspection target. /// @@ -229,10 +255,10 @@ ShaderAsset target /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - ShaderAsset target -) => (target.name, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + ShaderAsset target + ) => (target.name, null); /// /// Renders the value presentation for the current editor frame. /// @@ -242,10 +268,10 @@ ShaderAsset target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - ShaderAsset target -) { + protected override void Draw( + InspectionDrawContext context, + ShaderAsset target + ) { if (!context.interactions.TryGetModule(out var documents) || documents is null) return; if (!documents.assets.TryGetFileSystemEntry(target.assetPath, out AssetFileEntry entry)) @@ -253,6 +279,6 @@ ShaderAsset target if (documents.TryOpen(entry, out ShaderEditorDocuments.Draft draft)) new ShaderEditorCanvas(documents, draft).DrawInspector(context, []); else - Widget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Waiting for Shader asset import…"); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/CreateShaderAsset.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/CreateShaderAsset.cs index f4ea9c5c3..b4b964f10 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/CreateShaderAsset.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/CreateShaderAsset.cs @@ -5,15 +5,15 @@ using Inno.Assets.Pipeline; using Inno.Core.Graphs; using Inno.Editor.Interactions; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Rendering.Shaders; namespace Inno.Editor.Panel.ShaderEditor; - [EditorAction("shader/create-asset", "panel/asset.file-browser")] internal sealed class CreateShaderAsset( ShaderEditorDocuments documents, - AssetEditorModule browser + AssetEditingModule browser ) : EditorAction { /// @@ -28,9 +28,9 @@ AssetEditorModule browser protected override EditorActionState Query(EditorActionContext context) { AssetPath path = AssetPath.Parse(context.target); - return documents.assets.sourceMounts.Any(mount => mount.id == path.source && !mount.isReadOnly) - ? EditorActionState.enabled : EditorActionState.disabled; + return documents.assets.sourceMounts.Any(mount => mount.id == path.source && !mount.isReadOnly) ? EditorActionState.enabled : EditorActionState.disabled; } + /// /// Executes the prepared operation and publishes only a completed result. /// @@ -51,10 +51,14 @@ protected override void Execute(EditorActionContext context) path = new(parent.source, prefix + (index++ == 0 ? "New Shader" : "New Shader " + index) + ".ishader"); } while (File.Exists(mount.Resolve(path.localPath)) || File.Exists(mount.Resolve(path.localPath) + ".imeta")); - GraphDocument graph = (documents.templates ?? throw new InvalidOperationException("Shader templates have not started.")) - .Create(context.argument, documents.serialization, documents.context); + GraphDocument graph = (documents.templates ?? throw new InvalidOperationException("Shader templates have not started.")).Create( + context.argument, + documents.serialization, + documents.context + ); AssetFileEntry created = browser.CreateSource(path, GraphDocumentCodec.Encode(graph, documents.serialization)); - browser.BeginCreatedSourceRename(created); + _ = context.interactions.For("panel/asset.file-browser", created).Select(); + _ = context.interactions.For("panel/asset.file-browser", created).Execute("file-browser/rename"); context.interactions.OpenPanel("rendering.shader-editor"); } } @@ -71,19 +75,15 @@ internal sealed class ShaderTemplateMenu(ShaderEditorDocuments documents) : Edit /// /// The builder consumed by build; ownership remains with the caller unless explicitly stated otherwise. /// -public override void Build( - EditorMenuContext context, - EditorMenuBuilder builder -) { + public override void Build( + EditorMenuContext context, + EditorMenuBuilder builder + ) { if (documents.templates is null) return; builder.AddGroup("Create/Rendering", order: 200, separatorBefore: true); builder.AddGroup("Create/Rendering/Shaders", order: 0); foreach (ShaderGraphTemplateInfo template in documents.templates.templates) - builder.Add( - "Create/Rendering/Shaders/" + template.displayName, - "shader/create-asset", - argument: template.id, - order: 0); + builder.Add("Create/Rendering/Shaders/" + template.displayName, "shader/create-asset", argument: template.id, order: 0); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasActions.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasActions.cs index 6f701608b..158fe9913 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasActions.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasActions.cs @@ -1,3 +1,6 @@ +using Inno.Editor.Core; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Shaders.ImGui; using System; using System.Collections.Generic; using System.Linq; @@ -12,7 +15,6 @@ using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; - [EditorMenuSource(ShaderEditorCanvas.C_AREA)] internal sealed class ShaderCanvasMenu(ShaderEditorDocuments documents) : EditorMenuSource { @@ -41,53 +43,73 @@ EditorMenuBuilder builder builder.Add("Create/Outputs/Fragment Output", "shader/create-output", order: 10, argument: "Fragment"); builder.Add("Create/Outputs/Compute Output", "shader/create-output", order: 20, argument: "Compute"); } + foreach (string definition in documents.nodes.definitionIds) - if (definition is not ("inno.shader.source" or ShaderGraphNodes.callDefinitionId) - && documents.CanCreate(draft, new(definition))) + if (definition is not ("inno.shader.source" or ShaderGraphNodes.callDefinitionId) && documents.CanCreate(draft, new(definition))) { string category = Category(definition); int order = CategoryOrder(category); - if (documents.drawers?.TryGetPresentation(definition, out ShaderNodePresentation presentation) == true - && presentation.createPath.Length != 0) + if (documents.drawers?.TryGetPresentation(definition, out ShaderNodePresentation presentation) == true && presentation.createPath.Length != 0) { category = presentation.createPath; order = presentation.createOrder; builder.AddGroup("Create/" + category, order, presentation.separatorBefore); } - builder.Add("Create/" + category + "/" + (documents.drawers?.GetDisplayName(definition) - ?? Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.NicifyName(definition.Replace("inno.shader.", "", StringComparison.Ordinal).Replace('-', ' '))), - "shader/create-node", order, argument: new ShaderNodeCreation(definition)); + + builder.Add( + "Create/" + category + "/" + (documents.drawers?.GetDisplayName(definition) ?? EditorDisplayNames.Nicify(definition.Replace("inno.shader.", "", StringComparison.Ordinal).Replace('-', ' '))), + "shader/create-node", + order, + argument: new ShaderNodeCreation(definition) + ); } + var functionLibraries = new List<(AssetFileEntry source, AssetInfo info, ShaderFunctionAsset library)>(); foreach (AssetFileEntry source in documents.assets.GetFileSystemEntries(includeDirectories: false)) - if (source.extension == ".ishadersource" && documents.assets.TryGetInfo(source.assetPath, out var info) && info is not null - && documents.assets.TryLoad(info.persistentId, out ShaderFunctionAsset? library) && library is not null && !library.isMissing) + if (source.extension == ".ishadersource" + && documents.assets.TryGetInfo( + source.assetPath, + out var info + ) + && info is not null + && documents.assets.TryLoad( + info.persistentId, + out ShaderFunctionAsset? library + ) + && library is not null + && !library.isMissing) functionLibraries.Add((source, info, library)); - foreach (var catalog in functionLibraries - .Select(static value => (path: NormalizeCatalog(value.library.catalogPath), value.library.catalogOrder)) - .Where(static value => value.path.Length != 0) - .GroupBy(static value => value.path.Split('/')[0], StringComparer.Ordinal) - .Select(static group => (name: group.Key, order: group.Min(static value => value.catalogOrder))) - .OrderBy(static value => value.order).ThenBy(static value => value.name, StringComparer.Ordinal)) + foreach (var catalog in functionLibraries.Select(static value => (path: NormalizeCatalog(value.library.catalogPath), value.library.catalogOrder)).Where(static value => value.path.Length != 0).GroupBy( + static value => value.path.Split('/')[0], + StringComparer.Ordinal + ).Select(static group => (name: group.Key, order: group.Min(static value => value.catalogOrder))).OrderBy(static value => value.order).ThenBy( + static value => value.name, + StringComparer.Ordinal + )) builder.AddGroup("Create/Functions/" + catalog.name, order: 500 + catalog.order, separatorBefore: true); - foreach ((AssetFileEntry source, AssetInfo info, ShaderFunctionAsset library) in functionLibraries - .OrderBy(static value => value.library.catalogOrder) - .ThenBy(static value => value.source.nameWithoutExtension, StringComparer.Ordinal)) + foreach ((AssetFileEntry source, AssetInfo info, ShaderFunctionAsset library)in functionLibraries.OrderBy(static value => value.library.catalogOrder).ThenBy( + static value => value.source.nameWithoutExtension, + StringComparer.Ordinal + )) foreach (string function in library.exports) - if (documents.CanCreate(draft, new("inno.shader.source", info.persistentId, function))) - { - string catalog = NormalizeCatalog(library.catalogPath); - string prefix = "Create/Functions/" + (catalog.Length == 0 ? "General" : catalog); - builder.Add(prefix + "/" + source.nameWithoutExtension + "/" + function, "shader/create-node", - order: 500 + library.catalogOrder, - argument: new ShaderNodeCreation("inno.shader.source", info.persistentId, function)); - } - foreach (AssetFileEntry source in documents.assets.GetFileSystemEntries(includeDirectories: false) - .Where(static entry => entry.extension == ".ishader") - .OrderBy(static entry => entry.assetPath.ToString(), StringComparer.Ordinal)) + if (documents.CanCreate(draft, new("inno.shader.source", info.persistentId, function))) + { + string catalog = NormalizeCatalog(library.catalogPath); + string prefix = "Create/Functions/" + (catalog.Length == 0 ? "General" : catalog); + builder.Add( + prefix + "/" + source.nameWithoutExtension + "/" + function, + "shader/create-node", + order: 500 + library.catalogOrder, + argument: new ShaderNodeCreation("inno.shader.source", info.persistentId, function) + ); + } + + foreach (AssetFileEntry source in documents.assets.GetFileSystemEntries(includeDirectories: false).Where(static entry => entry.extension == ".ishader").OrderBy( + static entry => entry.assetPath.ToString(), + StringComparer.Ordinal + )) { - if (!documents.assets.TryGetInfo(source.assetPath, out AssetInfo? info) || info is null - || info.persistentId == draft.id || info.status != AssetImportStatus.Imported) + if (!documents.assets.TryGetInfo(source.assetPath, out AssetInfo? info) || info is null || info.persistentId == draft.id || info.status != AssetImportStatus.Imported) continue; ShaderGraphNodeInterface nodeInterface; try @@ -96,20 +118,20 @@ EditorMenuBuilder builder } catch (Exception failure) when ((failure is InvalidOperationException or ArgumentException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) - { - continue; - } + { + continue; + } + var creation = new ShaderNodeCreation(ShaderGraphNodes.callDefinitionId, info.persistentId); if (!documents.CanCreate(draft, creation)) continue; string catalog = NormalizeCatalog(nodeInterface.createPath); string root = nodeInterface.kind == ShaderGraphNodeKind.DomainOutput ? "Domain Outputs" : "Graph Nodes"; string group = "Create/" + root + (catalog.Length == 0 ? "" : "/" + catalog); - builder.AddGroup(group, order: nodeInterface.createOrder, - separatorBefore: nodeInterface.kind == ShaderGraphNodeKind.DomainOutput); - builder.Add(group + "/" + nodeInterface.displayName, "shader/create-node", - order: nodeInterface.createOrder, argument: creation); + builder.AddGroup(group, order: nodeInterface.createOrder, separatorBefore: nodeInterface.kind == ShaderGraphNodeKind.DomainOutput); + builder.Add(group + "/" + nodeInterface.displayName, "shader/create-node", order: nodeInterface.createOrder, argument: creation); } + builder.Add("View/Focus Selection", "shader/focus", order: 900); builder.Add("Edit/Copy", "shader/copy", order: 1000); builder.Add("Edit/Cut", "shader/cut", order: 110); @@ -124,7 +146,6 @@ EditorMenuBuilder builder builder.Add("Assets/Show Shader in File Browser", "shader/reveal-shader", order: 1900); builder.Add("Assets/Show Source in File Browser", "shader/reveal-source", order: 1910); builder.Add("Assets/Copy Shader To Project", "shader/copy-to-project", order: 1920, separatorBefore: true); - static string Category(string definition) => definition switch { "inno.shader.constant" or "inno.shader.stage-input" => "Inputs", @@ -137,7 +158,6 @@ EditorMenuBuilder builder _ when definition.Contains("surface-output", StringComparison.Ordinal) => "Domain Outputs", _ => "Domain" }; - static int CategoryOrder(string category) => category switch { "Outputs" => 0, @@ -151,9 +171,7 @@ _ when definition.Contains("surface-output", StringComparison.Ordinal) => "Domai "Domain Outputs" => 800, _ => 850 }; - - static string NormalizeCatalog(string value) - => string.Join('/', value.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); + static string NormalizeCatalog(string value) => string.Join('/', value.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); } } @@ -169,39 +187,33 @@ internal sealed class CreateShaderOutput(ShaderEditorDocuments documents) : Edit /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) + protected override EditorActionState Query(EditorActionContext context) { - if (!documents.TryGet(context.target, out var draft) || draft.readOnly - || !Enum.TryParse(context.argument, out Inno.Rendering.ShaderStage stage)) - return EditorActionState.disabled; + if (!documents.TryGet(context.target, out var draft) || draft.readOnly || !Enum.TryParse(context.argument, out Inno.Rendering.ShaderStage stage)) + return EditorActionState.disabled; GraphDocument graph = documents.Controller(draft).document; if (ShaderGraphDocument.ReadTarget(graph, documents.serialization, documents.context).Length != 0) return EditorActionState.disabled; - bool exists = graph.nodes.Where(static node => node.definitionId == ShaderGraphDocument.outputDefinitionId) - .Any(node => ShaderGraphDocument.Read(node, "settings", new ShaderGraphStageSettings(), documents.serialization, documents.context).stage == stage); + bool exists = graph.nodes.Where(static node => node.definitionId == ShaderGraphDocument.outputDefinitionId).Any(node => ShaderGraphDocument.Read(node, "settings", new ShaderGraphStageSettings(), documents.serialization, documents.context).stage == stage); return exists ? EditorActionState.disabled : EditorActionState.enabled; } + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); var controller = documents.Controller(draft); GraphDocument graph = controller.document.Clone(); Inno.Rendering.Assets.ShaderDefinition definition = ShaderGraphDocument.ReadDefinition(graph, documents.serialization, documents.context); Inno.Rendering.ShaderStage stage = Enum.Parse(context.argument); - Inno.Rendering.ShaderProgramKind kind = stage == Inno.Rendering.ShaderStage.Compute - ? Inno.Rendering.ShaderProgramKind.Compute : Inno.Rendering.ShaderProgramKind.Raster; + Inno.Rendering.ShaderProgramKind kind = stage == Inno.Rendering.ShaderStage.Compute ? Inno.Rendering.ShaderProgramKind.Compute : Inno.Rendering.ShaderProgramKind.Raster; ShaderGraphPassProgram[] programs = ShaderGraphPrograms.Read(graph, documents.serialization, documents.context); - ShaderGraphPassProgram? incomplete = kind == Inno.Rendering.ShaderProgramKind.Raster - ? programs.FirstOrDefault(program => definition.passes.Any(pass => pass.name == program.pass && pass.programKind == kind) - && !program.stages.Select(id => graph.FindNode(new(id))).OfType() - .Any(node => ShaderGraphDocument.Read(node, "settings", new ShaderGraphStageSettings(), documents.serialization, documents.context).stage == stage)) - : null; + ShaderGraphPassProgram? incomplete = kind == Inno.Rendering.ShaderProgramKind.Raster ? programs.FirstOrDefault(program => definition.passes.Any(pass => pass.name == program.pass && pass.programKind == kind) && !program.stages.Select(id => graph.FindNode(new(id))).OfType().Any(node => ShaderGraphDocument.Read(node, "settings", new ShaderGraphStageSettings(), documents.serialization, documents.context).stage == stage)) : null; string name; string[] stages; if (incomplete is ShaderGraphPassProgram existing && existing.pass is not null) @@ -215,15 +227,30 @@ protected override void Execute(EditorActionContext cont name = prefix; for (int suffix = 2; definition.passes.Any(pass => pass.name == name); suffix++) name = prefix + " " + suffix; - definition.passes = [.. definition.passes, new(name, kind)]; - graph.SetMetadata(ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(documents.serialization.Serialize(definition, documents.context), documents.serialization, documents.context)); + definition.passes = [..definition.passes, new(name, kind)]; + graph.SetMetadata( + ShaderGraphDocument.definitionKey, + ShaderGraphDocument.Encode(documents.serialization.Serialize(definition, documents.context), documents.serialization, documents.context) + ); stages = []; } + GraphNodeId id = new(Guid.NewGuid().ToString("N")); - var node = new GraphNodeRecord(id, ShaderGraphDocument.outputDefinitionId) { position = draft.menuPosition }; - node.SetValue("settings", ShaderGraphDocument.Encode(new ShaderGraphStageSettings { stage = stage, - outputs = stage == Inno.Rendering.ShaderStage.Compute ? [] : [new() { id = stage == Inno.Rendering.ShaderStage.Vertex ? "position" : "color", - kind = stage == Inno.Rendering.ShaderStage.Vertex ? ShaderIrOutputKind.ClipPosition : ShaderIrOutputKind.Color }] }, documents.serialization, documents.context)); + var node = new GraphNodeRecord(id, ShaderGraphDocument.outputDefinitionId) + { + position = draft.menuPosition + }; + node.SetValue( + "settings", + ShaderGraphDocument.Encode( + new ShaderGraphStageSettings { + stage = stage, + outputs = stage == Inno.Rendering.ShaderStage.Compute ? [] : [new() { id = stage == Inno.Rendering.ShaderStage.Vertex ? "position" : "color", kind = stage == Inno.Rendering.ShaderStage.Vertex ? ShaderIrOutputKind.ClipPosition : ShaderIrOutputKind.Color }] + }, + documents.serialization, + documents.context + ) + ); graph.AddNode(node); graph = ShaderGraphPrograms.Bind(graph, name, stages.Select(static value => new GraphNodeId(value)).Append(id), documents.serialization, documents.context); controller.ReplaceDocument(graph, "Create " + stage + " Output"); @@ -245,16 +272,21 @@ internal sealed class CreateShaderNode(ShaderEditorDocuments documents) : Editor /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => documents.TryGet(context.target, out var draft) && documents.CanCreate(draft, context.argument) ? EditorActionState.enabled : EditorActionState.disabled; + protected override EditorActionState Query(EditorActionContext context) => documents.TryGet( + context.target, + out var draft + ) + && documents.CanCreate( + draft, + context.argument + ) ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The context that supplies state and services for this operation. /// - protected override void Execute(EditorActionContext context) - => documents.Create(documents.Open(context.target), context.argument); + protected override void Execute(EditorActionContext context) => documents.Create(documents.Open(context.target), context.argument); } internal abstract class ShaderSelectionAction(ShaderEditorDocuments documents) : EditorAction @@ -262,15 +294,16 @@ internal abstract class ShaderSelectionAction(ShaderEditorDocuments documents) : /// /// Gets documents presented by this Shader Editor action. /// -protected ShaderEditorDocuments documents { get; } = documents; + protected ShaderEditorDocuments documents { get; } = documents; /// /// Gets whether writes is active for the current instance. /// -protected virtual bool writes => true; + protected virtual bool writes => true; /// /// Gets whether needs selection is active for the current instance. /// -protected virtual bool needsSelection => true; + protected virtual bool needsSelection => true; + /// /// Evaluates the operation's current availability and presentation state. /// @@ -280,10 +313,14 @@ internal abstract class ShaderSelectionAction(ShaderEditorDocuments documents) : /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => documents.TryGet(context.target, out var draft) - && (!writes || !draft.readOnly) && (!needsSelection || draft.canvas.selectedNodes.Count != 0) - ? EditorActionState.enabled : EditorActionState.disabled; + protected override EditorActionState Query(EditorActionContext context) => documents.TryGet( + context.target, + out var draft + ) + && (!writes + || !draft.readOnly) + && (!needsSelection + || draft.canvas.selectedNodes.Count != 0) ? EditorActionState.enabled : EditorActionState.disabled; } [EditorAction("shader/copy", ShaderEditorCanvas.C_AREA)] @@ -293,7 +330,8 @@ internal sealed class CopyShaderNodes(ShaderEditorDocuments documents) : ShaderS /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; + /// /// Executes the prepared operation and publishes only a completed result. /// @@ -315,7 +353,8 @@ internal sealed class DeleteShaderNodes(ShaderEditorDocuments documents) : Shade /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// @@ -364,7 +403,8 @@ internal sealed class PasteShaderNodes(ShaderEditorDocuments documents) : Shader /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Evaluates the operation's current availability and presentation state. /// @@ -374,10 +414,16 @@ internal sealed class PasteShaderNodes(ShaderEditorDocuments documents) : Shader /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => base.Query(context).isEnabled && documents.clipboard is not null - && documents.TryGet(context.target, out var draft) && documents.CanPaste(draft, documents.clipboard) - ? EditorActionState.enabled : EditorActionState.disabled; + protected override EditorActionState Query(EditorActionContext context) => base.Query(context).isEnabled + && documents.clipboard is not null + && documents.TryGet( + context.target, + out var draft + ) + && documents.CanPaste( + draft, + documents.clipboard + ) ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// @@ -405,13 +451,13 @@ internal sealed class DuplicateShaderNodes(ShaderEditorDocuments documents) : Sh /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) + protected override EditorActionState Query(EditorActionContext context) { if (!base.Query(context).isEnabled || !documents.TryGet(context.target, out var draft)) return EditorActionState.disabled; - return draft.canvas.selectedNodes.Any(id => documents.Controller(draft).document.FindNode(id)?.definitionId == ShaderGraphDocument.outputDefinitionId) - ? EditorActionState.disabled : EditorActionState.enabled; + return draft.canvas.selectedNodes.Any(id => documents.Controller(draft).document.FindNode(id)?.definitionId == ShaderGraphDocument.outputDefinitionId) ? EditorActionState.disabled : EditorActionState.enabled; } + /// /// Executes the prepared operation and publishes only a completed result. /// @@ -453,15 +499,15 @@ internal sealed class SaveShader(ShaderEditorDocuments documents) : ShaderSelect /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The context that supplies state and services for this operation. /// - protected override void Execute(EditorActionContext context) - => context.interactions.documents.Save(documents.Open(context.target).documentId); + protected override void Execute(EditorActionContext context) => context.interactions.documents.Save(documents.Open(context.target).documentId); } [EditorAction("shader/format", ShaderEditorCanvas.C_AREA)] @@ -470,22 +516,19 @@ internal sealed class FormatShaderGraph(ShaderEditorDocuments documents) : Shade /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); var controller = documents.Controller(draft); - controller.ReplaceDocument(ShaderGraphAutoLayout.Apply( - controller.document, - documents.serialization, - documents.context, - documents.Describe), "Format Shader Graph"); + controller.ReplaceDocument(ShaderGraphAutoLayout.Apply(controller.document, documents.serialization, documents.context, documents.Describe), "Format Shader Graph"); draft.frameRequested = true; documents.Changed(draft); } @@ -497,17 +540,17 @@ internal sealed class ReloadShaderSource(ShaderEditorDocuments documents) : Shad /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The context that supplies state and services for this operation. /// - protected override void Execute(EditorActionContext context) - => context.interactions.documents.Revert(documents.Open(context.target).documentId); + protected override void Execute(EditorActionContext context) => context.interactions.documents.Revert(documents.Open(context.target).documentId); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasOrganizationActions.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasOrganizationActions.cs index ecad1d19c..29a18c26b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasOrganizationActions.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Interactions/ShaderCanvasOrganizationActions.cs @@ -7,27 +7,27 @@ using Inno.Core.Graphs; using Inno.Editor.Graph; using Inno.Editor.Interactions; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Rendering; using Inno.Rendering.Shaders; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - internal struct ShaderCanvasGroup { /// /// Gets the stable identity used to reference this value across subsystem boundaries. /// -public string id { get; set; } + public string id { get; set; } /// /// Gets the title text used by the current instance. /// -public string title { get; set; } + public string title { get; set; } /// /// Gets graph nodes affected by this organization action. /// -public string[] nodes { get; set; } + public string[] nodes { get; set; } } [EditorAction("shader/focus", ShaderEditorCanvas.C_AREA)] @@ -36,18 +36,19 @@ internal sealed class FocusShaderNodes(ShaderEditorDocuments documents) : Shader /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) => documents.Open(context.target).frameRequested = true; + protected override void Execute(EditorActionContext context) => documents.Open(context.target).frameRequested = true; } [EditorAction("shader/reroute", ShaderEditorCanvas.C_AREA)] @@ -56,7 +57,8 @@ internal sealed class InsertShaderReroute(ShaderEditorDocuments documents) : Sha /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -66,22 +68,25 @@ internal sealed class InsertShaderReroute(ShaderEditorDocuments documents) : Sha /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) - => base.Query(context).isEnabled && documents.Open(context.target).selectedEdge is not null ? EditorActionState.enabled : EditorActionState.disabled; + protected override EditorActionState Query(EditorActionContext context) => base.Query(context).isEnabled + && documents.Open(context.target).selectedEdge is not null ? EditorActionState.enabled : EditorActionState.disabled; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); var controller = documents.Controller(draft); GraphDocument graph = controller.document.Clone(); GraphEdgeRecord edge = graph.edges.Single(value => value.id == draft.selectedEdge); ShaderNodePort source = draft.ports[edge.output.nodeId].Single(value => value.id == edge.output.portId.value); - var node = new GraphNodeRecord(new(Guid.NewGuid().ToString("N")), "inno.shader.reroute") { position = draft.menuPosition }; + var node = new GraphNodeRecord(new(Guid.NewGuid().ToString("N")), "inno.shader.reroute") + { + position = draft.menuPosition + }; node.SetValue("valueType", ShaderGraphDocument.Encode(ShaderGraphType.Capture(source.type), documents.serialization, documents.context)); string stage = ShaderGraphDocument.Read(graph.FindNode(edge.output.nodeId)!, "stage", "", documents.serialization, documents.context); node.SetValue("stage", ShaderGraphDocument.Encode(stage, documents.serialization, documents.context)); @@ -103,22 +108,33 @@ internal sealed class GroupShaderNodes(ShaderEditorDocuments documents) : Shader internal static ShaderCanvasGroup[] Read( ShaderEditorDocuments documents, GraphDocument graph - ) - => graph.metadata.TryGetValue(C_GROUPS, out GraphSerializedValue? value) - ? ShaderGraphDocument.Decode(value, documents.serialization, documents.context) : []; + ) => graph.metadata.TryGetValue( + C_GROUPS, + out GraphSerializedValue? value + ) ? ShaderGraphDocument.Decode( + value, + documents.serialization, + documents.context + ) : []; /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); GraphDocument graph = documents.Controller(draft).document.Clone(); ShaderCanvasGroup[] groups = Read(documents, graph); - groups = [.. groups, new() { id = Guid.NewGuid().ToString("N"), title = "Group " + (groups.Length + 1), - nodes = draft.canvas.selectedNodes.Select(static id => id.value).ToArray() }]; + groups = [..groups, new() + { + id = Guid.NewGuid().ToString("N"), + title = "Group " + (groups.Length + 1), + nodes = draft.canvas.selectedNodes.Select(static id => id.value).ToArray() + } + + ]; graph.SetMetadata(C_GROUPS, ShaderGraphDocument.Encode(groups, documents.serialization, documents.context)); documents.Controller(draft).ReplaceDocument(graph, "Group Shader Nodes"); draft.selectedGroupId = groups[^1].id; @@ -135,13 +151,14 @@ internal sealed class UngroupShaderNodes(ShaderEditorDocuments documents) : Shad /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); GraphDocument graph = documents.Controller(draft).document.Clone(); - var groups = GroupShaderNodes.Read(documents, graph).Where(group => draft.selectedGroupId.Length != 0 - ? group.id != draft.selectedGroupId - : !group.nodes.All(id => draft.canvas.selectedNodes.Contains(new(id)))).ToArray(); + var groups = GroupShaderNodes.Read( + documents, + graph + ).Where(group => draft.selectedGroupId.Length != 0 ? group.id != draft.selectedGroupId : !group.nodes.All(id => draft.canvas.selectedNodes.Contains(new(id)))).ToArray(); graph.SetMetadata(GroupShaderNodes.C_GROUPS, ShaderGraphDocument.Encode(groups, documents.serialization, documents.context)); documents.Controller(draft).ReplaceDocument(graph, "Ungroup Shader Nodes"); draft.selectedGroupId = ""; @@ -152,9 +169,8 @@ protected override void Execute(EditorActionContext context) [EditorAction("shader/collapse-subgraph", ShaderEditorCanvas.C_AREA)] internal sealed class CollapseShaderSubgraph( ShaderEditorDocuments documents, - AssetEditorModule browser -) - : ShaderSelectionAction(documents) + AssetBrowserModule browser +) : ShaderSelectionAction(documents) { /// /// Evaluates whether the requested change can be applied to the current generation. @@ -165,27 +181,21 @@ AssetEditorModule browser /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) + protected override EditorActionState Query(EditorActionContext context) { if (!base.Query(context).isEnabled) return EditorActionState.disabled; ShaderEditorDocuments.Draft draft = documents.Open(context.target); GraphDocument graph = documents.Controller(draft).document; GraphNodeRecord[] selected = draft.canvas.selectedNodes.Select(graph.FindNode).OfType().ToArray(); - if (selected.Length != draft.canvas.selectedNodes.Count || selected.Any(static node => - node.definitionId is ShaderGraphDocument.outputDefinitionId - or ShaderGraphNodes.inputDefinitionId - or ShaderGraphNodes.outputDefinitionId)) + if (selected.Length != draft.canvas.selectedNodes.Count + || selected.Any(static node => node.definitionId is ShaderGraphDocument.outputDefinitionId or ShaderGraphNodes.inputDefinitionId or ShaderGraphNodes.outputDefinitionId)) return EditorActionState.disabled; - string[] stages = selected.Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.stageKey, - "", documents.serialization, documents.context)) - .Distinct(StringComparer.Ordinal).ToArray(); + string[] stages = selected.Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.stageKey, "", documents.serialization, documents.context)).Distinct(StringComparer.Ordinal).ToArray(); if (stages.Length != 1 || stages[0].Length == 0) return EditorActionState.disabled; HashSet ids = selected.Select(static node => node.id).ToHashSet(); - return graph.edges.Any(edge => ids.Contains(edge.input.nodeId) != ids.Contains(edge.output.nodeId)) - ? EditorActionState.enabled - : EditorActionState.disabled; + return graph.edges.Any(edge => ids.Contains(edge.input.nodeId) != ids.Contains(edge.output.nodeId)) ? EditorActionState.enabled : EditorActionState.disabled; } /// @@ -194,44 +204,25 @@ or ShaderGraphNodes.inputDefinitionId /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { ShaderEditorDocuments.Draft draft = documents.Open(context.target); GraphDocumentController controller = documents.Controller(draft); GraphDocument parent = controller.document; - HashSet selected = [.. draft.canvas.selectedNodes]; + HashSet selected = [..draft.canvas.selectedNodes]; GraphNodeRecord[] nodes = parent.nodes.Where(node => selected.Contains(node.id)).ToArray(); - string stage = nodes.Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.stageKey, - "", documents.serialization, documents.context)) - .Distinct(StringComparer.Ordinal).Single(); - GraphEdgeRecord[] incomingEdges = parent.edges - .Where(edge => selected.Contains(edge.input.nodeId) && !selected.Contains(edge.output.nodeId)).ToArray(); - GraphEdgeRecord[] outgoingEdges = parent.edges - .Where(edge => selected.Contains(edge.output.nodeId) && !selected.Contains(edge.input.nodeId)).ToArray(); - + string stage = nodes.Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.stageKey, "", documents.serialization, documents.context)).Distinct(StringComparer.Ordinal).Single(); + GraphEdgeRecord[] incomingEdges = parent.edges.Where(edge => selected.Contains(edge.input.nodeId) && !selected.Contains(edge.output.nodeId)).ToArray(); + GraphEdgeRecord[] outgoingEdges = parent.edges.Where(edge => selected.Contains(edge.output.nodeId) && !selected.Contains(edge.input.nodeId)).ToArray(); var inputGroups = incomingEdges.GroupBy(static edge => edge.output).ToArray(); var outputGroups = outgoingEdges.GroupBy(static edge => edge.output).ToArray(); var inputNames = new HashSet(StringComparer.Ordinal); var outputNames = new HashSet(StringComparer.Ordinal); - ShaderGraphNodePortDefinition[] inputs = inputGroups.Select(group => Port( - Unique(group.First().input.portId.value, "input", inputNames), - draft.ports[group.First().input.nodeId].Single(port => port.id == group.First().input.portId.value).type, - required: true)).ToArray(); - ShaderGraphNodePortDefinition[] outputs = outputGroups.Select(group => Port( - Unique(group.Key.portId.value, "output", outputNames), - draft.ports[group.Key.nodeId].Single(port => port.id == group.Key.portId.value).type, - required: false)).ToArray(); - + ShaderGraphNodePortDefinition[] inputs = inputGroups.Select(group => Port(Unique(group.First().input.portId.value, "input", inputNames), draft.ports[group.First().input.nodeId].Single(port => port.id == group.First().input.portId.value).type, required: true)).ToArray(); + ShaderGraphNodePortDefinition[] outputs = outputGroups.Select(group => Port(Unique(group.Key.portId.value, "output", outputNames), draft.ports[group.Key.nodeId].Single(port => port.id == group.Key.portId.value).type, required: false)).ToArray(); ShaderDefinition parentDefinition = ShaderGraphDocument.ReadDefinition(parent, documents.serialization, documents.context); - HashSet ownedBindings = nodes.Where(static node => node.definitionId == "inno.shader.stage-input") - .Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.settingsKey, - new ShaderGraphInputSettings(), documents.serialization, documents.context).id) - .ToHashSet(StringComparer.Ordinal); - ShaderDefinition childDefinition = new( - "Graph Node", - parentDefinition.properties.Where(property => ownedBindings.Contains(property.id.value)), - [], - []); + HashSet ownedBindings = nodes.Where(static node => node.definitionId == "inno.shader.stage-input").Select(node => ShaderGraphDocument.Read(node, ShaderGraphDocument.settingsKey, new ShaderGraphInputSettings(), documents.serialization, documents.context).id).ToHashSet(StringComparer.Ordinal); + ShaderDefinition childDefinition = new("Graph Node", parentDefinition.properties.Where(property => ownedBindings.Contains(property.id.value)), [], []); GraphDocument child = ShaderGraphDocument.Create(childDefinition, documents.serialization, documents.context); string displayName = SuggestedName(documents, draft, selected); var nodeSettings = new ShaderGraphNodeSettings @@ -242,7 +233,6 @@ protected override void Execute(EditorActionContext context) effect = outputs.Length == 0 ? ShaderGraphNodeEffect.SideEffect : ShaderGraphNodeEffect.Pure }; ShaderGraphNodes.WriteSettings(child, nodeSettings, documents.serialization, documents.context); - float minX = nodes.Min(static node => node.position.x); float minY = nodes.Min(static node => node.position.y); var remap = new Dictionary(); @@ -253,57 +243,51 @@ protected override void Execute(EditorActionContext context) { position = new(source.position.x - minX + 280, source.position.y - minY + 80) }; - foreach ((string key, GraphSerializedValue value) in source.values) + foreach ((string key, GraphSerializedValue value)in source.values) if (key != ShaderGraphDocument.stageKey) copy.SetValue(key, value.Clone()); child.AddNode(copy); remap.Add(source.id, id); } - foreach (GraphEdgeRecord edge in parent.edges.Where(edge => selected.Contains(edge.output.nodeId) - && selected.Contains(edge.input.nodeId))) - child.AddEdge(new( - new("body/" + edge.id.value), - new(remap[edge.output.nodeId], edge.output.portId), - new(remap[edge.input.nodeId], edge.input.portId))); + foreach (GraphEdgeRecord edge in parent.edges.Where(edge => selected.Contains(edge.output.nodeId) && selected.Contains(edge.input.nodeId))) + child.AddEdge(new(new("body/" + edge.id.value), new(remap[edge.output.nodeId], edge.output.portId), new(remap[edge.input.nodeId], edge.input.portId))); if (inputs.Length != 0) { var boundary = new GraphNodeRecord(new("function-inputs"), ShaderGraphNodes.inputDefinitionId) { position = new(20, 80) }; - boundary.SetValue(ShaderGraphDocument.settingsKey, ShaderGraphDocument.Encode( - new ShaderGraphNodeInputSettings { ports = inputs }, documents.serialization, documents.context)); + boundary.SetValue( + ShaderGraphDocument.settingsKey, + ShaderGraphDocument.Encode(new ShaderGraphNodeInputSettings { ports = inputs }, documents.serialization, documents.context) + ); child.AddNode(boundary); for (int index = 0; index < inputGroups.Length; index++) foreach (GraphEdgeRecord edge in inputGroups[index]) - child.AddEdge(new( - new("input/" + edge.id.value), - new(boundary.id, new(inputs[index].id)), - new(remap[edge.input.nodeId], edge.input.portId))); + child.AddEdge(new(new("input/" + edge.id.value), new(boundary.id, new(inputs[index].id)), new(remap[edge.input.nodeId], edge.input.portId))); } + if (outputs.Length != 0) { var boundary = new GraphNodeRecord(new("function-outputs"), ShaderGraphNodes.outputDefinitionId) { position = new(nodes.Max(static node => node.position.x) - minX + 600, 80) }; - boundary.SetValue(ShaderGraphDocument.settingsKey, ShaderGraphDocument.Encode( - new ShaderGraphNodeOutputSettings { ports = outputs }, documents.serialization, documents.context)); + boundary.SetValue( + ShaderGraphDocument.settingsKey, + ShaderGraphDocument.Encode(new ShaderGraphNodeOutputSettings { ports = outputs }, documents.serialization, documents.context) + ); child.AddNode(boundary); for (int index = 0; index < outputGroups.Length; index++) - child.AddEdge(new( - new("output/" + index), - new(remap[outputGroups[index].Key.nodeId], outputGroups[index].Key.portId), - new(boundary.id, new(outputs[index].id)))); + child.AddEdge(new(new("output/" + index), new(remap[outputGroups[index].Key.nodeId], outputGroups[index].Key.portId), new(boundary.id, new(outputs[index].id)))); } AssetPath path = UniquePath(draft.path, displayName, documents); using EditorHistoryTransaction transaction = documents.interactions.history.BeginTransaction("Collapse Shader Subgraph"); - AssetFileEntry created = browser.CreateSource(path, GraphDocumentCodec.Encode(child, documents.serialization)); + AssetFileEntry created = browser.edits.CreateSource(path, GraphDocumentCodec.Encode(child, documents.serialization)); Guid sourceId = documents.AssetId(created); - ShaderGraphNodeInterface nodeInterface = ShaderGraphNodes.ReadInterface( - child, documents.serialization, documents.context); + ShaderGraphNodeInterface nodeInterface = ShaderGraphNodes.ReadInterface(child, documents.serialization, documents.context); GraphDocument candidate = parent.Clone(); foreach (GraphNodeId id in selected) candidate.RemoveNode(id); @@ -317,24 +301,16 @@ protected override void Execute(EditorActionContext context) call.SetValue(ShaderGraphNodes.interfaceKey, ShaderGraphDocument.Encode(nodeInterface, documents.serialization, documents.context)); candidate.AddNode(call); for (int index = 0; index < inputGroups.Length; index++) - candidate.AddEdge(new( - new(Guid.NewGuid().ToString("N")), - inputGroups[index].Key, - new(call.id, new(inputs[index].id)))); + candidate.AddEdge(new(new(Guid.NewGuid().ToString("N")), inputGroups[index].Key, new(call.id, new(inputs[index].id)))); for (int index = 0; index < outputGroups.Length; index++) foreach (GraphEdgeRecord edge in outputGroups[index]) - candidate.AddEdge(new( - new(Guid.NewGuid().ToString("N")), - new(call.id, new(outputs[index].id)), - edge.input)); - + candidate.AddEdge(new(new(Guid.NewGuid().ToString("N")), new(call.id, new(outputs[index].id)), edge.input)); ShaderCanvasGroup[] groups = GroupShaderNodes.Read(documents, candidate).Select(group => { group.nodes = group.nodes.Where(id => !selected.Contains(new(id))).ToArray(); return group; }).Where(static group => group.nodes.Length != 0).ToArray(); - candidate.SetMetadata(GroupShaderNodes.C_GROUPS, - ShaderGraphDocument.Encode(groups, documents.serialization, documents.context)); + candidate.SetMetadata(GroupShaderNodes.C_GROUPS, ShaderGraphDocument.Encode(groups, documents.serialization, documents.context)); controller.ReplaceDocument(candidate, "Collapse Shader Subgraph"); transaction.Commit(); draft.canvas.SelectNodes([call.id]); @@ -346,9 +322,12 @@ private static ShaderGraphNodePortDefinition Port( string id, ShaderSourceType type, bool required - ) - => new() { id = id, type = ShaderGraphType.Capture(type), required = required }; - + ) => new() + { + id = id, + type = ShaderGraphType.Capture(type), + required = required + }; private static string Unique( string candidate, string fallback, @@ -366,12 +345,11 @@ private static string SuggestedName( ShaderEditorDocuments.Draft draft, HashSet selected ) { - ShaderCanvasGroup? group = GroupShaderNodes.Read(documents, documents.Controller(draft).document) - .FirstOrDefault(value => value.nodes.Length == selected.Count - && value.nodes.All(id => selected.Contains(new(id)))); - return group is ShaderCanvasGroup exact && !string.IsNullOrWhiteSpace(exact.title) - ? exact.title - : "Shader Node"; + ShaderCanvasGroup? group = GroupShaderNodes.Read( + documents, + documents.Controller(draft).document + ).FirstOrDefault(value => value.nodes.Length == selected.Count && value.nodes.All(id => selected.Contains(new(id)))); + return group is ShaderCanvasGroup exact && !string.IsNullOrWhiteSpace(exact.title) ? exact.title : "Shader Node"; } private static AssetPath UniquePath( @@ -395,13 +373,14 @@ ShaderEditorDocuments documents [EditorAction("shader/reveal-source", ShaderEditorCanvas.C_AREA)] internal sealed class RevealShaderFunction( ShaderEditorDocuments documents, - AssetEditorModule browser + AssetBrowserModule browser ) : ShaderSelectionAction(documents) { /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; + /// /// Evaluates whether the requested change can be applied to the current generation. /// @@ -411,33 +390,31 @@ AssetEditorModule browser /// /// The validated editor action state that represents the completed operation. /// -protected override EditorActionState Query(EditorActionContext context) + protected override EditorActionState Query(EditorActionContext context) { if (!base.Query(context).isEnabled) return EditorActionState.disabled; var draft = documents.Open(context.target); - string? definition = draft.canvas.selectedNodes.Count == 1 - ? documents.Controller(draft).document.FindNode(draft.canvas.selectedNodes.First())?.definitionId - : null; - return definition is "inno.shader.source" or ShaderGraphNodes.callDefinitionId - ? EditorActionState.enabled - : EditorActionState.disabled; + string? definition = draft.canvas.selectedNodes.Count == 1 ? documents.Controller(draft).document.FindNode(draft.canvas.selectedNodes.First())?.definitionId : null; + return definition is "inno.shader.source" or ShaderGraphNodes.callDefinitionId ? EditorActionState.enabled : EditorActionState.disabled; } + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); GraphNodeRecord node = documents.Controller(draft).document.FindNode(draft.canvas.selectedNodes.First())!; Reveal(documents, browser, ShaderGraphDocument.Read(node, "sourceId", Guid.Empty, documents.serialization, documents.context)); } + internal static void Reveal( ShaderEditorDocuments documents, - AssetEditorModule browser, + AssetBrowserModule browser, Guid sourceId ) { if (!documents.assets.TryGetInfo(sourceId, out AssetInfo? info) || info is null) @@ -454,24 +431,25 @@ Guid sourceId [EditorAction("shader/reveal-shader", ShaderEditorCanvas.C_AREA)] internal sealed class RevealShaderAsset( ShaderEditorDocuments documents, - AssetEditorModule browser + AssetBrowserModule browser ) : ShaderSelectionAction(documents) { /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); string parent = Path.GetDirectoryName(draft.path.localPath)?.Replace('\\', '/') ?? string.Empty; @@ -489,41 +467,43 @@ internal sealed class ShowShaderDiagnostics(ShaderEditorDocuments documents) : S /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) => documents.ShowCheck(documents.Open(context.target)); + protected override void Execute(EditorActionContext context) => documents.ShowCheck(documents.Open(context.target)); } [EditorAction("shader/copy-to-project", ShaderEditorCanvas.C_AREA)] internal sealed class CopyShaderToProject( ShaderEditorDocuments documents, - AssetEditorModule browser + AssetBrowserModule browser ) : ShaderSelectionAction(documents) { /// /// Gets whether writes is active for the current instance. /// -protected override bool writes => false; + protected override bool writes => false; /// /// Gets whether needs selection is active for the current instance. /// -protected override bool needsSelection => false; + protected override bool needsSelection => false; + /// /// Executes the prepared operation and publishes only a completed result. /// /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void Execute(EditorActionContext context) + protected override void Execute(EditorActionContext context) { var draft = documents.Open(context.target); AssetSourceMount mount = documents.assets.sourceMounts.Single(value => value.id == AssetSourceId.project); @@ -531,8 +511,9 @@ protected override void Execute(EditorActionContext context) AssetPath destination = AssetPath.Project(name + ".ishader"); for (int index = 2; File.Exists(mount.Resolve(destination.localPath)) || File.Exists(mount.Resolve(destination.localPath) + ".imeta"); index++) destination = AssetPath.Project(name + " " + index + ".ishader"); - AssetFileEntry entry = browser.CreateSource(destination, GraphDocumentCodec.Encode(documents.Controller(draft).document, documents.serialization)); - browser.BeginCreatedSourceRename(entry); + AssetFileEntry entry = browser.edits.CreateSource(destination, GraphDocumentCodec.Encode(documents.Controller(draft).document, documents.serialization)); + _ = context.interactions.For("panel/asset.file-browser", entry).Select(); + _ = context.interactions.For("panel/asset.file-browser", entry).Execute("file-browser/rename"); context.interactions.OpenPanel("rendering.shader-editor"); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Presentation/ShaderCheckModal.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Presentation/ShaderCheckModal.cs index b288d7f55..e590b3d71 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Presentation/ShaderCheckModal.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Presentation/ShaderCheckModal.cs @@ -1,3 +1,4 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using Inno.Core.Diagnostics; @@ -8,7 +9,6 @@ using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - /// /// Displays transient draft-check progress and publishes completed diagnostics to the shared Console. /// @@ -20,11 +20,10 @@ internal sealed class ShaderCheckModal(ShaderEditorDocuments documents) : Editor { private const string C_DIAGNOSTIC_GROUP = "Shader Check"; private string m_status = "Checking the current Shader draft…"; - /// /// Gets whether this value is visible. /// -public override bool isVisible => documents.TryGetCheckDraft(out _); + public override bool isVisible => documents.TryGetCheckDraft(out _); /// /// Renders this feature using the current editor presentation context. @@ -32,7 +31,7 @@ internal sealed class ShaderCheckModal(ShaderEditorDocuments documents) : Editor /// /// The operation scope that provides state, services, and ownership boundaries. /// -protected override void OnDraw(EditorContext context) + protected override void OnDraw(EditorContext context) { _ = context; if (documents.TryGetCheckDraft(out ShaderEditorDocuments.Draft? draft)) @@ -45,16 +44,14 @@ protected override void OnDraw(EditorContext context) else { Publish(draft, snapshot); - m_status = snapshot.state == EditorShaderCompilationState.Succeeded - ? "Shader check completed." - : "Shader check failed. See Console diagnostics."; + m_status = snapshot.state == EditorShaderCompilationState.Succeeded ? "Shader check completed." : "Shader check failed. See Console diagnostics."; documents.CloseCheck(); } } // Default modal policy is shared with script compilation: the same fixed width, // centered placement, blocking behavior, fade timing and content-sized height. - ImGuiWidget.WrappedText(m_status); + EditorUI.current.widgets.WrappedText(m_status); } private static void Publish( @@ -65,24 +62,20 @@ EditorShaderDraftCompilationSnapshot snapshot for (int index = 0; index < snapshot.diagnostics.Count; index++) { ShaderDiagnostic shader = snapshot.diagnostics[index]; - DiagnosticLocation? location = shader.location is ShaderSourceLocation source - ? new(source.assetPath, source.line, source.column) - : null; + DiagnosticLocation? location = shader.location is ShaderSourceLocation source ? new(source.assetPath, source.line, source.column) : null; diagnostics.Add(shader.severity switch { DiagnosticSeverity.Info => Diagnostic.Info(shader.code, shader.message, location), DiagnosticSeverity.Warning => Diagnostic.Warning(shader.code, shader.message, location), DiagnosticSeverity.Error => Diagnostic.Error(shader.code, shader.message, location), - _ => Diagnostic.Error(shader.code, shader.message, location) - }); + _ => Diagnostic.Error(shader.code, shader.message, location)}); } if (diagnostics.Count == 0) { - diagnostics.Add(snapshot.state == EditorShaderCompilationState.Succeeded - ? Diagnostic.Info("SHADER_CHECK_SUCCEEDED", "Shader check completed without diagnostics.") - : Diagnostic.Error("SHADER_CHECK_FAILED", "Shader check failed without a compiler diagnostic.")); + diagnostics.Add(snapshot.state == EditorShaderCompilationState.Succeeded ? Diagnostic.Info("SHADER_CHECK_SUCCEEDED", "Shader check completed without diagnostics.") : Diagnostic.Error("SHADER_CHECK_FAILED", "Shader check failed without a compiler diagnostic.")); } + Diagnostics.Set(draft.id, C_DIAGNOSTIC_GROUP, diagnostics, draft.path.ToString()); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/ShaderEditorPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/ShaderEditorPanel.cs index 04372735b..f958f61a8 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/ShaderEditorPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/ShaderEditorPanel.cs @@ -1,4 +1,4 @@ -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; using Inno.Assets; @@ -8,11 +8,10 @@ using Inno.Editor.Shaders; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Rendering; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; - [EditorPanel("rendering.shader-editor", "Shader Editor", order: 230, menuPath: "Authoring")] internal sealed class ShaderEditorPanel(ShaderEditorDocuments documents) : EditorPanel { @@ -28,6 +27,7 @@ internal sealed class ShaderEditorPanel(ShaderEditorDocuments documents) : Edito /// Gets the preferred initial window size in logical editor units. /// public override Vector2 initialSize => new(960f, 640f); + /// /// Draws this feature using the current editor presentation context. /// @@ -40,24 +40,31 @@ protected override void OnDraw(EditorContext context) if (documents.interactions.selection.selectedTarget is ShaderAsset shader) _ = documents.assets.TryGetFileSystemEntry(shader.assetPath, out entry); if (documents.interactions.selection.selectedTarget is ShaderInspectionSelection selection - && documents.assets.TryGetInfo(selection.assetId, out AssetInfo? info) && info is not null) + && documents.assets.TryGetInfo( + selection.assetId, + out AssetInfo? info + ) + && info is not null) _ = documents.assets.TryGetFileSystemEntry(info.assetPath, out entry); if (entry is null || entry.isDirectory || !entry.assetPath.localPath.EndsWith(".ishader", StringComparison.OrdinalIgnoreCase)) { ShaderEditorCanvas.DrawHeader(documents, null); - ImGuiWidget.CenteredWrappedText( + EditorUI.current.widgets.CenteredWrappedText( "Select a Shader (.ishader) in the File Browser.", - Vector2.Max(Vector2.One, NativeImGui.GetContentRegionAvail()), - new Vector2(40f, 28f)); + Vector2.Max(Vector2.One, Ui.GetContentRegionAvailable()), + new Vector2(40f, 28f) + ); return; } + try { new ShaderEditorCanvas(documents, documents.Open(entry)).Draw(); } - catch (Exception failure) when ((failure is System.IO.IOException or InvalidOperationException or FormatException) && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) + catch (Exception failure) when ((failure is System.IO.IOException or InvalidOperationException or FormatException) + && Inno.Core.Execution.RetirementPendingException.Find(failure) is null) { - EditorWidget.WrappedText(failure.Message); + EditorUI.current.widgets.WrappedText(failure.Message); } } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Stats/Inno.Editor.Panel.Stats.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Stats/Inno.Editor.Panel.Stats.csproj index 12de0d3dc..eff0d64ca 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Stats/Inno.Editor.Panel.Stats.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Stats/Inno.Editor.Panel.Stats.csproj @@ -11,12 +11,6 @@ - - diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs index 63623af64..8023e09fc 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs @@ -1,15 +1,12 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Collections.Generic; using System.Linq; - using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Stats; - /// /// Shows lightweight runtime metrics. /// @@ -17,11 +14,10 @@ namespace Inno.Editor.Panel.Stats; internal sealed class StatsPanel : EditorPanel { private readonly FrameStatisticsSampler m_statistics = new(); - /// /// Gets whether use window padding is active for the current instance. /// -public override bool useWindowPadding => false; + public override bool useWindowPadding => false; /// /// Creates the panel. @@ -39,26 +35,20 @@ internal StatsPanel() protected override void OnDraw(EditorContext context) { m_statistics.Update(context.frame.totalTime, context.frame.deltaTime); - - DrawGroup("Frame Timing", - [ - ("Time", $"{context.frame.totalTime:F2} s"), - ("Delta", $"{m_statistics.deltaTime * 1000f:F2} ms"), - ("FPS", $"{m_statistics.framesPerSecond:F1}") - ]); + DrawGroup( + "Frame Timing", + [("Time", $"{context.frame.totalTime:F2} s"), ("Delta", $"{m_statistics.deltaTime * 1000f:F2} ms"), ("FPS", $"{m_statistics.framesPerSecond:F1}")] + ); IReadOnlyList statistics = context.statistics.GetSnapshot(); if (statistics.Count == 0) { - EditorWidget.Hint("No feature statistics were published for the current frame."); + EditorUI.current.widgets.Hint("No feature statistics were published for the current frame."); return; } - foreach (IGrouping group in statistics.GroupBy( - static statistic => statistic.groupId)) + foreach (IGrouping group in statistics.GroupBy(static statistic => statistic.groupId)) { - DrawGroup( - group.First().groupName, - group.Select(static statistic => (statistic.label, statistic.value))); + DrawGroup(group.First().groupName, group.Select(static statistic => (statistic.label, statistic.value))); } } @@ -66,46 +56,38 @@ private static void DrawGroup( string title, IEnumerable<(string label, string value)> values ) { - EditorWidget.CollectionSectionHeader(title); - - System.Numerics.Vector2 padding = EditorWidget.style.inspectorSectionPadding; - ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp - | ImGuiTableFlags.RowBg - | ImGuiTableFlags.NoSavedSettings - | ImGuiTableFlags.PadOuterX; - NativeImGui.PushStyleVar( - (int)(ImGuiStyleVar.CellPadding), - new System.Numerics.Vector2( - padding.X, - EditorWidget.style.statisticRowPadding)); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableRowBg), EditorPalette.collectionRow); - NativeImGui.PushStyleColor((int)(ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); + EditorUI.current.widgets.CollectionSectionHeader(title); + System.Numerics.Vector2 padding = EditorUI.current.widgets.style.inspectorSectionPadding; + ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.PadOuterX; + Ui.PushStyleVar((ImGuiStyleVar.CellPadding), new System.Numerics.Vector2(padding.X, EditorUI.current.widgets.style.statisticRowPadding)); + Ui.PushStyleColor((ImGuiCol.TableRowBg), EditorPalette.collectionRow); + Ui.PushStyleColor((ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); try { - if (!NativeImGui.BeginTable($"##stats_{title}", 2, (int)(flags), default(System.Numerics.Vector2), 0f)) + if (!Ui.BeginTable($"##stats_{title}", 2, flags, default(System.Numerics.Vector2), 0f)) return; try { - NativeImGui.TableSetupColumn("##label", (int)(ImGuiTableColumnFlags.WidthStretch), 0.42f); - NativeImGui.TableSetupColumn("##value", (int)(ImGuiTableColumnFlags.WidthStretch), 0.58f); - foreach ((string label, string value) in values) + Ui.TableSetupColumn("##label", (ImGuiTableColumnFlags.WidthStretch), 0.42f); + Ui.TableSetupColumn("##value", (ImGuiTableColumnFlags.WidthStretch), 0.58f); + foreach ((string label, string value)in values) { - NativeImGui.TableNextRow(); - NativeImGui.TableSetColumnIndex(0); - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); - NativeImGui.TableSetColumnIndex(1); - EditorWidget.WrappedText(value); + Ui.TableNextRow(); + Ui.TableSetColumnIndex(0); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); + Ui.TableSetColumnIndex(1); + EditorUI.current.widgets.WrappedText(value); } } finally { - NativeImGui.EndTable(); + Ui.EndTable(); } } finally { - NativeImGui.PopStyleColor(2); - NativeImGui.PopStyleVar(); + Ui.PopStyleColor(2); + Ui.PopStyleVar(); } } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs similarity index 94% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs rename to src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs index 342a8253f..ea598c569 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconAttribute.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconAttribute.cs @@ -1,7 +1,8 @@ +using Inno.Editor.Assets; +using Inno.Editor.Core; using System; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets.ImGui; /// /// Declares the icon used by the Asset Browser for an imported asset type or source extension. /// @@ -9,14 +10,7 @@ namespace Inno.Editor.Panel.FileBrowser; /// Apply one or more declarations to a container type. The container has no runtime responsibility; /// it only keeps related icon declarations together so the type catalog can discover them. /// -[AttributeUsage( - AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Interface | - AttributeTargets.Enum | - AttributeTargets.Delegate, - AllowMultiple = true, - Inherited = false)] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)] public sealed class AssetIconAttribute : Attribute { /// @@ -83,24 +77,20 @@ public AssetIconAttribute( /// an extension declaration. /// public Type? assetType { get; } - /// /// Gets the normalized source extension represented by this declaration, or /// for a type declaration. /// public string? extension { get; } - /// /// Gets the Editor icon glyph to render. /// public string icon { get; } - /// /// Gets whether a type declaration may also represent derived asset types. Extension declarations /// always return . /// public bool useForChildren { get; } - /// /// Gets the tie-breaking priority after target specificity. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconRegistry.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconRegistry.cs similarity index 60% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconRegistry.cs rename to src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconRegistry.cs index 8f8dbad20..8428eff48 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/AssetIconRegistry.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/AssetIconRegistry.cs @@ -1,22 +1,20 @@ +using Inno.Editor.Assets; +using Inno.Editor.Core; using System; using System.Collections.Generic; using System.Linq; - using Inno.Assets; using Inno.Extensibility.Types; using Inno.Editor.Settings; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets.ImGui; internal sealed class AssetIconRegistry : TypeRegistry { private readonly EditorSettings m_settings; - internal AssetIconRegistry( EditorSettings settings, TypeCatalog types - ) - : base(types) + ) : base(types) { m_settings = settings ?? throw new ArgumentNullException(nameof(settings)); } @@ -35,10 +33,12 @@ out string icon } catch (ArgumentException) { - // Extension-defined attributes may still contain a literal glyph. + // Extension-defined attributes may still contain a literal glyph. } + return true; } + return false; } @@ -55,35 +55,31 @@ protected override Snapshot Build(TypeCacheSnapshot types) { var typeRegistrations = new List(); var extensionRegistrations = new List(); - foreach (Type declarationType in types.types - .Select(typeRef => typeRef.Resolve(types)) - .OrderBy(static type => type.Assembly.GetName().Name, StringComparer.Ordinal) - .ThenBy(static type => type.FullName, StringComparer.Ordinal)) + foreach (Type declarationType in types.types.Select(typeRef => typeRef.Resolve(types)).OrderBy( + static type => type.Assembly.GetName().Name, + StringComparer.Ordinal + ).ThenBy( + static type => type.FullName, + StringComparer.Ordinal + )) { - foreach (AssetIconAttribute attribute in declarationType - .GetCustomAttributes(typeof(AssetIconAttribute), inherit: false) - .Cast() - .OrderBy(static value => value.assetType?.FullName ?? value.extension, StringComparer.Ordinal) - .ThenBy(static value => value.priority)) + foreach (AssetIconAttribute attribute in declarationType.GetCustomAttributes( + typeof(AssetIconAttribute), + inherit: false + ).Cast().OrderBy( + static value => value.assetType?.FullName ?? value.extension, + StringComparer.Ordinal + ).ThenBy(static value => value.priority)) { if (attribute.assetType is Type assetType) { ValidateType(attribute, declarationType, assetType, typeRegistrations); - typeRegistrations.Add(new TypeRegistration( - assetType, - attribute.useForChildren, - attribute.priority, - declarationType, - attribute.icon)); + typeRegistrations.Add(new TypeRegistration(assetType, attribute.useForChildren, attribute.priority, declarationType, attribute.icon)); } else if (attribute.extension is string extension) { ValidateExtension(attribute, declarationType, extension, extensionRegistrations); - extensionRegistrations.Add(new ExtensionRegistration( - extension, - attribute.priority, - declarationType, - attribute.icon)); + extensionRegistrations.Add(new ExtensionRegistration(extension, attribute.priority, declarationType, attribute.icon)); } } } @@ -101,15 +97,12 @@ out string icon { foreach (TypeRegistration registration in current.typeRegistrations) { - if (!AssetTypeDistance.TryGet(assetType, registration.assetType, out int distance) || - distance > 0 && !registration.useForChildren) + if (!EditorTypeSpecificity.TryGet(assetType, registration.assetType, out int distance) || distance > 0 && !registration.useForChildren) { continue; } - if (best is null || - distance < bestDistance || - distance == bestDistance && registration.priority > best.priority) + if (best is null || distance < bestDistance || distance == bestDistance && registration.priority > best.priority) { best = registration; bestDistance = distance; @@ -130,9 +123,10 @@ out string icon { if (!relativePath.EndsWith(registration.extension, StringComparison.OrdinalIgnoreCase)) continue; - if (best is null || - registration.extension.Length > best.extension.Length || - registration.extension.Length == best.extension.Length && registration.priority > best.priority) + if (best is null + || registration.extension.Length > best.extension.Length + || registration.extension.Length == best.extension.Length + && registration.priority > best.priority) { best = registration; } @@ -150,20 +144,12 @@ IReadOnlyList registrations ) { if (!typeof(AssetObject).IsAssignableFrom(assetType)) { - throw new InvalidOperationException( - $"Asset icon declaration on '{declarationType.FullName}' targets " + - $"'{assetType.FullName}', " + - $"which does not derive from '{typeof(AssetObject).FullName}'."); + throw new InvalidOperationException($"Asset icon declaration on '{declarationType.FullName}' targets " + $"'{assetType.FullName}', " + $"which does not derive from '{typeof(AssetObject).FullName}'."); } - if (registrations.Any(value => - value.assetType == assetType && - value.useForChildren == attribute.useForChildren && - value.priority == attribute.priority)) + if (registrations.Any(value => value.assetType == assetType && value.useForChildren == attribute.useForChildren && value.priority == attribute.priority)) { - throw new InvalidOperationException( - $"Asset icon registration for '{assetType.FullName}' conflicts at priority " + - $"{attribute.priority}."); + throw new InvalidOperationException($"Asset icon registration for '{assetType.FullName}' conflicts at priority " + $"{attribute.priority}."); } } @@ -173,13 +159,9 @@ private static void ValidateExtension( string extension, IReadOnlyList registrations ) { - if (registrations.Any(value => - string.Equals(value.extension, extension, StringComparison.OrdinalIgnoreCase) && - value.priority == attribute.priority)) + if (registrations.Any(value => string.Equals(value.extension, extension, StringComparison.OrdinalIgnoreCase) && value.priority == attribute.priority)) { - throw new InvalidOperationException( - $"Asset icon declaration on '{declarationType.FullName}' for extension '{extension}' " + - $"conflicts at priority {attribute.priority}."); + throw new InvalidOperationException($"Asset icon declaration on '{declarationType.FullName}' for extension '{extension}' " + $"conflicts at priority {attribute.priority}."); } } @@ -187,7 +169,6 @@ internal sealed record Snapshot( TypeRegistration[] typeRegistrations, ExtensionRegistration[] extensionRegistrations ); - internal sealed record TypeRegistration( Type assetType, bool useForChildren, @@ -195,7 +176,6 @@ internal sealed record TypeRegistration( Type declarationType, string icon ); - internal sealed record ExtensionRegistration( string extension, int priority, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/BuiltInAssetIcons.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/BuiltInAssetIcons.cs similarity index 88% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/BuiltInAssetIcons.cs rename to src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/BuiltInAssetIcons.cs index 29156b80a..5079b01f9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Icons/BuiltInAssetIcons.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Icons/BuiltInAssetIcons.cs @@ -1,8 +1,9 @@ -using Inno.Adapter.Presentation.ImGui; +using Inno.Editor.ImGui; +using Inno.Editor.Assets; +using Inno.Editor.Core; using Inno.Editor.Settings; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets.ImGui; [AssetIcon(".txt", ImGuiIcon.FileLines)] [AssetIcon(".json", ImGuiIcon.FileLines)] [AssetIcon(".yaml", ImGuiIcon.FileLines)] diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj new file mode 100644 index 000000000..ca910bbac --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj @@ -0,0 +1,28 @@ + + + net10.0 + 14.0 + disable + enable + true + $(WarningsAsErrors);CS1572;CS1573;CS1591 + Editor + + + + + + + + + + + + + + + + + + + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetSelectionInspectionDrawer.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs similarity index 76% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetSelectionInspectionDrawer.cs rename to src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs index 5b81ce30a..e64a28d5a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/AssetEditors/AssetSelectionInspectionDrawer.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs @@ -1,21 +1,18 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Assets; using System; - using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.Inspection; using Inno.Editor.Interactions; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Adapter.Presentation.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; - -namespace Inno.Editor.Panel.FileBrowser; +namespace Inno.Editor.Assets.ImGui; [InspectionDrawer(typeof(AssetFileEntry))] internal sealed class AssetSelectionInspectionDrawer : InspectionDrawer { - private readonly AssetEditorModule m_assets; - + private readonly AssetBrowserModule m_assets; /// /// Creates an Asset source drawer that shares the Asset Browser presentation registry. /// @@ -25,7 +22,7 @@ internal sealed class AssetSelectionInspectionDrawer : InspectionDrawer /// Thrown when is . /// - internal AssetSelectionInspectionDrawer(AssetEditorModule assets) + internal AssetSelectionInspectionDrawer(AssetBrowserModule assets) { m_assets = assets ?? throw new ArgumentNullException(nameof(assets)); } @@ -51,7 +48,6 @@ protected override string GetIcon( InspectionDrawContext context, AssetFileEntry target ) => m_assets.GetIcon(target); - /// /// Binds a caller-visible label to the current inspection target. /// @@ -67,9 +63,7 @@ AssetFileEntry target protected override (string name, Action? setter) BindName( InspectionDrawContext context, AssetFileEntry target - ) - => (target.nameWithoutExtension, null); - + ) => (target.nameWithoutExtension, null); /// /// Renders the header presentation for the current editor frame. /// @@ -82,9 +76,7 @@ AssetFileEntry target protected override void DrawHeader( InspectionDrawContext context, AssetFileEntry target - ) - => EditorWidget.ColoredText(EditorPalette.assetBreadcrumbText, target.assetPath.ToString()); - + ) => EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, target.assetPath.ToString()); /// /// Renders the value presentation for the current editor frame. /// @@ -98,22 +90,16 @@ protected override void Draw( InspectionDrawContext context, AssetFileEntry entry ) { - if (!EditorWidget.SectionHeader( - "Asset", - "This view describes the selected source entry. Structured assets may replace it with a dedicated authoring drawer.")) + if (!EditorUI.current.widgets.SectionHeader("Asset", "This view describes the selected source entry. Structured assets may replace it with a dedicated authoring drawer.")) { return; } DrawMetadata( "Type", - m_assets.IsPluginRoot(entry) - ? "IPlugin" - : entry.isDirectory && AssetSample.HasSampleDirectoryName(entry.assetPath) - ? "ISample" - : entry.isDirectory - ? "Directory" - : "File"); + m_assets.edits.IsPluginRoot(entry) ? "IPlugin" : entry.isDirectory + && AssetSample.HasSampleDirectoryName(entry.assetPath) ? "ISample" : entry.isDirectory ? "Directory" : "File" + ); if (!entry.isDirectory) { DrawMetadata("Extension", string.IsNullOrEmpty(entry.extension) ? "" : entry.extension); @@ -124,9 +110,6 @@ private static void DrawMetadata( string label, string value ) { - EditorWidget.PropertyRow( - "asset.metadata." + label, - label, - () => EditorWidget.Hint(value)); + EditorUI.current.widgets.PropertyRow("asset.metadata." + label, label, () => EditorUI.current.widgets.Hint(value)); } } diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs new file mode 100644 index 000000000..bfbe2a40e --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserModule.cs @@ -0,0 +1,150 @@ +using Inno.Editor.ImGui; +using System; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Editor.Assets; +using Inno.Editor.Core; +using Inno.Editor.Inspection; +using Inno.Editor.Interactions; +using Inno.Editor.Settings; +using Inno.Extensibility.Types; + +namespace Inno.Editor.Assets.ImGui; +/// +/// Owns asset navigation and icon presentation independently from source mutations. +/// +[EditorModule("asset-browser", order: 100)] +public sealed class AssetBrowserModule : EditorModule, IInspectionIconProvider +{ + private readonly EditorSettings m_settings; + private readonly AssetIconRegistry m_icons; + /// + /// Creates the asset presentation module for the current editor generation. + /// + /// + /// The independent authoritative asset editing service. + /// + /// + /// The editor selection and action owner. + /// + /// + /// The editor settings that supply semantic icon choices. + /// + /// + /// The active extension type catalog. + /// + /// + /// A required owner is null. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public AssetBrowserModule( + AssetEditingModule edits, + EditorInteractions interactions, + EditorSettings settings, + TypeCatalog types + ) { + this.edits = edits ?? throw new ArgumentNullException(nameof(edits)); + m_settings = settings ?? throw new ArgumentNullException(nameof(settings)); + m_icons = new AssetIconRegistry(settings, types); + browser = new AssetBrowserState(interactions, edits.pipeline); + } + + /// + /// Gets the source editing service; this module does not implement source transactions. + /// + public AssetEditingModule edits { get; } + /// + /// Gets navigation state; object selection belongs to the shared interaction owner. + /// + public AssetBrowserState browser { get; } + /// + /// Gets the configured folder glyph used by asset navigation. + /// + public string folderIcon => m_settings.Get("Editor/Appearance/Icons/Folder").GetAsString("value", Inno.Editor.ImGui.ImGuiIcon.Folder)!; + + /// + /// Captures an immutable snapshot of the current observable state. + /// + /// + /// The lifecycle or domain state applied by this operation. + /// + protected override void Capture(EditorState state) + { + state.Set("root", browser.root.ToString()); + state.Set("assetsDirectory", browser.GetDirectory(AssetBrowserRoot.Assets)); + state.Set("pluginsDirectory", browser.GetDirectory(AssetBrowserRoot.Plugins)); + } + + /// + /// Restores the supplied snapshot while preserving current invariants. + /// + /// + /// The lifecycle or domain state applied by this operation. + /// + protected override void Restore(EditorState state) + { + AssetBrowserRoot restoredRoot = Enum.TryParse(state.Get("root", string.Empty), out AssetBrowserRoot parsedRoot) ? parsedRoot : AssetBrowserRoot.Assets; + string assetsDirectory = RestoreDirectory(AssetBrowserRoot.Assets, state.Get("assetsDirectory", string.Empty)); + string pluginsDirectory = RestoreDirectory(AssetBrowserRoot.Plugins, state.Get("pluginsDirectory", string.Empty)); + browser.Restore(restoredRoot, assetsDirectory, pluginsDirectory); + } + + private string RestoreDirectory( + AssetBrowserRoot root, + string path + ) { + string directory = AssetPath.Parse(path).ToString(); + if (string.IsNullOrEmpty(directory)) + return string.Empty; + AssetPath isolated = AssetPath.Parse(directory); + if ((root == AssetBrowserRoot.Assets) != (isolated.source == AssetSourceId.project)) + return string.Empty; + while (!IsAvailableDirectory(directory)) + { + string parent = ParentDirectory(directory); + if (string.Equals(parent, directory, StringComparison.Ordinal)) + return string.Empty; + directory = parent; + } + + return directory; + } + + private bool IsAvailableDirectory(string path) => edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(path), out AssetFileEntry entry) && entry.isDirectory; + /// + /// Resolves the presentation icon registered for an asset type or source extension. + /// + /// + /// The source entry whose presentation icon should be resolved. + /// + /// + /// The most specific registered icon, or the built-in directory or file icon when no + /// registration matches the entry. + /// + /// + /// Thrown when is . + /// + public string GetIcon(AssetFileEntry entry) + { + ArgumentNullException.ThrowIfNull(entry); + if (entry.isDirectory) + return folderIcon; + _ = edits.pipeline.TryGetAssetType(entry.assetPath, out Type? assetType); + if (m_icons.TryResolve(assetType, entry.assetPath.ToString(), out string icon)) + { + return icon; + } + + return m_settings.Get("Editor/Appearance/Icons/File").GetAsString("value", Inno.Editor.ImGui.ImGuiIcon.File)!; + } + + /// + protected override void OnDispose() => m_icons.Dispose(); + private static string ParentDirectory(string value) + { + AssetPath path = AssetPath.Parse(value); + string local = path.localPath; + int slash = local.LastIndexOf('/'); + return new AssetPath(path.source, slash < 0 ? string.Empty : local[..slash]).ToString(); + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs similarity index 71% rename from src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs rename to src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs index 35c008cd8..edc2981d1 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/AssetBrowserState.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs @@ -1,15 +1,13 @@ - +using Inno.Editor.Assets; using System; using System.Linq; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Editor.Inspection; -namespace Inno.Editor.Panel.FileBrowser; - +namespace Inno.Editor.Assets.ImGui; /// /// Identifies the authoring or installed-content root displayed by the Asset Browser. /// @@ -19,7 +17,6 @@ public enum AssetBrowserRoot /// The writable project Assets authoring root. /// Assets, - /// /// The read-only Plugins installation root. /// @@ -35,7 +32,6 @@ public sealed class AssetBrowserState private readonly AssetPipeline m_assets; private string m_assetsDirectory = string.Empty; private string m_pluginsDirectory = string.Empty; - /// /// Creates Asset Browser navigation state. /// @@ -60,14 +56,10 @@ AssetPipeline assets /// Gets the root currently displayed by the Asset Browser. /// public AssetBrowserRoot root { get; private set; } - /// /// Gets the current directory inside . An empty value identifies that root's overview. /// - public string currentDirectory => root == AssetBrowserRoot.Assets - ? m_assetsDirectory - : m_pluginsDirectory; - + public string currentDirectory => root == AssetBrowserRoot.Assets ? m_assetsDirectory : m_pluginsDirectory; /// /// Gets the most recently visited writable project directory, independently of the displayed root. /// @@ -88,41 +80,39 @@ AssetPipeline assets public string? GetSelectedPath(EditorContext context) { ArgumentNullException.ThrowIfNull(context); - if (m_interactions.selection.selectedTarget is AssetInspectionSelection group - && m_assets.TryGetInfo(group.primaryAssetId, out AssetInfo? info) && info is not null) - return info.assetPath.ToString(); return (m_interactions.selection.selectedTarget as Inno.Assets.Pipeline.AssetFileEntry)?.assetPath.ToString(); } - internal bool IsSelected( + /// + /// Checks whether the source entry belongs to the current asset selection. + /// + /// + /// The current editor callback context. + /// + /// + /// The indexed source identity to inspect. + /// + /// + /// True when the entry is selected; otherwise false. + /// + public bool IsSelected( EditorContext context, AssetFileEntry entry - ) - => m_interactions.selection.selectedTarget is AssetInspectionSelection group - ? m_assets.TryGetInfo(entry.assetPath, out AssetInfo? info) && info is not null && group.assetIds.Contains(info.persistentId) - : string.Equals(GetSelectedPath(context), entry.assetPath.ToString(), StringComparison.Ordinal); - - internal void ToggleSelection( + ) => m_interactions.selection.Contains(entry); + /// + /// Adds or removes an asset from the current selection without retaining runtime asset objects. + /// + /// + /// The current editor callback context. + /// + /// + /// The indexed source identity to toggle. + /// + public void ToggleSelection( EditorContext context, AssetFileEntry entry ) { - if (entry.isDirectory || !m_assets.TryGetInfo(entry.assetPath, out AssetInfo? info) || info is null) - { - Select(context, entry.assetPath.ToString()); - return; - } - var ids = m_interactions.selection.selectedTarget is AssetInspectionSelection group ? group.assetIds.ToList() : new System.Collections.Generic.List(); - if (ids.Count == 0 && m_interactions.selection.selectedTarget is AssetFileEntry selected && !selected.isDirectory - && m_assets.TryGetInfo(selected.assetPath, out AssetInfo? previous) && previous is not null) - ids.Add(previous.persistentId); - if (!ids.Remove(info.persistentId)) - ids.Add(info.persistentId); - if (ids.Count == 0) - Select(context, null); - else if (ids.Count == 1 && m_assets.TryGetInfo(ids[0], out AssetInfo? remaining) && remaining is not null) - Select(context, remaining.assetPath.ToString()); - else - m_interactions.SetSelection(new AssetInspectionSelection(ids)); + m_interactions.ToggleSelection(entry); } /// @@ -132,7 +122,6 @@ AssetFileEntry entry /// The authoring or installed-content root to display. /// public void SetRoot(AssetBrowserRoot value) => root = value; - /// /// Sets the current Asset Browser directory and infers its root from the isolated source identity. /// @@ -149,9 +138,7 @@ public void SetCurrentDirectory(string relativePath) } AssetPath path = AssetPath.Parse(normalized); - AssetBrowserRoot targetRoot = path.source == AssetSourceId.project - ? AssetBrowserRoot.Assets - : AssetBrowserRoot.Plugins; + AssetBrowserRoot targetRoot = path.source == AssetSourceId.project ? AssetBrowserRoot.Assets : AssetBrowserRoot.Plugins; SetDirectory(targetRoot, normalized); root = targetRoot; } @@ -176,21 +163,25 @@ public void Select( object? target = null; if (!string.IsNullOrWhiteSpace(relativePath)) { - if (m_assets.TryGetFileSystemEntry( - Inno.Assets.AssetPath.Parse(Normalize(relativePath)), - out Inno.Assets.Pipeline.AssetFileEntry entry)) + if (m_assets.TryGetFileSystemEntry(Inno.Assets.AssetPath.Parse(Normalize(relativePath)), out Inno.Assets.Pipeline.AssetFileEntry entry)) { target = entry; } } - _ = m_interactions.For(FileBrowserInteractionIds.C_AREA, target).Select(); - } - internal string GetDirectory(AssetBrowserRoot targetRoot) - => targetRoot == AssetBrowserRoot.Assets - ? m_assetsDirectory - : m_pluginsDirectory; + m_interactions.SetSelection(target); + } + /// + /// Gets the last visited isolated directory for a navigation root. + /// + /// + /// The root whose independent navigation position is requested. + /// + /// + /// The source-relative directory, or an empty string for the root. + /// + public string GetDirectory(AssetBrowserRoot targetRoot) => targetRoot == AssetBrowserRoot.Assets ? m_assetsDirectory : m_pluginsDirectory; internal void Restore( AssetBrowserRoot restoredRoot, string assetsDirectory, @@ -201,7 +192,19 @@ string pluginsDirectory root = restoredRoot; } - internal void SetLocation( + /// + /// Changes the displayed source root and its isolated navigation directory. + /// + /// + /// The navigation root that owns the requested directory. + /// + /// + /// The isolated source-relative directory; empty selects the root. + /// + /// + /// The directory belongs to a different source root. + /// + public void SetLocation( AssetBrowserRoot targetRoot, string directory ) { @@ -212,9 +215,7 @@ string directory bool isProject = path.source == AssetSourceId.project; if ((targetRoot == AssetBrowserRoot.Assets) != isProject) { - throw new ArgumentException( - $"Directory '{directory}' does not belong to the '{targetRoot}' root.", - nameof(directory)); + throw new ArgumentException($"Directory '{directory}' does not belong to the '{targetRoot}' root.", nameof(directory)); } } diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs new file mode 100644 index 000000000..930055ea3 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs @@ -0,0 +1,7 @@ +using Inno.Editor.Assets.ImGui; +using Inno.Scripting.Api; + +[assembly: ScriptingApiNamespace("InnoEditor.Assets", "Inno.Editor.Assets.ImGui", ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetBrowserRoot), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetBrowserState), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetIconAttribute), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs new file mode 100644 index 000000000..3a2a94147 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs @@ -0,0 +1,168 @@ +using System; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using CanvasDrawing = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Graph.ImGui; +/// +/// Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. +/// +public readonly struct GraphCanvasView +{ + private readonly GraphCanvasState m_state; + private readonly Vector2 m_origin; + private readonly Vector2 m_size; + /// + /// Creates a frame-local graph presenter over navigation owned by its document. + /// + /// + /// The existing neutral navigation and selection state. + /// + /// + /// The screen-space canvas origin. + /// + /// + /// The finite nonnegative canvas extent. + /// + /// + /// The canvas geometry is invalid. + /// + public GraphCanvasView( + GraphCanvasState state, + Vector2 origin, + Vector2 size + ) { + ArgumentNullException.ThrowIfNull(state); + if (!float.IsFinite(origin.X) || !float.IsFinite(origin.Y)) + throw new ArgumentOutOfRangeException(nameof(origin)); + if (!float.IsFinite(size.X) || !float.IsFinite(size.Y) || size.X < 0f || size.Y < 0f) + throw new ArgumentOutOfRangeException(nameof(size)); + m_state = state; + m_origin = origin; + m_size = size; + } + + /// + /// Projects a graph-space location into the current screen rectangle. + /// + /// + /// The domain-independent graph coordinate. + /// + /// + /// The corresponding screen coordinate. + /// + public Vector2 ToScreen(GraphPosition point) => m_origin + new Vector2(m_state.pan.x, m_state.pan.y) + new Vector2(point.x, point.y) * m_state.zoom; + /// + /// Converts a screen location back into graph coordinates. + /// + /// + /// The screen coordinate to project. + /// + /// + /// The graph coordinate under the pointer. + /// + public GraphPosition ToGraph(Vector2 point) => new((point.X - m_origin.X - m_state.pan.x) / m_state.zoom, (point.Y - m_origin.Y - m_state.pan.y) / m_state.zoom); + /// + /// Draws a bounded graph-space grid through the current UI drawing context. + /// + /// + /// The packed presentation color. + /// + /// + /// Positive graph-space grid spacing. + /// + /// + /// Spacing is nonpositive or nonfinite. + /// + public void DrawGrid( + uint color, + float spacing = 32f + ) { + if (!float.IsFinite(spacing) || spacing <= 0f) + throw new ArgumentOutOfRangeException(nameof(spacing)); + float step = MathF.Max(4f, spacing * m_state.zoom); + for (float x = ((m_state.pan.x % step) + step) % step; x < m_size.X; x += step) + CanvasDrawing.DrawLine(m_origin + new Vector2(x, 0f), m_origin + new Vector2(x, m_size.Y), color); + for (float y = ((m_state.pan.y % step) + step) % step; y < m_size.Y; y += step) + CanvasDrawing.DrawLine(m_origin + new Vector2(0f, y), m_origin + new Vector2(m_size.X, y), color); + } + + /// + /// Draws a horizontal-tangent connection using the same curve used for hit testing. + /// + /// + /// The output endpoint in screen space. + /// + /// + /// The input endpoint in screen space. + /// + /// + /// The packed connection color. + /// + /// + /// The positive screen-space stroke width. + /// + public void DrawConnection( + Vector2 output, + Vector2 input, + uint color, + float thickness = 2f + ) { + float tangent = Tangent(output, input); + CanvasDrawing.DrawCubicCurve(output, output + new Vector2(tangent, 0f), input - new Vector2(tangent, 0f), input, color, thickness); + } + + /// + /// Checks pointer distance against the displayed cubic connection without any UI or document access. + /// + /// + /// The screen-space pointer location. + /// + /// + /// The displayed output endpoint. + /// + /// + /// The displayed input endpoint. + /// + /// + /// Nonnegative screen-space acceptance radius. + /// + /// + /// True when the pointer is within the curve's hit area, including its endpoints. + /// + /// + /// Radius is negative or nonfinite. + /// + public static bool HitConnection( + Vector2 point, + Vector2 output, + Vector2 input, + float radius = 6f + ) { + if (!float.IsFinite(radius) || radius < 0f) + throw new ArgumentOutOfRangeException(nameof(radius)); + float tangent = Tangent(output, input); + Vector2 first = output + new Vector2(tangent, 0f); + Vector2 second = input - new Vector2(tangent, 0f); + Vector2 previous = output; + for (int segment = 1; segment <= 32; segment++) + { + float t = segment / 32f; + float s = 1f - t; + Vector2 next = s * s * s * output + 3f * s * s * t * first + 3f * s * t * t * second + t * t * t * input; + Vector2 line = next - previous; + float projection = Math.Clamp(Vector2.Dot(point - previous, line) / MathF.Max(0.001f, line.LengthSquared()), 0f, 1f); + if (Vector2.DistanceSquared(point, previous + projection * line) <= radius * radius) + return true; + previous = next; + } + + return false; + } + + private static float Tangent( + Vector2 output, + Vector2 input + ) => MathF.Max(36f, MathF.Abs(input.X - output.X) * 0.45f); +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj new file mode 100644 index 000000000..5b4ed9def --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj @@ -0,0 +1,23 @@ + + + net10.0 + 14.0 + disable + enable + true + $(WarningsAsErrors);CS1572;CS1573;CS1591 + Editor + + + + + + + + + + + + + + diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs new file mode 100644 index 000000000..db9164f54 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs @@ -0,0 +1,5 @@ +using Inno.Scripting.Api; +using Inno.Editor.Graph.ImGui; + +[assembly: ScriptingApiNamespace("InnoEditor.Graph", "Inno.Editor.Graph.ImGui", ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasView), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs new file mode 100644 index 000000000..c7ac2ca73 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Controls.cs @@ -0,0 +1,138 @@ +using System.Numerics; +using Inno.Native.ImGui; +using RawImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Provides pointer-free controls within the active Editor UI session. +/// +public static partial class ImGui +{ + /// + /// Submits an explicitly drawn control interaction rectangle. + /// + /// + /// The visible label and optional stable ID suffix. + /// + /// + /// The explicit two-dimensional extent. + /// + /// + /// The explicit control and interaction options. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool InvisibleButton( + string label, + Vector2 size, + ImGuiButtonFlags flags = ImGuiButtonFlags.None + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.InvisibleButton(label, size, (int)flags); + } + + /// + /// Draws a menu item with explicit availability and checked state. + /// + /// + /// The visible label and optional stable ID suffix. + /// + /// + /// A display label only; execution remains with the Action router. + /// + /// + /// Whether the item is currently selected. + /// + /// + /// Whether the control can accept input. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool MenuItem( + string label, + string shortcut = "", + bool selected = false, + bool enabled = true + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.MenuItem(label, shortcut, selected, enabled); + } + + /// + /// Edits an integer within explicit inclusive bounds. + /// + /// + /// The visible label and optional stable ID suffix. + /// + /// + /// The caller-owned value updated only after an accepted edit. + /// + /// + /// The inclusive lower bound or upper-left corner. + /// + /// + /// The inclusive upper bound or lower-right corner. + /// + /// + /// The numeric display format. + /// + /// + /// The explicit control and interaction options. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool SliderInt( + string label, + ref int value, + int minimum, + int maximum, + string format = "%d", + ImGuiSliderFlags flags = ImGuiSliderFlags.None + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.SliderInt(label, ref value, minimum, maximum, format, (int)flags); + } + + /// + /// Edits an integer by pointer drag with explicit speed and optional bounds. + /// + /// + /// The visible label and optional stable ID suffix. + /// + /// + /// The caller-owned value updated only after an accepted edit. + /// + /// + /// The pointer movement multiplier. + /// + /// + /// The inclusive lower bound or upper-left corner. + /// + /// + /// The inclusive upper bound or lower-right corner. + /// + /// + /// The numeric display format. + /// + /// + /// The explicit control and interaction options. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool DragInt( + string label, + ref int value, + float speed = 1f, + int minimum = 0, + int maximum = 0, + string format = "%d", + ImGuiSliderFlags flags = ImGuiSliderFlags.None + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.DragInt(label, ref value, speed, minimum, maximum, format, (int)flags); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs new file mode 100644 index 000000000..0345dfa35 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Drawing.cs @@ -0,0 +1,70 @@ +using System.Numerics; +using Inno.Native.ImGui; +using RawImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Provides frame-bounded drawing access and copied presentation values. +/// +public static partial class ImGui +{ + /// + /// Gets the current style color including the active alpha. + /// + /// + /// The packed RGBA color or semantic style slot. + /// + /// + /// A copied value from the active presentation context. + /// + public static uint GetStyleColor(ImGuiCol color) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetColorU32((int)color); + } + + /// + /// Derives a control identity from the current window and ID stack. + /// + /// + /// The visible label and optional stable ID suffix. + /// + /// + /// A copied value from the active presentation context. + /// + public static uint GetControlId(string label) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetID(label); + } + + /// + /// Clips subsequent controls and drawing to a screen rectangle. + /// + /// + /// The inclusive lower bound or upper-left corner. + /// + /// + /// The inclusive upper bound or lower-right corner. + /// + /// + /// Whether to intersect the enclosing clip. + /// + public static void PushClipRect( + Vector2 minimum, + Vector2 maximum, + bool intersect + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushClipRect(minimum, maximum, intersect); + } + + /// + /// Restores the enclosing control clip after a matching push. + /// + public static void PopClipRect() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PopClipRect(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs new file mode 100644 index 000000000..bf8a53a6a --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Input.cs @@ -0,0 +1,278 @@ +using System.Numerics; +using Inno.Native.ImGui; +using RawImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Reads copied input values from the active Editor UI session. +/// +public static partial class ImGui +{ + /// + /// Gets the current pointer position in screen-space UI units. + /// + /// + /// A copied value from the active presentation context. + /// + public static Vector2 GetMousePos() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetMousePos(); + } + + /// + /// Checks a pointer-button press in the current frame. + /// + /// + /// The pointer button to inspect. + /// + /// + /// Whether a held key or button may produce repeats. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsMouseClicked( + ImGuiMouseButton button, + bool repeat = false + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsMouseClicked((int)button, repeat); + } + + /// + /// Checks a pointer-button release in the current frame. + /// + /// + /// The pointer button to inspect. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsMouseReleased(ImGuiMouseButton button) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsMouseReleased((int)button); + } + + /// + /// Checks the backend-classified double-click event. + /// + /// + /// The pointer button to inspect. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsMouseDoubleClicked(ImGuiMouseButton button) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsMouseDoubleClicked((int)button); + } + + /// + /// Checks whether a presentation key remains held. + /// + /// + /// The presentation key to inspect. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsKeyDown(ImGuiKey key) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsKeyDown((Inno.Native.ImGui.ImGuiKey)key); + } + + /// + /// Checks a presentation-key press with explicit repeat policy. + /// + /// + /// The presentation key to inspect. + /// + /// + /// Whether a held key or button may produce repeats. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsKeyPressed( + ImGuiKey key, + bool repeat = true + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsKeyPressed((Inno.Native.ImGui.ImGuiKey)key, repeat); + } + + /// + /// Resets the consumed pointer-drag distance. + /// + /// + /// The pointer button to inspect. + /// + public static void ResetMouseDragDelta(ImGuiMouseButton button) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.ResetMouseDragDelta((int)button); + } + + /// + /// Requests the semantic cursor for the hovered control. + /// + /// + /// The semantic pointer shape. + /// + public static void SetMouseCursor(ImGuiMouseCursor cursor) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetMouseCursor((int)cursor); + } + + /// + /// Checks pointer containment in a screen rectangle with optional window clipping. + /// + /// + /// The inclusive lower bound or upper-left corner. + /// + /// + /// The inclusive upper bound or lower-right corner. + /// + /// + /// Whether the current clipping rectangle participates. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsMouseHoveringRect( + Vector2 minimum, + Vector2 maximum, + bool clip = true + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsMouseHoveringRect(minimum, maximum, clip); + } + + /// + /// Checks window hover under the declared interaction policy. + /// + /// + /// The explicit control and interaction options. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsWindowHovered(ImGuiHoveredFlags flags = ImGuiHoveredFlags.None) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsWindowHovered((int)flags); + } + + /// + /// Checks keyboard focus within the declared window scope. + /// + /// + /// The explicit control and interaction options. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsWindowFocused(ImGuiFocusedFlags flags = ImGuiFocusedFlags.None) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsWindowFocused((int)flags); + } + + /// + /// Checks whether a submitted control currently owns hover. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsAnyItemHovered() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsAnyItemHovered(); + } + + /// + /// Checks whether a submitted control owns an active interaction. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsAnyItemActive() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsAnyItemActive(); + } + + /// + /// Checks the first frame of the last control interaction. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsItemActivated() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsItemActivated(); + } + + /// + /// Checks the completion frame of an edited control. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool IsItemDeactivatedAfterEdit() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsItemDeactivatedAfterEdit(); + } + + /// + /// Requests focus for a relative control position. + /// + /// + /// The relative focus offset from the next submitted control. + /// + public static void SetKeyboardFocusHere(int offset = 0) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetKeyboardFocusHere(offset); + } + + /// + /// Chooses the last item as the default selection in an appearing popup. + /// + public static void SetItemDefaultFocus() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetItemDefaultFocus(); + } + + /// + /// Copies literal text through the host clipboard adapter. + /// + /// + /// Literal text; no formatting interpretation is applied. + /// + public static void SetClipboardText(string text) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetClipboardText(text); + } + + /// + /// Gets the current native frame identity for transient diagnostics. + /// + /// + /// A copied value from the active presentation context. + /// + public static int GetFrameCount() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetFrameCount(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs new file mode 100644 index 000000000..0143391de --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Layout.cs @@ -0,0 +1,282 @@ +using System.Numerics; +using Inno.Native.ImGui; +using RawImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Provides scoped layout operations without exposing native context storage. +/// +public static partial class ImGui +{ + /// + /// Places the next control at a screen-space location. + /// + /// + /// The requested coordinate in UI units. + /// + public static void SetCursorScreenPosition(Vector2 position) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetCursorScreenPos(position); + } + + /// + /// Changes only the local horizontal layout cursor. + /// + /// + /// The requested coordinate in UI units. + /// + public static void SetCursorPosX(float position) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetCursorPosX(position); + } + + /// + /// Changes only the local vertical layout cursor. + /// + /// + /// The requested coordinate in UI units. + /// + public static void SetCursorPosY(float position) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetCursorPosY(position); + } + + /// + /// Gets the local horizontal layout cursor. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetCursorPosX() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetCursorPosX(); + } + + /// + /// Gets the local vertical layout cursor. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetCursorPosY() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetCursorPosY(); + } + + /// + /// Gets the current window screen-space origin. + /// + /// + /// A copied value from the active presentation context. + /// + public static Vector2 GetWindowPos() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetWindowPos(); + } + + /// + /// Gets the current window size in UI units. + /// + /// + /// A copied value from the active presentation context. + /// + public static Vector2 GetWindowSize() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetWindowSize(); + } + + /// + /// Gets the active font size in UI units. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetFontSize() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetFontSize(); + } + + /// + /// Gets the standard control height including frame padding. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetFrameHeight() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetFrameHeight(); + } + + /// + /// Gets the active font line height without spacing. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetTextLineHeight() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetTextLineHeight(); + } + + /// + /// Gets the standard control height and following vertical spacing. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetFrameHeightWithSpacing() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetFrameHeightWithSpacing(); + } + + /// + /// Gets the native viewport scale for the current window. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetWindowDpiScale() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetWindowDpiScale(); + } + + /// + /// Aligns the next text baseline with framed controls. + /// + public static void AlignTextToFramePadding() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.AlignTextToFramePadding(); + } + + /// + /// Allows explicitly layered controls to share the next item bounds. + /// + public static void SetNextItemAllowOverlap() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetNextItemAllowOverlap(); + } + + /// + /// Requests a size for the next window under an explicit layout condition. + /// + /// + /// The explicit two-dimensional extent. + /// + /// + /// The condition controlling when the request is applied. + /// + public static void SetNextWindowSize( + Vector2 size, + ImGuiCond condition = ImGuiCond.None + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetNextWindowSize(size, (int)condition); + } + + /// + /// Declares virtual content size for the next scrollable window. + /// + /// + /// The explicit two-dimensional extent. + /// + public static void SetNextWindowContentSize(Vector2 size) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetNextWindowContentSize(size); + } + + /// + /// Indents subsequent controls by an explicit amount or the standard step. + /// + /// + /// The requested layout distance. + /// + public static void Indent(float amount = 0f) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.Indent(amount); + } + + /// + /// Restores a matching indentation step. + /// + /// + /// The requested layout distance. + /// + public static void Unindent(float amount = 0f) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.Unindent(amount); + } + + /// + /// Restricts subsequent text wrapping to a local horizontal boundary. + /// + /// + /// The requested coordinate in UI units. + /// + public static void PushTextWrapPos(float position = 0f) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushTextWrapPos(position); + } + + /// + /// Restores the enclosing text-wrap boundary. + /// + public static void PopTextWrapPos() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PopTextWrapPos(); + } + + /// + /// Gets the current vertical scroll position. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetScrollY() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetScrollY(); + } + + /// + /// Gets the current vertical scroll range. + /// + /// + /// A copied value from the active presentation context. + /// + public static float GetScrollMaxY() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetScrollMaxY(); + } + + /// + /// Scrolls the most recently submitted control into the requested relative position. + /// + /// + /// The relative alignment from zero through one. + /// + public static void SetScrollHereY(float ratio = 0.5f) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetScrollHereY(ratio); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs new file mode 100644 index 000000000..8ecf5a21e --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/ImGui.Tables.cs @@ -0,0 +1,46 @@ +using System.Numerics; +using Inno.Native.ImGui; +using RawImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Presents tables through the active Editor UI context. +/// +public static partial class ImGui +{ + /// + /// Selects a declared table column for subsequent controls. + /// + /// + /// The zero-based item index. + /// + /// + /// True when the documented control event or condition is present; otherwise false. + /// + public static bool TableSetColumnIndex(int index) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.TableSetColumnIndex(index); + } + + /// + /// Sets a table row or cell background through the current table scope. + /// + /// + /// The table background role. + /// + /// + /// The packed RGBA color or semantic style slot. + /// + /// + /// The target column, or -1 for the current row. + /// + public static void TableSetBgColor( + ImGuiTableBgTarget target, + uint color, + int column = -1 + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TableSetBgColor((int)target, color, column); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs new file mode 100644 index 000000000..791a9be6f --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs @@ -0,0 +1,34 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported button flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiButtonFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiButtonFlags.None, + /// + /// React on left mouse button (default) + /// + MouseButtonLeft = (int)Inno.Native.ImGui.ImGuiButtonFlags.MouseButtonLeft, + /// + /// React on right mouse button + /// + MouseButtonRight = (int)Inno.Native.ImGui.ImGuiButtonFlags.MouseButtonRight, + /// + /// React on center mouse button + /// + MouseButtonMiddle = (int)Inno.Native.ImGui.ImGuiButtonFlags.MouseButtonMiddle, + /// + /// Selects the mouse button mask presentation behavior. + /// + MouseButtonMask = (int)Inno.Native.ImGui.ImGuiButtonFlags.MouseButtonMask, + /// + /// InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default. + /// + EnableNav = (int)Inno.Native.ImGui.ImGuiButtonFlags.EnableNav, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs new file mode 100644 index 000000000..47cb3cc84 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiChildFlags.cs @@ -0,0 +1,50 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported child flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiChildFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiChildFlags.None, + /// + /// Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) + /// + Borders = (int)Inno.Native.ImGui.ImGuiChildFlags.Borders, + /// + /// Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) + /// + AlwaysUseWindowPadding = (int)Inno.Native.ImGui.ImGuiChildFlags.AlwaysUseWindowPadding, + /// + /// Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) + /// + ResizeX = (int)Inno.Native.ImGui.ImGuiChildFlags.ResizeX, + /// + /// Allow resize from bottom border (layout direction). " + /// + ResizeY = (int)Inno.Native.ImGui.ImGuiChildFlags.ResizeY, + /// + /// Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above. + /// + AutoResizeX = (int)Inno.Native.ImGui.ImGuiChildFlags.AutoResizeX, + /// + /// Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above. + /// + AutoResizeY = (int)Inno.Native.ImGui.ImGuiChildFlags.AutoResizeY, + /// + /// Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. + /// + AlwaysAutoResize = (int)Inno.Native.ImGui.ImGuiChildFlags.AlwaysAutoResize, + /// + /// Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. + /// + FrameStyle = (int)Inno.Native.ImGui.ImGuiChildFlags.FrameStyle, + /// + /// Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows. + /// + NavFlattened = (int)Inno.Native.ImGui.ImGuiChildFlags.NavFlattened, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs new file mode 100644 index 000000000..eeee906c8 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCol.cs @@ -0,0 +1,261 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported col choices at the Editor presentation boundary. +/// +public enum ImGuiCol : int +{ + /// + /// Selects the text presentation behavior. + /// + Text = (int)Inno.Native.ImGui.ImGuiCol.Text, + /// + /// Selects the text disabled presentation behavior. + /// + TextDisabled = (int)Inno.Native.ImGui.ImGuiCol.TextDisabled, + /// + /// Selects the window bg presentation behavior. + /// + WindowBg = (int)Inno.Native.ImGui.ImGuiCol.WindowBg, + /// + /// Selects the child bg presentation behavior. + /// + ChildBg = (int)Inno.Native.ImGui.ImGuiCol.ChildBg, + /// + /// Selects the popup bg presentation behavior. + /// + PopupBg = (int)Inno.Native.ImGui.ImGuiCol.PopupBg, + /// + /// Selects the border presentation behavior. + /// + Border = (int)Inno.Native.ImGui.ImGuiCol.Border, + /// + /// Selects the border shadow presentation behavior. + /// + BorderShadow = (int)Inno.Native.ImGui.ImGuiCol.BorderShadow, + /// + /// Selects the frame bg presentation behavior. + /// + FrameBg = (int)Inno.Native.ImGui.ImGuiCol.FrameBg, + /// + /// Selects the frame bg hovered presentation behavior. + /// + FrameBgHovered = (int)Inno.Native.ImGui.ImGuiCol.FrameBgHovered, + /// + /// Selects the frame bg active presentation behavior. + /// + FrameBgActive = (int)Inno.Native.ImGui.ImGuiCol.FrameBgActive, + /// + /// Selects the title bg presentation behavior. + /// + TitleBg = (int)Inno.Native.ImGui.ImGuiCol.TitleBg, + /// + /// Selects the title bg active presentation behavior. + /// + TitleBgActive = (int)Inno.Native.ImGui.ImGuiCol.TitleBgActive, + /// + /// Selects the title bg collapsed presentation behavior. + /// + TitleBgCollapsed = (int)Inno.Native.ImGui.ImGuiCol.TitleBgCollapsed, + /// + /// Selects the menu bar bg presentation behavior. + /// + MenuBarBg = (int)Inno.Native.ImGui.ImGuiCol.MenuBarBg, + /// + /// Selects the scrollbar bg presentation behavior. + /// + ScrollbarBg = (int)Inno.Native.ImGui.ImGuiCol.ScrollbarBg, + /// + /// Selects the scrollbar grab presentation behavior. + /// + ScrollbarGrab = (int)Inno.Native.ImGui.ImGuiCol.ScrollbarGrab, + /// + /// Selects the scrollbar grab hovered presentation behavior. + /// + ScrollbarGrabHovered = (int)Inno.Native.ImGui.ImGuiCol.ScrollbarGrabHovered, + /// + /// Selects the scrollbar grab active presentation behavior. + /// + ScrollbarGrabActive = (int)Inno.Native.ImGui.ImGuiCol.ScrollbarGrabActive, + /// + /// Selects the check mark presentation behavior. + /// + CheckMark = (int)Inno.Native.ImGui.ImGuiCol.CheckMark, + /// + /// Selects the slider grab presentation behavior. + /// + SliderGrab = (int)Inno.Native.ImGui.ImGuiCol.SliderGrab, + /// + /// Selects the slider grab active presentation behavior. + /// + SliderGrabActive = (int)Inno.Native.ImGui.ImGuiCol.SliderGrabActive, + /// + /// Selects the button presentation behavior. + /// + Button = (int)Inno.Native.ImGui.ImGuiCol.Button, + /// + /// Selects the button hovered presentation behavior. + /// + ButtonHovered = (int)Inno.Native.ImGui.ImGuiCol.ButtonHovered, + /// + /// Selects the button active presentation behavior. + /// + ButtonActive = (int)Inno.Native.ImGui.ImGuiCol.ButtonActive, + /// + /// Selects the header presentation behavior. + /// + Header = (int)Inno.Native.ImGui.ImGuiCol.Header, + /// + /// Selects the header hovered presentation behavior. + /// + HeaderHovered = (int)Inno.Native.ImGui.ImGuiCol.HeaderHovered, + /// + /// Selects the header active presentation behavior. + /// + HeaderActive = (int)Inno.Native.ImGui.ImGuiCol.HeaderActive, + /// + /// Selects the separator presentation behavior. + /// + Separator = (int)Inno.Native.ImGui.ImGuiCol.Separator, + /// + /// Selects the separator hovered presentation behavior. + /// + SeparatorHovered = (int)Inno.Native.ImGui.ImGuiCol.SeparatorHovered, + /// + /// Selects the separator active presentation behavior. + /// + SeparatorActive = (int)Inno.Native.ImGui.ImGuiCol.SeparatorActive, + /// + /// Selects the resize grip presentation behavior. + /// + ResizeGrip = (int)Inno.Native.ImGui.ImGuiCol.ResizeGrip, + /// + /// Selects the resize grip hovered presentation behavior. + /// + ResizeGripHovered = (int)Inno.Native.ImGui.ImGuiCol.ResizeGripHovered, + /// + /// Selects the resize grip active presentation behavior. + /// + ResizeGripActive = (int)Inno.Native.ImGui.ImGuiCol.ResizeGripActive, + /// + /// Selects the input text cursor presentation behavior. + /// + InputTextCursor = (int)Inno.Native.ImGui.ImGuiCol.InputTextCursor, + /// + /// Selects the tab hovered presentation behavior. + /// + TabHovered = (int)Inno.Native.ImGui.ImGuiCol.TabHovered, + /// + /// Selects the tab presentation behavior. + /// + Tab = (int)Inno.Native.ImGui.ImGuiCol.Tab, + /// + /// Selects the tab selected presentation behavior. + /// + TabSelected = (int)Inno.Native.ImGui.ImGuiCol.TabSelected, + /// + /// Selects the tab selected overline presentation behavior. + /// + TabSelectedOverline = (int)Inno.Native.ImGui.ImGuiCol.TabSelectedOverline, + /// + /// Selects the tab dimmed presentation behavior. + /// + TabDimmed = (int)Inno.Native.ImGui.ImGuiCol.TabDimmed, + /// + /// Selects the tab dimmed selected presentation behavior. + /// + TabDimmedSelected = (int)Inno.Native.ImGui.ImGuiCol.TabDimmedSelected, + /// + /// Selects the tab dimmed selected overline presentation behavior. + /// + TabDimmedSelectedOverline = (int)Inno.Native.ImGui.ImGuiCol.TabDimmedSelectedOverline, + /// + /// Selects the docking preview presentation behavior. + /// + DockingPreview = (int)Inno.Native.ImGui.ImGuiCol.DockingPreview, + /// + /// Selects the docking empty bg presentation behavior. + /// + DockingEmptyBg = (int)Inno.Native.ImGui.ImGuiCol.DockingEmptyBg, + /// + /// Selects the plot lines presentation behavior. + /// + PlotLines = (int)Inno.Native.ImGui.ImGuiCol.PlotLines, + /// + /// Selects the plot lines hovered presentation behavior. + /// + PlotLinesHovered = (int)Inno.Native.ImGui.ImGuiCol.PlotLinesHovered, + /// + /// Selects the plot histogram presentation behavior. + /// + PlotHistogram = (int)Inno.Native.ImGui.ImGuiCol.PlotHistogram, + /// + /// Selects the plot histogram hovered presentation behavior. + /// + PlotHistogramHovered = (int)Inno.Native.ImGui.ImGuiCol.PlotHistogramHovered, + /// + /// Selects the table header bg presentation behavior. + /// + TableHeaderBg = (int)Inno.Native.ImGui.ImGuiCol.TableHeaderBg, + /// + /// Selects the table border strong presentation behavior. + /// + TableBorderStrong = (int)Inno.Native.ImGui.ImGuiCol.TableBorderStrong, + /// + /// Selects the table border light presentation behavior. + /// + TableBorderLight = (int)Inno.Native.ImGui.ImGuiCol.TableBorderLight, + /// + /// Selects the table row bg presentation behavior. + /// + TableRowBg = (int)Inno.Native.ImGui.ImGuiCol.TableRowBg, + /// + /// Selects the table row bg alt presentation behavior. + /// + TableRowBgAlt = (int)Inno.Native.ImGui.ImGuiCol.TableRowBgAlt, + /// + /// Selects the text link presentation behavior. + /// + TextLink = (int)Inno.Native.ImGui.ImGuiCol.TextLink, + /// + /// Selects the text selected bg presentation behavior. + /// + TextSelectedBg = (int)Inno.Native.ImGui.ImGuiCol.TextSelectedBg, + /// + /// Selects the tree lines presentation behavior. + /// + TreeLines = (int)Inno.Native.ImGui.ImGuiCol.TreeLines, + /// + /// Selects the drag drop target presentation behavior. + /// + DragDropTarget = (int)Inno.Native.ImGui.ImGuiCol.DragDropTarget, + /// + /// Selects the drag drop target bg presentation behavior. + /// + DragDropTargetBg = (int)Inno.Native.ImGui.ImGuiCol.DragDropTargetBg, + /// + /// Selects the unsaved marker presentation behavior. + /// + UnsavedMarker = (int)Inno.Native.ImGui.ImGuiCol.UnsavedMarker, + /// + /// Selects the nav cursor presentation behavior. + /// + NavCursor = (int)Inno.Native.ImGui.ImGuiCol.NavCursor, + /// + /// Selects the nav windowing highlight presentation behavior. + /// + NavWindowingHighlight = (int)Inno.Native.ImGui.ImGuiCol.NavWindowingHighlight, + /// + /// Selects the nav windowing dim bg presentation behavior. + /// + NavWindowingDimBg = (int)Inno.Native.ImGui.ImGuiCol.NavWindowingDimBg, + /// + /// Selects the modal window dim bg presentation behavior. + /// + ModalWindowDimBg = (int)Inno.Native.ImGui.ImGuiCol.ModalWindowDimBg, + /// + /// Marks the exclusive end of the valid option range. + /// + Count = (int)Inno.Native.ImGui.ImGuiCol.Count, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs new file mode 100644 index 000000000..6db099ef5 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiColorEditFlags.cs @@ -0,0 +1,138 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported color edit flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiColorEditFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiColorEditFlags.None, + /// + /// // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). + /// + NoAlpha = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoAlpha, + /// + /// // ColorEdit: disable picker when clicking on color square. + /// + NoPicker = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoPicker, + /// + /// // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. + /// + NoOptions = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoOptions, + /// + /// // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs) + /// + NoSmallPreview = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoSmallPreview, + /// + /// // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square). + /// + NoInputs = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoInputs, + /// + /// // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. + /// + NoTooltip = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoTooltip, + /// + /// // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). + /// + NoLabel = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoLabel, + /// + /// // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. + /// + NoSidePreview = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoSidePreview, + /// + /// // ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source. + /// + NoDragDrop = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoDragDrop, + /// + /// // ColorButton: disable border (which is enforced by default) + /// + NoBorder = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoBorder, + /// + /// // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0. + /// + NoColorMarkers = (int)Inno.Native.ImGui.ImGuiColorEditFlags.NoColorMarkers, + /// + /// // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. + /// + AlphaOpaque = (int)Inno.Native.ImGui.ImGuiColorEditFlags.AlphaOpaque, + /// + /// // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. + /// + AlphaNoBg = (int)Inno.Native.ImGui.ImGuiColorEditFlags.AlphaNoBg, + /// + /// // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. + /// + AlphaPreviewHalf = (int)Inno.Native.ImGui.ImGuiColorEditFlags.AlphaPreviewHalf, + /// + /// // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. + /// + AlphaBar = (int)Inno.Native.ImGui.ImGuiColorEditFlags.AlphaBar, + /// + /// Selects the hdr presentation behavior. + /// + Hdr = (int)Inno.Native.ImGui.ImGuiColorEditFlags.Hdr, + /// + /// Selects the display rgb presentation behavior. + /// + DisplayRgb = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DisplayRgb, + /// + /// Selects the display hsv presentation behavior. + /// + DisplayHsv = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DisplayHsv, + /// + /// // " + /// + DisplayHex = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DisplayHex, + /// + /// // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. + /// + Uint8 = (int)Inno.Native.ImGui.ImGuiColorEditFlags.Uint8, + /// + /// // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. + /// + Float = (int)Inno.Native.ImGui.ImGuiColorEditFlags.Float, + /// + /// // ColorPicker: bar for Hue, rectangle for Sat/Value. + /// + PickerHueBar = (int)Inno.Native.ImGui.ImGuiColorEditFlags.PickerHueBar, + /// + /// // ColorPicker: wheel for Hue, triangle for Sat/Value. + /// + PickerHueWheel = (int)Inno.Native.ImGui.ImGuiColorEditFlags.PickerHueWheel, + /// + /// Selects the input rgb presentation behavior. + /// + InputRgb = (int)Inno.Native.ImGui.ImGuiColorEditFlags.InputRgb, + /// + /// Selects the input hsv presentation behavior. + /// + InputHsv = (int)Inno.Native.ImGui.ImGuiColorEditFlags.InputHsv, + /// + /// Selects the default options presentation behavior. + /// + DefaultOptions = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DefaultOptions, + /// + /// Selects the alpha mask presentation behavior. + /// + AlphaMask = (int)Inno.Native.ImGui.ImGuiColorEditFlags.AlphaMask, + /// + /// Selects the display mask presentation behavior. + /// + DisplayMask = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DisplayMask, + /// + /// Selects the data type mask presentation behavior. + /// + DataTypeMask = (int)Inno.Native.ImGui.ImGuiColorEditFlags.DataTypeMask, + /// + /// Selects the picker mask presentation behavior. + /// + PickerMask = (int)Inno.Native.ImGui.ImGuiColorEditFlags.PickerMask, + /// + /// Selects the input mask presentation behavior. + /// + InputMask = (int)Inno.Native.ImGui.ImGuiColorEditFlags.InputMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs new file mode 100644 index 000000000..535401069 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiComboFlags.cs @@ -0,0 +1,50 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported combo flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiComboFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiComboFlags.None, + /// + /// Align the popup toward the left by default + /// + PopupAlignLeft = (int)Inno.Native.ImGui.ImGuiComboFlags.PopupAlignLeft, + /// + /// Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() + /// + HeightSmall = (int)Inno.Native.ImGui.ImGuiComboFlags.HeightSmall, + /// + /// Max ~8 items visible (default) + /// + HeightRegular = (int)Inno.Native.ImGui.ImGuiComboFlags.HeightRegular, + /// + /// Max ~20 items visible + /// + HeightLarge = (int)Inno.Native.ImGui.ImGuiComboFlags.HeightLarge, + /// + /// As many fitting items as possible + /// + HeightLargest = (int)Inno.Native.ImGui.ImGuiComboFlags.HeightLargest, + /// + /// Display on the preview box without the square arrow button + /// + NoArrowButton = (int)Inno.Native.ImGui.ImGuiComboFlags.NoArrowButton, + /// + /// Display only a square arrow button + /// + NoPreview = (int)Inno.Native.ImGui.ImGuiComboFlags.NoPreview, + /// + /// Width dynamically calculated from preview contents + /// + WidthFitPreview = (int)Inno.Native.ImGui.ImGuiComboFlags.WidthFitPreview, + /// + /// Selects the height mask presentation behavior. + /// + HeightMask = (int)Inno.Native.ImGui.ImGuiComboFlags.HeightMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs new file mode 100644 index 000000000..67cbbf90e --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiCond.cs @@ -0,0 +1,30 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported cond choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiCond : int +{ + /// + /// No condition (always set the variable), same as _Always + /// + None = (int)Inno.Native.ImGui.ImGuiCond.None, + /// + /// No condition (always set the variable), same as _None + /// + Always = (int)Inno.Native.ImGui.ImGuiCond.Always, + /// + /// Set the variable once per runtime session (only the first call will succeed) + /// + Once = (int)Inno.Native.ImGui.ImGuiCond.Once, + /// + /// Set the variable if the object/window has no persistently saved data (no entry in .ini file) + /// + FirstUseEver = (int)Inno.Native.ImGui.ImGuiCond.FirstUseEver, + /// + /// Set the variable if the object/window is appearing after being hidden/inactive (or the first time) + /// + Appearing = (int)Inno.Native.ImGui.ImGuiCond.Appearing, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs new file mode 100644 index 000000000..04bcc642c --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiFocusedFlags.cs @@ -0,0 +1,38 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported focused flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiFocusedFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiFocusedFlags.None, + /// + /// Return true if any children of the window is focused + /// + ChildWindows = (int)Inno.Native.ImGui.ImGuiFocusedFlags.ChildWindows, + /// + /// Test from root window (top most parent of the current hierarchy) + /// + RootWindow = (int)Inno.Native.ImGui.ImGuiFocusedFlags.RootWindow, + /// + /// Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + /// + AnyWindow = (int)Inno.Native.ImGui.ImGuiFocusedFlags.AnyWindow, + /// + /// Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + /// + NoPopupHierarchy = (int)Inno.Native.ImGui.ImGuiFocusedFlags.NoPopupHierarchy, + /// + /// Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + /// + DockHierarchy = (int)Inno.Native.ImGui.ImGuiFocusedFlags.DockHierarchy, + /// + /// Selects the root and child windows presentation behavior. + /// + RootAndChildWindows = (int)Inno.Native.ImGui.ImGuiFocusedFlags.RootAndChildWindows, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs new file mode 100644 index 000000000..0a8025156 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiHoveredFlags.cs @@ -0,0 +1,94 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported hovered flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiHoveredFlags : int +{ + /// + /// Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. + /// + None = (int)Inno.Native.ImGui.ImGuiHoveredFlags.None, + /// + /// IsWindowHovered() only: Return true if any children of the window is hovered + /// + ChildWindows = (int)Inno.Native.ImGui.ImGuiHoveredFlags.ChildWindows, + /// + /// IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) + /// + RootWindow = (int)Inno.Native.ImGui.ImGuiHoveredFlags.RootWindow, + /// + /// IsWindowHovered() only: Return true if any window is hovered + /// + AnyWindow = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AnyWindow, + /// + /// IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + /// + NoPopupHierarchy = (int)Inno.Native.ImGui.ImGuiHoveredFlags.NoPopupHierarchy, + /// + /// IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + /// + DockHierarchy = (int)Inno.Native.ImGui.ImGuiHoveredFlags.DockHierarchy, + /// + /// Return true even if a popup window is normally blocking access to this item/window + /// + AllowWhenBlockedByPopup = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByPopup, + /// + /// Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + /// + AllowWhenBlockedByActiveItem = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, + /// + /// IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item. + /// + AllowWhenOverlappedByItem = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByItem, + /// + /// IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window. + /// + AllowWhenOverlappedByWindow = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenOverlappedByWindow, + /// + /// IsItemHovered() only: Return true even if the item is disabled + /// + AllowWhenDisabled = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenDisabled, + /// + /// IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse + /// + NoNavOverride = (int)Inno.Native.ImGui.ImGuiHoveredFlags.NoNavOverride, + /// + /// Selects the allow when overlapped presentation behavior. + /// + AllowWhenOverlapped = (int)Inno.Native.ImGui.ImGuiHoveredFlags.AllowWhenOverlapped, + /// + /// Selects the rect only presentation behavior. + /// + RectOnly = (int)Inno.Native.ImGui.ImGuiHoveredFlags.RectOnly, + /// + /// Selects the root and child windows presentation behavior. + /// + RootAndChildWindows = (int)Inno.Native.ImGui.ImGuiHoveredFlags.RootAndChildWindows, + /// + /// Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. + /// + ForTooltip = (int)Inno.Native.ImGui.ImGuiHoveredFlags.ForTooltip, + /// + /// Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. + /// + Stationary = (int)Inno.Native.ImGui.ImGuiHoveredFlags.Stationary, + /// + /// IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. + /// + DelayNone = (int)Inno.Native.ImGui.ImGuiHoveredFlags.DelayNone, + /// + /// IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + /// + DelayShort = (int)Inno.Native.ImGui.ImGuiHoveredFlags.DelayShort, + /// + /// IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + /// + DelayNormal = (int)Inno.Native.ImGui.ImGuiHoveredFlags.DelayNormal, + /// + /// IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + /// + NoSharedDelay = (int)Inno.Native.ImGui.ImGuiHoveredFlags.NoSharedDelay, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs new file mode 100644 index 000000000..57a99f283 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiInputTextFlags.cs @@ -0,0 +1,114 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported input text flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiInputTextFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiInputTextFlags.None, + /// + /// Allow 0123456789.+-*/ + /// + CharsDecimal = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CharsDecimal, + /// + /// Allow 0123456789ABCDEFabcdef + /// + CharsHexadecimal = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CharsHexadecimal, + /// + /// Allow 0123456789.+-*/eE (Scientific notation input) + /// + CharsScientific = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CharsScientific, + /// + /// Turn a..z into A..Z + /// + CharsUppercase = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CharsUppercase, + /// + /// Filter out spaces, tabs + /// + CharsNoBlank = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CharsNoBlank, + /// + /// Pressing TAB input a '\t' character into the text field + /// + AllowTabInput = (int)Inno.Native.ImGui.ImGuiInputTextFlags.AllowTabInput, + /// + /// Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! + /// + EnterReturnsTrue = (int)Inno.Native.ImGui.ImGuiInputTextFlags.EnterReturnsTrue, + /// + /// Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) + /// + EscapeClearsAll = (int)Inno.Native.ImGui.ImGuiInputTextFlags.EscapeClearsAll, + /// + /// In multi-line mode, validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). + /// + CtrlEnterForNewLine = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CtrlEnterForNewLine, + /// + /// Read-only mode + /// + ReadOnly = (int)Inno.Native.ImGui.ImGuiInputTextFlags.ReadOnly, + /// + /// Password mode, display all characters as '*', disable copy + /// + Password = (int)Inno.Native.ImGui.ImGuiInputTextFlags.Password, + /// + /// Overwrite mode + /// + AlwaysOverwrite = (int)Inno.Native.ImGui.ImGuiInputTextFlags.AlwaysOverwrite, + /// + /// Select entire text when first taking mouse focus + /// + AutoSelectAll = (int)Inno.Native.ImGui.ImGuiInputTextFlags.AutoSelectAll, + /// + /// InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value. + /// + ParseEmptyRefVal = (int)Inno.Native.ImGui.ImGuiInputTextFlags.ParseEmptyRefVal, + /// + /// InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. + /// + DisplayEmptyRefVal = (int)Inno.Native.ImGui.ImGuiInputTextFlags.DisplayEmptyRefVal, + /// + /// Disable following the cursor horizontally + /// + NoHorizontalScroll = (int)Inno.Native.ImGui.ImGuiInputTextFlags.NoHorizontalScroll, + /// + /// Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). + /// + NoUndoRedo = (int)Inno.Native.ImGui.ImGuiInputTextFlags.NoUndoRedo, + /// + /// When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! + /// + ElideLeft = (int)Inno.Native.ImGui.ImGuiInputTextFlags.ElideLeft, + /// + /// Callback on pressing TAB (for completion handling) + /// + CallbackCompletion = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackCompletion, + /// + /// Callback on pressing Up/Down arrows (for history handling) + /// + CallbackHistory = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackHistory, + /// + /// Callback on each iteration. User code may query cursor position, modify text buffer. + /// + CallbackAlways = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackAlways, + /// + /// Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. + /// + CallbackCharFilter = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackCharFilter, + /// + /// Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) + /// + CallbackResize = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackResize, + /// + /// Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. + /// + CallbackEdit = (int)Inno.Native.ImGui.ImGuiInputTextFlags.CallbackEdit, + /// + /// InputTextMultiline(): word-wrap lines that are too long. + /// + WordWrap = (int)Inno.Native.ImGui.ImGuiInputTextFlags.WordWrap, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs new file mode 100644 index 000000000..0120f2fa9 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiKey.cs @@ -0,0 +1,669 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported key choices at the Editor presentation boundary. +/// +public enum ImGuiKey : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiKey.None, + /// + /// Identifies the NamedKeyBegin presentation key or modifier. + /// + NamedKeyBegin = (int)Inno.Native.ImGui.ImGuiKey.NamedKeyBegin, + /// + /// == ImGuiKey_NamedKey_BEGIN + /// + Tab = (int)Inno.Native.ImGui.ImGuiKey.Tab, + /// + /// Identifies the LeftArrow presentation key or modifier. + /// + LeftArrow = (int)Inno.Native.ImGui.ImGuiKey.LeftArrow, + /// + /// Identifies the RightArrow presentation key or modifier. + /// + RightArrow = (int)Inno.Native.ImGui.ImGuiKey.RightArrow, + /// + /// Identifies the UpArrow presentation key or modifier. + /// + UpArrow = (int)Inno.Native.ImGui.ImGuiKey.UpArrow, + /// + /// Identifies the DownArrow presentation key or modifier. + /// + DownArrow = (int)Inno.Native.ImGui.ImGuiKey.DownArrow, + /// + /// Identifies the PageUp presentation key or modifier. + /// + PageUp = (int)Inno.Native.ImGui.ImGuiKey.PageUp, + /// + /// Identifies the PageDown presentation key or modifier. + /// + PageDown = (int)Inno.Native.ImGui.ImGuiKey.PageDown, + /// + /// Identifies the Home presentation key or modifier. + /// + Home = (int)Inno.Native.ImGui.ImGuiKey.Home, + /// + /// Identifies the End presentation key or modifier. + /// + End = (int)Inno.Native.ImGui.ImGuiKey.End, + /// + /// Identifies the Insert presentation key or modifier. + /// + Insert = (int)Inno.Native.ImGui.ImGuiKey.Insert, + /// + /// Identifies the Delete presentation key or modifier. + /// + Delete = (int)Inno.Native.ImGui.ImGuiKey.Delete, + /// + /// Identifies the Backspace presentation key or modifier. + /// + Backspace = (int)Inno.Native.ImGui.ImGuiKey.Backspace, + /// + /// Identifies the Space presentation key or modifier. + /// + Space = (int)Inno.Native.ImGui.ImGuiKey.Space, + /// + /// Identifies the Enter presentation key or modifier. + /// + Enter = (int)Inno.Native.ImGui.ImGuiKey.Enter, + /// + /// Identifies the Escape presentation key or modifier. + /// + Escape = (int)Inno.Native.ImGui.ImGuiKey.Escape, + /// + /// Identifies the LeftCtrl presentation key or modifier. + /// + LeftCtrl = (int)Inno.Native.ImGui.ImGuiKey.LeftCtrl, + /// + /// Identifies the LeftShift presentation key or modifier. + /// + LeftShift = (int)Inno.Native.ImGui.ImGuiKey.LeftShift, + /// + /// Identifies the LeftAlt presentation key or modifier. + /// + LeftAlt = (int)Inno.Native.ImGui.ImGuiKey.LeftAlt, + /// + /// Identifies the LeftSuper presentation key or modifier. + /// + LeftSuper = (int)Inno.Native.ImGui.ImGuiKey.LeftSuper, + /// + /// Identifies the RightCtrl presentation key or modifier. + /// + RightCtrl = (int)Inno.Native.ImGui.ImGuiKey.RightCtrl, + /// + /// Identifies the RightShift presentation key or modifier. + /// + RightShift = (int)Inno.Native.ImGui.ImGuiKey.RightShift, + /// + /// Identifies the RightAlt presentation key or modifier. + /// + RightAlt = (int)Inno.Native.ImGui.ImGuiKey.RightAlt, + /// + /// Identifies the RightSuper presentation key or modifier. + /// + RightSuper = (int)Inno.Native.ImGui.ImGuiKey.RightSuper, + /// + /// Identifies the Menu presentation key or modifier. + /// + Menu = (int)Inno.Native.ImGui.ImGuiKey.Menu, + /// + /// Identifies the _0 presentation key or modifier. + /// + _0 = (int)Inno.Native.ImGui.ImGuiKey._0, + /// + /// Identifies the _1 presentation key or modifier. + /// + _1 = (int)Inno.Native.ImGui.ImGuiKey._1, + /// + /// Identifies the _2 presentation key or modifier. + /// + _2 = (int)Inno.Native.ImGui.ImGuiKey._2, + /// + /// Identifies the _3 presentation key or modifier. + /// + _3 = (int)Inno.Native.ImGui.ImGuiKey._3, + /// + /// Identifies the _4 presentation key or modifier. + /// + _4 = (int)Inno.Native.ImGui.ImGuiKey._4, + /// + /// Identifies the _5 presentation key or modifier. + /// + _5 = (int)Inno.Native.ImGui.ImGuiKey._5, + /// + /// Identifies the _6 presentation key or modifier. + /// + _6 = (int)Inno.Native.ImGui.ImGuiKey._6, + /// + /// Identifies the _7 presentation key or modifier. + /// + _7 = (int)Inno.Native.ImGui.ImGuiKey._7, + /// + /// Identifies the _8 presentation key or modifier. + /// + _8 = (int)Inno.Native.ImGui.ImGuiKey._8, + /// + /// Identifies the _9 presentation key or modifier. + /// + _9 = (int)Inno.Native.ImGui.ImGuiKey._9, + /// + /// Identifies the A presentation key or modifier. + /// + A = (int)Inno.Native.ImGui.ImGuiKey.A, + /// + /// Identifies the B presentation key or modifier. + /// + B = (int)Inno.Native.ImGui.ImGuiKey.B, + /// + /// Identifies the C presentation key or modifier. + /// + C = (int)Inno.Native.ImGui.ImGuiKey.C, + /// + /// Identifies the D presentation key or modifier. + /// + D = (int)Inno.Native.ImGui.ImGuiKey.D, + /// + /// Identifies the E presentation key or modifier. + /// + E = (int)Inno.Native.ImGui.ImGuiKey.E, + /// + /// Identifies the F presentation key or modifier. + /// + F = (int)Inno.Native.ImGui.ImGuiKey.F, + /// + /// Identifies the G presentation key or modifier. + /// + G = (int)Inno.Native.ImGui.ImGuiKey.G, + /// + /// Identifies the H presentation key or modifier. + /// + H = (int)Inno.Native.ImGui.ImGuiKey.H, + /// + /// Identifies the I presentation key or modifier. + /// + I = (int)Inno.Native.ImGui.ImGuiKey.I, + /// + /// Identifies the J presentation key or modifier. + /// + J = (int)Inno.Native.ImGui.ImGuiKey.J, + /// + /// Identifies the K presentation key or modifier. + /// + K = (int)Inno.Native.ImGui.ImGuiKey.K, + /// + /// Identifies the L presentation key or modifier. + /// + L = (int)Inno.Native.ImGui.ImGuiKey.L, + /// + /// Identifies the M presentation key or modifier. + /// + M = (int)Inno.Native.ImGui.ImGuiKey.M, + /// + /// Identifies the N presentation key or modifier. + /// + N = (int)Inno.Native.ImGui.ImGuiKey.N, + /// + /// Identifies the O presentation key or modifier. + /// + O = (int)Inno.Native.ImGui.ImGuiKey.O, + /// + /// Identifies the P presentation key or modifier. + /// + P = (int)Inno.Native.ImGui.ImGuiKey.P, + /// + /// Identifies the Q presentation key or modifier. + /// + Q = (int)Inno.Native.ImGui.ImGuiKey.Q, + /// + /// Identifies the R presentation key or modifier. + /// + R = (int)Inno.Native.ImGui.ImGuiKey.R, + /// + /// Identifies the S presentation key or modifier. + /// + S = (int)Inno.Native.ImGui.ImGuiKey.S, + /// + /// Identifies the T presentation key or modifier. + /// + T = (int)Inno.Native.ImGui.ImGuiKey.T, + /// + /// Identifies the U presentation key or modifier. + /// + U = (int)Inno.Native.ImGui.ImGuiKey.U, + /// + /// Identifies the V presentation key or modifier. + /// + V = (int)Inno.Native.ImGui.ImGuiKey.V, + /// + /// Identifies the W presentation key or modifier. + /// + W = (int)Inno.Native.ImGui.ImGuiKey.W, + /// + /// Identifies the X presentation key or modifier. + /// + X = (int)Inno.Native.ImGui.ImGuiKey.X, + /// + /// Identifies the Y presentation key or modifier. + /// + Y = (int)Inno.Native.ImGui.ImGuiKey.Y, + /// + /// Identifies the Z presentation key or modifier. + /// + Z = (int)Inno.Native.ImGui.ImGuiKey.Z, + /// + /// Identifies the F1 presentation key or modifier. + /// + F1 = (int)Inno.Native.ImGui.ImGuiKey.F1, + /// + /// Identifies the F2 presentation key or modifier. + /// + F2 = (int)Inno.Native.ImGui.ImGuiKey.F2, + /// + /// Identifies the F3 presentation key or modifier. + /// + F3 = (int)Inno.Native.ImGui.ImGuiKey.F3, + /// + /// Identifies the F4 presentation key or modifier. + /// + F4 = (int)Inno.Native.ImGui.ImGuiKey.F4, + /// + /// Identifies the F5 presentation key or modifier. + /// + F5 = (int)Inno.Native.ImGui.ImGuiKey.F5, + /// + /// Identifies the F6 presentation key or modifier. + /// + F6 = (int)Inno.Native.ImGui.ImGuiKey.F6, + /// + /// Identifies the F7 presentation key or modifier. + /// + F7 = (int)Inno.Native.ImGui.ImGuiKey.F7, + /// + /// Identifies the F8 presentation key or modifier. + /// + F8 = (int)Inno.Native.ImGui.ImGuiKey.F8, + /// + /// Identifies the F9 presentation key or modifier. + /// + F9 = (int)Inno.Native.ImGui.ImGuiKey.F9, + /// + /// Identifies the F10 presentation key or modifier. + /// + F10 = (int)Inno.Native.ImGui.ImGuiKey.F10, + /// + /// Identifies the F11 presentation key or modifier. + /// + F11 = (int)Inno.Native.ImGui.ImGuiKey.F11, + /// + /// Identifies the F12 presentation key or modifier. + /// + F12 = (int)Inno.Native.ImGui.ImGuiKey.F12, + /// + /// Identifies the F13 presentation key or modifier. + /// + F13 = (int)Inno.Native.ImGui.ImGuiKey.F13, + /// + /// Identifies the F14 presentation key or modifier. + /// + F14 = (int)Inno.Native.ImGui.ImGuiKey.F14, + /// + /// Identifies the F15 presentation key or modifier. + /// + F15 = (int)Inno.Native.ImGui.ImGuiKey.F15, + /// + /// Identifies the F16 presentation key or modifier. + /// + F16 = (int)Inno.Native.ImGui.ImGuiKey.F16, + /// + /// Identifies the F17 presentation key or modifier. + /// + F17 = (int)Inno.Native.ImGui.ImGuiKey.F17, + /// + /// Identifies the F18 presentation key or modifier. + /// + F18 = (int)Inno.Native.ImGui.ImGuiKey.F18, + /// + /// Identifies the F19 presentation key or modifier. + /// + F19 = (int)Inno.Native.ImGui.ImGuiKey.F19, + /// + /// Identifies the F20 presentation key or modifier. + /// + F20 = (int)Inno.Native.ImGui.ImGuiKey.F20, + /// + /// Identifies the F21 presentation key or modifier. + /// + F21 = (int)Inno.Native.ImGui.ImGuiKey.F21, + /// + /// Identifies the F22 presentation key or modifier. + /// + F22 = (int)Inno.Native.ImGui.ImGuiKey.F22, + /// + /// Identifies the F23 presentation key or modifier. + /// + F23 = (int)Inno.Native.ImGui.ImGuiKey.F23, + /// + /// Identifies the F24 presentation key or modifier. + /// + F24 = (int)Inno.Native.ImGui.ImGuiKey.F24, + /// + /// Identifies the Apostrophe presentation key or modifier. + /// + Apostrophe = (int)Inno.Native.ImGui.ImGuiKey.Apostrophe, + /// + /// Identifies the Comma presentation key or modifier. + /// + Comma = (int)Inno.Native.ImGui.ImGuiKey.Comma, + /// + /// Identifies the Minus presentation key or modifier. + /// + Minus = (int)Inno.Native.ImGui.ImGuiKey.Minus, + /// + /// Identifies the Period presentation key or modifier. + /// + Period = (int)Inno.Native.ImGui.ImGuiKey.Period, + /// + /// Identifies the Slash presentation key or modifier. + /// + Slash = (int)Inno.Native.ImGui.ImGuiKey.Slash, + /// + /// Identifies the Semicolon presentation key or modifier. + /// + Semicolon = (int)Inno.Native.ImGui.ImGuiKey.Semicolon, + /// + /// Identifies the Equal presentation key or modifier. + /// + Equal = (int)Inno.Native.ImGui.ImGuiKey.Equal, + /// + /// Identifies the LeftBracket presentation key or modifier. + /// + LeftBracket = (int)Inno.Native.ImGui.ImGuiKey.LeftBracket, + /// + /// Identifies the Backslash presentation key or modifier. + /// + Backslash = (int)Inno.Native.ImGui.ImGuiKey.Backslash, + /// + /// Identifies the RightBracket presentation key or modifier. + /// + RightBracket = (int)Inno.Native.ImGui.ImGuiKey.RightBracket, + /// + /// Identifies the GraveAccent presentation key or modifier. + /// + GraveAccent = (int)Inno.Native.ImGui.ImGuiKey.GraveAccent, + /// + /// Identifies the CapsLock presentation key or modifier. + /// + CapsLock = (int)Inno.Native.ImGui.ImGuiKey.CapsLock, + /// + /// Identifies the ScrollLock presentation key or modifier. + /// + ScrollLock = (int)Inno.Native.ImGui.ImGuiKey.ScrollLock, + /// + /// Identifies the NumLock presentation key or modifier. + /// + NumLock = (int)Inno.Native.ImGui.ImGuiKey.NumLock, + /// + /// Identifies the PrintScreen presentation key or modifier. + /// + PrintScreen = (int)Inno.Native.ImGui.ImGuiKey.PrintScreen, + /// + /// Identifies the Pause presentation key or modifier. + /// + Pause = (int)Inno.Native.ImGui.ImGuiKey.Pause, + /// + /// Identifies the Keypad0 presentation key or modifier. + /// + Keypad0 = (int)Inno.Native.ImGui.ImGuiKey.Keypad0, + /// + /// Identifies the Keypad1 presentation key or modifier. + /// + Keypad1 = (int)Inno.Native.ImGui.ImGuiKey.Keypad1, + /// + /// Identifies the Keypad2 presentation key or modifier. + /// + Keypad2 = (int)Inno.Native.ImGui.ImGuiKey.Keypad2, + /// + /// Identifies the Keypad3 presentation key or modifier. + /// + Keypad3 = (int)Inno.Native.ImGui.ImGuiKey.Keypad3, + /// + /// Identifies the Keypad4 presentation key or modifier. + /// + Keypad4 = (int)Inno.Native.ImGui.ImGuiKey.Keypad4, + /// + /// Identifies the Keypad5 presentation key or modifier. + /// + Keypad5 = (int)Inno.Native.ImGui.ImGuiKey.Keypad5, + /// + /// Identifies the Keypad6 presentation key or modifier. + /// + Keypad6 = (int)Inno.Native.ImGui.ImGuiKey.Keypad6, + /// + /// Identifies the Keypad7 presentation key or modifier. + /// + Keypad7 = (int)Inno.Native.ImGui.ImGuiKey.Keypad7, + /// + /// Identifies the Keypad8 presentation key or modifier. + /// + Keypad8 = (int)Inno.Native.ImGui.ImGuiKey.Keypad8, + /// + /// Identifies the Keypad9 presentation key or modifier. + /// + Keypad9 = (int)Inno.Native.ImGui.ImGuiKey.Keypad9, + /// + /// Identifies the KeypadDecimal presentation key or modifier. + /// + KeypadDecimal = (int)Inno.Native.ImGui.ImGuiKey.KeypadDecimal, + /// + /// Identifies the KeypadDivide presentation key or modifier. + /// + KeypadDivide = (int)Inno.Native.ImGui.ImGuiKey.KeypadDivide, + /// + /// Identifies the KeypadMultiply presentation key or modifier. + /// + KeypadMultiply = (int)Inno.Native.ImGui.ImGuiKey.KeypadMultiply, + /// + /// Identifies the KeypadSubtract presentation key or modifier. + /// + KeypadSubtract = (int)Inno.Native.ImGui.ImGuiKey.KeypadSubtract, + /// + /// Identifies the KeypadAdd presentation key or modifier. + /// + KeypadAdd = (int)Inno.Native.ImGui.ImGuiKey.KeypadAdd, + /// + /// Identifies the KeypadEnter presentation key or modifier. + /// + KeypadEnter = (int)Inno.Native.ImGui.ImGuiKey.KeypadEnter, + /// + /// Identifies the KeypadEqual presentation key or modifier. + /// + KeypadEqual = (int)Inno.Native.ImGui.ImGuiKey.KeypadEqual, + /// + /// Identifies the AppBack presentation key or modifier. + /// + AppBack = (int)Inno.Native.ImGui.ImGuiKey.AppBack, + /// + /// Identifies the AppForward presentation key or modifier. + /// + AppForward = (int)Inno.Native.ImGui.ImGuiKey.AppForward, + /// + /// Identifies the Oem102 presentation key or modifier. + /// + Oem102 = (int)Inno.Native.ImGui.ImGuiKey.Oem102, + /// + /// Identifies the GamepadStart presentation key or modifier. + /// + GamepadStart = (int)Inno.Native.ImGui.ImGuiKey.GamepadStart, + /// + /// Identifies the GamepadBack presentation key or modifier. + /// + GamepadBack = (int)Inno.Native.ImGui.ImGuiKey.GamepadBack, + /// + /// Identifies the GamepadFaceLeft presentation key or modifier. + /// + GamepadFaceLeft = (int)Inno.Native.ImGui.ImGuiKey.GamepadFaceLeft, + /// + /// Identifies the GamepadFaceRight presentation key or modifier. + /// + GamepadFaceRight = (int)Inno.Native.ImGui.ImGuiKey.GamepadFaceRight, + /// + /// Identifies the GamepadFaceUp presentation key or modifier. + /// + GamepadFaceUp = (int)Inno.Native.ImGui.ImGuiKey.GamepadFaceUp, + /// + /// Identifies the GamepadFaceDown presentation key or modifier. + /// + GamepadFaceDown = (int)Inno.Native.ImGui.ImGuiKey.GamepadFaceDown, + /// + /// Identifies the GamepadDpadLeft presentation key or modifier. + /// + GamepadDpadLeft = (int)Inno.Native.ImGui.ImGuiKey.GamepadDpadLeft, + /// + /// Identifies the GamepadDpadRight presentation key or modifier. + /// + GamepadDpadRight = (int)Inno.Native.ImGui.ImGuiKey.GamepadDpadRight, + /// + /// Identifies the GamepadDpadUp presentation key or modifier. + /// + GamepadDpadUp = (int)Inno.Native.ImGui.ImGuiKey.GamepadDpadUp, + /// + /// Identifies the GamepadDpadDown presentation key or modifier. + /// + GamepadDpadDown = (int)Inno.Native.ImGui.ImGuiKey.GamepadDpadDown, + /// + /// Identifies the GamepadL1 presentation key or modifier. + /// + GamepadL1 = (int)Inno.Native.ImGui.ImGuiKey.GamepadL1, + /// + /// Identifies the GamepadR1 presentation key or modifier. + /// + GamepadR1 = (int)Inno.Native.ImGui.ImGuiKey.GamepadR1, + /// + /// Identifies the GamepadL2 presentation key or modifier. + /// + GamepadL2 = (int)Inno.Native.ImGui.ImGuiKey.GamepadL2, + /// + /// Identifies the GamepadR2 presentation key or modifier. + /// + GamepadR2 = (int)Inno.Native.ImGui.ImGuiKey.GamepadR2, + /// + /// Identifies the GamepadL3 presentation key or modifier. + /// + GamepadL3 = (int)Inno.Native.ImGui.ImGuiKey.GamepadL3, + /// + /// Identifies the GamepadR3 presentation key or modifier. + /// + GamepadR3 = (int)Inno.Native.ImGui.ImGuiKey.GamepadR3, + /// + /// Identifies the GamepadLStickLeft presentation key or modifier. + /// + GamepadLStickLeft = (int)Inno.Native.ImGui.ImGuiKey.GamepadLStickLeft, + /// + /// Identifies the GamepadLStickRight presentation key or modifier. + /// + GamepadLStickRight = (int)Inno.Native.ImGui.ImGuiKey.GamepadLStickRight, + /// + /// Identifies the GamepadLStickUp presentation key or modifier. + /// + GamepadLStickUp = (int)Inno.Native.ImGui.ImGuiKey.GamepadLStickUp, + /// + /// Identifies the GamepadLStickDown presentation key or modifier. + /// + GamepadLStickDown = (int)Inno.Native.ImGui.ImGuiKey.GamepadLStickDown, + /// + /// Identifies the GamepadRStickLeft presentation key or modifier. + /// + GamepadRStickLeft = (int)Inno.Native.ImGui.ImGuiKey.GamepadRStickLeft, + /// + /// Identifies the GamepadRStickRight presentation key or modifier. + /// + GamepadRStickRight = (int)Inno.Native.ImGui.ImGuiKey.GamepadRStickRight, + /// + /// Identifies the GamepadRStickUp presentation key or modifier. + /// + GamepadRStickUp = (int)Inno.Native.ImGui.ImGuiKey.GamepadRStickUp, + /// + /// Identifies the GamepadRStickDown presentation key or modifier. + /// + GamepadRStickDown = (int)Inno.Native.ImGui.ImGuiKey.GamepadRStickDown, + /// + /// Identifies the MouseLeft presentation key or modifier. + /// + MouseLeft = (int)Inno.Native.ImGui.ImGuiKey.MouseLeft, + /// + /// Identifies the MouseRight presentation key or modifier. + /// + MouseRight = (int)Inno.Native.ImGui.ImGuiKey.MouseRight, + /// + /// Identifies the MouseMiddle presentation key or modifier. + /// + MouseMiddle = (int)Inno.Native.ImGui.ImGuiKey.MouseMiddle, + /// + /// Identifies the MouseX1 presentation key or modifier. + /// + MouseX1 = (int)Inno.Native.ImGui.ImGuiKey.MouseX1, + /// + /// Identifies the MouseX2 presentation key or modifier. + /// + MouseX2 = (int)Inno.Native.ImGui.ImGuiKey.MouseX2, + /// + /// Identifies the MouseWheelX presentation key or modifier. + /// + MouseWheelX = (int)Inno.Native.ImGui.ImGuiKey.MouseWheelX, + /// + /// Identifies the MouseWheelY presentation key or modifier. + /// + MouseWheelY = (int)Inno.Native.ImGui.ImGuiKey.MouseWheelY, + /// + /// Identifies the ReservedForModCtrl presentation key or modifier. + /// + ReservedForModCtrl = (int)Inno.Native.ImGui.ImGuiKey.ReservedForModCtrl, + /// + /// Identifies the ReservedForModShift presentation key or modifier. + /// + ReservedForModShift = (int)Inno.Native.ImGui.ImGuiKey.ReservedForModShift, + /// + /// Identifies the ReservedForModAlt presentation key or modifier. + /// + ReservedForModAlt = (int)Inno.Native.ImGui.ImGuiKey.ReservedForModAlt, + /// + /// Identifies the ReservedForModSuper presentation key or modifier. + /// + ReservedForModSuper = (int)Inno.Native.ImGui.ImGuiKey.ReservedForModSuper, + /// + /// Identifies the NamedKeyEnd presentation key or modifier. + /// + NamedKeyEnd = (int)Inno.Native.ImGui.ImGuiKey.NamedKeyEnd, + /// + /// Identifies the NamedKeyCount presentation key or modifier. + /// + NamedKeyCount = (int)Inno.Native.ImGui.ImGuiKey.NamedKeyCount, + /// + /// Identifies the ImGuiModNone presentation key or modifier. + /// + ImGuiModNone = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModNone, + /// + /// Identifies the ImGuiModCtrl presentation key or modifier. + /// + ImGuiModCtrl = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModCtrl, + /// + /// Identifies the ImGuiModShift presentation key or modifier. + /// + ImGuiModShift = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModShift, + /// + /// Identifies the ImGuiModAlt presentation key or modifier. + /// + ImGuiModAlt = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModAlt, + /// + /// Identifies the ImGuiModSuper presentation key or modifier. + /// + ImGuiModSuper = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModSuper, + /// + /// Identifies the ImGuiModMask presentation key or modifier. + /// + ImGuiModMask = (int)Inno.Native.ImGui.ImGuiKey.ImGuiModMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs new file mode 100644 index 000000000..5afa25489 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseButton.cs @@ -0,0 +1,25 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported mouse button choices at the Editor presentation boundary. +/// +public enum ImGuiMouseButton : int +{ + /// + /// Selects the left presentation behavior. + /// + Left = (int)Inno.Native.ImGui.ImGuiMouseButton.Left, + /// + /// Selects the right presentation behavior. + /// + Right = (int)Inno.Native.ImGui.ImGuiMouseButton.Right, + /// + /// Selects the middle presentation behavior. + /// + Middle = (int)Inno.Native.ImGui.ImGuiMouseButton.Middle, + /// + /// Marks the exclusive end of the valid option range. + /// + Count = (int)Inno.Native.ImGui.ImGuiMouseButton.Count, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs new file mode 100644 index 000000000..86180b833 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiMouseCursor.cs @@ -0,0 +1,61 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported mouse cursor choices at the Editor presentation boundary. +/// +public enum ImGuiMouseCursor : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiMouseCursor.None, + /// + /// Selects the arrow presentation behavior. + /// + Arrow = (int)Inno.Native.ImGui.ImGuiMouseCursor.Arrow, + /// + /// Selects the text input presentation behavior. + /// + TextInput = (int)Inno.Native.ImGui.ImGuiMouseCursor.TextInput, + /// + /// Selects the resize all presentation behavior. + /// + ResizeAll = (int)Inno.Native.ImGui.ImGuiMouseCursor.ResizeAll, + /// + /// Selects the resize ns presentation behavior. + /// + ResizeNs = (int)Inno.Native.ImGui.ImGuiMouseCursor.ResizeNs, + /// + /// Selects the resize ew presentation behavior. + /// + ResizeEw = (int)Inno.Native.ImGui.ImGuiMouseCursor.ResizeEw, + /// + /// Selects the resize nesw presentation behavior. + /// + ResizeNesw = (int)Inno.Native.ImGui.ImGuiMouseCursor.ResizeNesw, + /// + /// Selects the resize nwse presentation behavior. + /// + ResizeNwse = (int)Inno.Native.ImGui.ImGuiMouseCursor.ResizeNwse, + /// + /// Selects the hand presentation behavior. + /// + Hand = (int)Inno.Native.ImGui.ImGuiMouseCursor.Hand, + /// + /// Selects the wait presentation behavior. + /// + Wait = (int)Inno.Native.ImGui.ImGuiMouseCursor.Wait, + /// + /// Selects the progress presentation behavior. + /// + Progress = (int)Inno.Native.ImGui.ImGuiMouseCursor.Progress, + /// + /// Selects the not allowed presentation behavior. + /// + NotAllowed = (int)Inno.Native.ImGui.ImGuiMouseCursor.NotAllowed, + /// + /// Marks the exclusive end of the valid option range. + /// + Count = (int)Inno.Native.ImGui.ImGuiMouseCursor.Count, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs new file mode 100644 index 000000000..017a77d2a --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiPopupFlags.cs @@ -0,0 +1,62 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported popup flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiPopupFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiPopupFlags.None, + /// + /// For BeginPopupContext*(): open on Left Mouse release. Only one button allowed! + /// + MouseButtonLeft = (int)Inno.Native.ImGui.ImGuiPopupFlags.MouseButtonLeft, + /// + /// For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default) + /// + MouseButtonRight = (int)Inno.Native.ImGui.ImGuiPopupFlags.MouseButtonRight, + /// + /// For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed! + /// + MouseButtonMiddle = (int)Inno.Native.ImGui.ImGuiPopupFlags.MouseButtonMiddle, + /// + /// For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation) + /// + NoReopen = (int)Inno.Native.ImGui.ImGuiPopupFlags.NoReopen, + /// + /// For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack + /// + NoOpenOverExistingPopup = (int)Inno.Native.ImGui.ImGuiPopupFlags.NoOpenOverExistingPopup, + /// + /// For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space + /// + NoOpenOverItems = (int)Inno.Native.ImGui.ImGuiPopupFlags.NoOpenOverItems, + /// + /// For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. + /// + AnyPopupId = (int)Inno.Native.ImGui.ImGuiPopupFlags.AnyPopupId, + /// + /// For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) + /// + AnyPopupLevel = (int)Inno.Native.ImGui.ImGuiPopupFlags.AnyPopupLevel, + /// + /// Selects the any popup presentation behavior. + /// + AnyPopup = (int)Inno.Native.ImGui.ImGuiPopupFlags.AnyPopup, + /// + /// Selects the mouse button shift presentation behavior. + /// + MouseButtonShift = (int)Inno.Native.ImGui.ImGuiPopupFlags.MouseButtonShift, + /// + /// Selects the mouse button mask presentation behavior. + /// + MouseButtonMask = (int)Inno.Native.ImGui.ImGuiPopupFlags.MouseButtonMask, + /// + /// Selects the invalid mask presentation behavior. + /// + InvalidMask = (int)Inno.Native.ImGui.ImGuiPopupFlags.InvalidMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs new file mode 100644 index 000000000..e8ba440f5 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSelectableFlags.cs @@ -0,0 +1,42 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported selectable flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiSelectableFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiSelectableFlags.None, + /// + /// Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups) + /// + NoAutoClosePopups = (int)Inno.Native.ImGui.ImGuiSelectableFlags.NoAutoClosePopups, + /// + /// Frame will span all columns of its container table (text will still fit in current column) + /// + SpanAllColumns = (int)Inno.Native.ImGui.ImGuiSelectableFlags.SpanAllColumns, + /// + /// Generate press events on double clicks too + /// + AllowDoubleClick = (int)Inno.Native.ImGui.ImGuiSelectableFlags.AllowDoubleClick, + /// + /// Cannot be selected, display grayed out text + /// + Disabled = (int)Inno.Native.ImGui.ImGuiSelectableFlags.Disabled, + /// + /// (WIP) Hit testing to allow subsequent widgets to overlap this one + /// + AllowOverlap = (int)Inno.Native.ImGui.ImGuiSelectableFlags.AllowOverlap, + /// + /// Make the item be displayed as if it is hovered + /// + Highlight = (int)Inno.Native.ImGui.ImGuiSelectableFlags.Highlight, + /// + /// Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block. + /// + SelectOnNav = (int)Inno.Native.ImGui.ImGuiSelectableFlags.SelectOnNav, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs new file mode 100644 index 000000000..6eadc7060 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiSliderFlags.cs @@ -0,0 +1,54 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported slider flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiSliderFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiSliderFlags.None, + /// + /// Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. + /// + Logarithmic = (int)Inno.Native.ImGui.ImGuiSliderFlags.Logarithmic, + /// + /// Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). + /// + NoRoundToFormat = (int)Inno.Native.ImGui.ImGuiSliderFlags.NoRoundToFormat, + /// + /// Disable Ctrl+Click or Enter key allowing to input text directly into the widget. + /// + NoInput = (int)Inno.Native.ImGui.ImGuiSliderFlags.NoInput, + /// + /// Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now. + /// + WrapAround = (int)Inno.Native.ImGui.ImGuiSliderFlags.WrapAround, + /// + /// Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. + /// + ClampOnInput = (int)Inno.Native.ImGui.ImGuiSliderFlags.ClampOnInput, + /// + /// Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. + /// + ClampZeroRange = (int)Inno.Native.ImGui.ImGuiSliderFlags.ClampZeroRange, + /// + /// Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. + /// + NoSpeedTweaks = (int)Inno.Native.ImGui.ImGuiSliderFlags.NoSpeedTweaks, + /// + /// DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. + /// + ColorMarkers = (int)Inno.Native.ImGui.ImGuiSliderFlags.ColorMarkers, + /// + /// Selects the always clamp presentation behavior. + /// + AlwaysClamp = (int)Inno.Native.ImGui.ImGuiSliderFlags.AlwaysClamp, + /// + /// Selects the invalid mask presentation behavior. + /// + InvalidMask = (int)Inno.Native.ImGui.ImGuiSliderFlags.InvalidMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs new file mode 100644 index 000000000..b61d56d6b --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiStyleVar.cs @@ -0,0 +1,177 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported style var choices at the Editor presentation boundary. +/// +public enum ImGuiStyleVar : int +{ + /// + /// Selects the alpha presentation behavior. + /// + Alpha = (int)Inno.Native.ImGui.ImGuiStyleVar.Alpha, + /// + /// Selects the disabled alpha presentation behavior. + /// + DisabledAlpha = (int)Inno.Native.ImGui.ImGuiStyleVar.DisabledAlpha, + /// + /// Selects the window padding presentation behavior. + /// + WindowPadding = (int)Inno.Native.ImGui.ImGuiStyleVar.WindowPadding, + /// + /// Selects the window rounding presentation behavior. + /// + WindowRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.WindowRounding, + /// + /// Selects the window border size presentation behavior. + /// + WindowBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.WindowBorderSize, + /// + /// Selects the window min size presentation behavior. + /// + WindowMinSize = (int)Inno.Native.ImGui.ImGuiStyleVar.WindowMinSize, + /// + /// Selects the window title align presentation behavior. + /// + WindowTitleAlign = (int)Inno.Native.ImGui.ImGuiStyleVar.WindowTitleAlign, + /// + /// Selects the child rounding presentation behavior. + /// + ChildRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.ChildRounding, + /// + /// Selects the child border size presentation behavior. + /// + ChildBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.ChildBorderSize, + /// + /// Selects the popup rounding presentation behavior. + /// + PopupRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.PopupRounding, + /// + /// Selects the popup border size presentation behavior. + /// + PopupBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.PopupBorderSize, + /// + /// Selects the frame padding presentation behavior. + /// + FramePadding = (int)Inno.Native.ImGui.ImGuiStyleVar.FramePadding, + /// + /// Selects the frame rounding presentation behavior. + /// + FrameRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.FrameRounding, + /// + /// Selects the frame border size presentation behavior. + /// + FrameBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.FrameBorderSize, + /// + /// Selects the item spacing presentation behavior. + /// + ItemSpacing = (int)Inno.Native.ImGui.ImGuiStyleVar.ItemSpacing, + /// + /// Selects the item inner spacing presentation behavior. + /// + ItemInnerSpacing = (int)Inno.Native.ImGui.ImGuiStyleVar.ItemInnerSpacing, + /// + /// Selects the indent spacing presentation behavior. + /// + IndentSpacing = (int)Inno.Native.ImGui.ImGuiStyleVar.IndentSpacing, + /// + /// Selects the cell padding presentation behavior. + /// + CellPadding = (int)Inno.Native.ImGui.ImGuiStyleVar.CellPadding, + /// + /// Selects the scrollbar size presentation behavior. + /// + ScrollbarSize = (int)Inno.Native.ImGui.ImGuiStyleVar.ScrollbarSize, + /// + /// Selects the scrollbar rounding presentation behavior. + /// + ScrollbarRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.ScrollbarRounding, + /// + /// Selects the scrollbar padding presentation behavior. + /// + ScrollbarPadding = (int)Inno.Native.ImGui.ImGuiStyleVar.ScrollbarPadding, + /// + /// Selects the grab min size presentation behavior. + /// + GrabMinSize = (int)Inno.Native.ImGui.ImGuiStyleVar.GrabMinSize, + /// + /// Selects the grab rounding presentation behavior. + /// + GrabRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.GrabRounding, + /// + /// Selects the image rounding presentation behavior. + /// + ImageRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.ImageRounding, + /// + /// Selects the image border size presentation behavior. + /// + ImageBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.ImageBorderSize, + /// + /// Selects the tab rounding presentation behavior. + /// + TabRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.TabRounding, + /// + /// Selects the tab border size presentation behavior. + /// + TabBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.TabBorderSize, + /// + /// Selects the tab min width base presentation behavior. + /// + TabMinWidthBase = (int)Inno.Native.ImGui.ImGuiStyleVar.TabMinWidthBase, + /// + /// Selects the tab min width shrink presentation behavior. + /// + TabMinWidthShrink = (int)Inno.Native.ImGui.ImGuiStyleVar.TabMinWidthShrink, + /// + /// Selects the tab bar border size presentation behavior. + /// + TabBarBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.TabBarBorderSize, + /// + /// Selects the tab bar overline size presentation behavior. + /// + TabBarOverlineSize = (int)Inno.Native.ImGui.ImGuiStyleVar.TabBarOverlineSize, + /// + /// Selects the table angled headers angle presentation behavior. + /// + TableAngledHeadersAngle = (int)Inno.Native.ImGui.ImGuiStyleVar.TableAngledHeadersAngle, + /// + /// Selects the table angled headers text align presentation behavior. + /// + TableAngledHeadersTextAlign = (int)Inno.Native.ImGui.ImGuiStyleVar.TableAngledHeadersTextAlign, + /// + /// Selects the tree lines size presentation behavior. + /// + TreeLinesSize = (int)Inno.Native.ImGui.ImGuiStyleVar.TreeLinesSize, + /// + /// Selects the tree lines rounding presentation behavior. + /// + TreeLinesRounding = (int)Inno.Native.ImGui.ImGuiStyleVar.TreeLinesRounding, + /// + /// Selects the button text align presentation behavior. + /// + ButtonTextAlign = (int)Inno.Native.ImGui.ImGuiStyleVar.ButtonTextAlign, + /// + /// Selects the selectable text align presentation behavior. + /// + SelectableTextAlign = (int)Inno.Native.ImGui.ImGuiStyleVar.SelectableTextAlign, + /// + /// Selects the separator text border size presentation behavior. + /// + SeparatorTextBorderSize = (int)Inno.Native.ImGui.ImGuiStyleVar.SeparatorTextBorderSize, + /// + /// Selects the separator text align presentation behavior. + /// + SeparatorTextAlign = (int)Inno.Native.ImGui.ImGuiStyleVar.SeparatorTextAlign, + /// + /// Selects the separator text padding presentation behavior. + /// + SeparatorTextPadding = (int)Inno.Native.ImGui.ImGuiStyleVar.SeparatorTextPadding, + /// + /// Selects the docking separator size presentation behavior. + /// + DockingSeparatorSize = (int)Inno.Native.ImGui.ImGuiStyleVar.DockingSeparatorSize, + /// + /// Marks the exclusive end of the valid option range. + /// + Count = (int)Inno.Native.ImGui.ImGuiStyleVar.Count, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs new file mode 100644 index 000000000..0664d4257 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabBarFlags.cs @@ -0,0 +1,62 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported tab bar flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTabBarFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTabBarFlags.None, + /// + /// Allow manually dragging tabs to re-order them + New tabs are appended at the end of list + /// + Reorderable = (int)Inno.Native.ImGui.ImGuiTabBarFlags.Reorderable, + /// + /// Automatically select new tabs when they appear + /// + AutoSelectNewTabs = (int)Inno.Native.ImGui.ImGuiTabBarFlags.AutoSelectNewTabs, + /// + /// Disable buttons to open the tab list popup + /// + TabListPopupButton = (int)Inno.Native.ImGui.ImGuiTabBarFlags.TabListPopupButton, + /// + /// Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() & & IsMouseClicked(2)) *p_open = false. + /// + NoCloseWithMiddleMouseButton = (int)Inno.Native.ImGui.ImGuiTabBarFlags.NoCloseWithMiddleMouseButton, + /// + /// Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) + /// + NoTabListScrollingButtons = (int)Inno.Native.ImGui.ImGuiTabBarFlags.NoTabListScrollingButtons, + /// + /// Disable tooltips when hovering a tab + /// + NoTooltip = (int)Inno.Native.ImGui.ImGuiTabBarFlags.NoTooltip, + /// + /// Draw selected overline markers over selected tab + /// + DrawSelectedOverline = (int)Inno.Native.ImGui.ImGuiTabBarFlags.DrawSelectedOverline, + /// + /// Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons. + /// + FittingPolicyMixed = (int)Inno.Native.ImGui.ImGuiTabBarFlags.FittingPolicyMixed, + /// + /// Shrink down tabs when they don't fit + /// + FittingPolicyShrink = (int)Inno.Native.ImGui.ImGuiTabBarFlags.FittingPolicyShrink, + /// + /// Enable scrolling buttons when tabs don't fit + /// + FittingPolicyScroll = (int)Inno.Native.ImGui.ImGuiTabBarFlags.FittingPolicyScroll, + /// + /// Selects the fitting policy mask presentation behavior. + /// + FittingPolicyMask = (int)Inno.Native.ImGui.ImGuiTabBarFlags.FittingPolicyMask, + /// + /// Selects the fitting policy default presentation behavior. + /// + FittingPolicyDefault = (int)Inno.Native.ImGui.ImGuiTabBarFlags.FittingPolicyDefault, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs new file mode 100644 index 000000000..58d858b6b --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTabItemFlags.cs @@ -0,0 +1,50 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported tab item flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTabItemFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTabItemFlags.None, + /// + /// Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure. + /// + UnsavedDocument = (int)Inno.Native.ImGui.ImGuiTabItemFlags.UnsavedDocument, + /// + /// Trigger flag to programmatically make the tab selected when calling BeginTabItem() + /// + SetSelected = (int)Inno.Native.ImGui.ImGuiTabItemFlags.SetSelected, + /// + /// Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() & & IsMouseClicked(2)) *p_open = false. + /// + NoCloseWithMiddleMouseButton = (int)Inno.Native.ImGui.ImGuiTabItemFlags.NoCloseWithMiddleMouseButton, + /// + /// Don't call PushID()/PopID() on BeginTabItem()/EndTabItem() + /// + NoPushId = (int)Inno.Native.ImGui.ImGuiTabItemFlags.NoPushId, + /// + /// Disable tooltip for the given tab + /// + NoTooltip = (int)Inno.Native.ImGui.ImGuiTabItemFlags.NoTooltip, + /// + /// Disable reordering this tab or having another tab cross over this tab + /// + NoReorder = (int)Inno.Native.ImGui.ImGuiTabItemFlags.NoReorder, + /// + /// Enforce the tab position to the left of the tab bar (after the tab list popup button) + /// + Leading = (int)Inno.Native.ImGui.ImGuiTabItemFlags.Leading, + /// + /// Enforce the tab position to the right of the tab bar (before the scrolling buttons) + /// + Trailing = (int)Inno.Native.ImGui.ImGuiTabItemFlags.Trailing, + /// + /// Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + /// + NoAssumedClosure = (int)Inno.Native.ImGui.ImGuiTabItemFlags.NoAssumedClosure, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs new file mode 100644 index 000000000..db6dbba57 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableBgTarget.cs @@ -0,0 +1,25 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported table bg target choices at the Editor presentation boundary. +/// +public enum ImGuiTableBgTarget : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTableBgTarget.None, + /// + /// Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) + /// + RowBg0 = (int)Inno.Native.ImGui.ImGuiTableBgTarget.RowBg0, + /// + /// Set row background color 1 (generally used for selection marking) + /// + RowBg1 = (int)Inno.Native.ImGui.ImGuiTableBgTarget.RowBg1, + /// + /// Set cell background color (top-most color) + /// + CellBg = (int)Inno.Native.ImGui.ImGuiTableBgTarget.CellBg, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs new file mode 100644 index 000000000..99b9b6ec3 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableColumnFlags.cs @@ -0,0 +1,122 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported table column flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTableColumnFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.None, + /// + /// Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) + /// + Disabled = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.Disabled, + /// + /// Default as a hidden/disabled column. + /// + DefaultHide = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.DefaultHide, + /// + /// Default as a sorting column. + /// + DefaultSort = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.DefaultSort, + /// + /// Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + /// + WidthStretch = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.WidthStretch, + /// + /// Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + /// + WidthFixed = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.WidthFixed, + /// + /// Disable manual resizing. + /// + NoResize = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoResize, + /// + /// Disable manual reordering this column, this will also prevent other columns from crossing over this column. + /// + NoReorder = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoReorder, + /// + /// Disable ability to hide/disable this column. + /// + NoHide = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoHide, + /// + /// Disable clipping for this column (all NoClip columns will render in a same draw command). + /// + NoClip = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoClip, + /// + /// Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). + /// + NoSort = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoSort, + /// + /// Disable ability to sort in the ascending direction. + /// + NoSortAscending = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoSortAscending, + /// + /// Disable ability to sort in the descending direction. + /// + NoSortDescending = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoSortDescending, + /// + /// TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call. + /// + NoHeaderLabel = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoHeaderLabel, + /// + /// Disable header text width contribution to automatic column width. + /// + NoHeaderWidth = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoHeaderWidth, + /// + /// Make the initial sort direction Ascending when first sorting on this column (default). + /// + PreferSortAscending = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.PreferSortAscending, + /// + /// Make the initial sort direction Descending when first sorting on this column. + /// + PreferSortDescending = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.PreferSortDescending, + /// + /// Use current Indent value when entering cell (default for column 0). + /// + IndentEnable = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IndentEnable, + /// + /// Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + /// + IndentDisable = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IndentDisable, + /// + /// TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row. + /// + AngledHeader = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.AngledHeader, + /// + /// Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. + /// + IsEnabled = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IsEnabled, + /// + /// Status: is visible == is enabled AND not clipped by scrolling. + /// + IsVisible = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IsVisible, + /// + /// Status: is currently part of the sort specs + /// + IsSorted = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IsSorted, + /// + /// Status: is hovered by mouse + /// + IsHovered = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IsHovered, + /// + /// Selects the width mask presentation behavior. + /// + WidthMask = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.WidthMask, + /// + /// Selects the indent mask presentation behavior. + /// + IndentMask = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.IndentMask, + /// + /// Selects the status mask presentation behavior. + /// + StatusMask = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.StatusMask, + /// + /// Selects the no direct resize presentation behavior. + /// + NoDirectResize = (int)Inno.Native.ImGui.ImGuiTableColumnFlags.NoDirectResize, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs new file mode 100644 index 000000000..e483ba5fa --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableFlags.cs @@ -0,0 +1,158 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported table flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTableFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTableFlags.None, + /// + /// Enable resizing columns. + /// + Resizable = (int)Inno.Native.ImGui.ImGuiTableFlags.Resizable, + /// + /// Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) + /// + Reorderable = (int)Inno.Native.ImGui.ImGuiTableFlags.Reorderable, + /// + /// Enable hiding/disabling columns in context menu. + /// + Hideable = (int)Inno.Native.ImGui.ImGuiTableFlags.Hideable, + /// + /// Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. + /// + Sortable = (int)Inno.Native.ImGui.ImGuiTableFlags.Sortable, + /// + /// Disable persisting columns order, width, visibility and sort settings in the .ini file. + /// + NoSavedSettings = (int)Inno.Native.ImGui.ImGuiTableFlags.NoSavedSettings, + /// + /// Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). + /// + ContextMenuInBody = (int)Inno.Native.ImGui.ImGuiTableFlags.ContextMenuInBody, + /// + /// Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) + /// + RowBg = (int)Inno.Native.ImGui.ImGuiTableFlags.RowBg, + /// + /// Draw horizontal borders between rows. + /// + BordersInnerH = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersInnerH, + /// + /// Draw horizontal borders at the top and bottom. + /// + BordersOuterH = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersOuterH, + /// + /// Draw vertical borders between columns. + /// + BordersInnerV = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersInnerV, + /// + /// Draw vertical borders on the left and right sides. + /// + BordersOuterV = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersOuterV, + /// + /// Draw horizontal borders. + /// + BordersH = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersH, + /// + /// Draw vertical borders. + /// + BordersV = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersV, + /// + /// Draw inner borders. + /// + BordersInner = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersInner, + /// + /// Draw outer borders. + /// + BordersOuter = (int)Inno.Native.ImGui.ImGuiTableFlags.BordersOuter, + /// + /// Draw all borders. + /// + Borders = (int)Inno.Native.ImGui.ImGuiTableFlags.Borders, + /// + /// Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style + /// + NoBordersInBody = (int)Inno.Native.ImGui.ImGuiTableFlags.NoBordersInBody, + /// + /// Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style + /// + NoBordersInBodyUntilResize = (int)Inno.Native.ImGui.ImGuiTableFlags.NoBordersInBodyUntilResize, + /// + /// Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. + /// + SizingFixedFit = (int)Inno.Native.ImGui.ImGuiTableFlags.SizingFixedFit, + /// + /// Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. + /// + SizingFixedSame = (int)Inno.Native.ImGui.ImGuiTableFlags.SizingFixedSame, + /// + /// Columns default to _WidthStretch with default weights proportional to each columns contents widths. + /// + SizingStretchProp = (int)Inno.Native.ImGui.ImGuiTableFlags.SizingStretchProp, + /// + /// Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). + /// + SizingStretchSame = (int)Inno.Native.ImGui.ImGuiTableFlags.SizingStretchSame, + /// + /// Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. + /// + NoHostExtendX = (int)Inno.Native.ImGui.ImGuiTableFlags.NoHostExtendX, + /// + /// Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. + /// + NoHostExtendY = (int)Inno.Native.ImGui.ImGuiTableFlags.NoHostExtendY, + /// + /// Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. + /// + NoKeepColumnsVisible = (int)Inno.Native.ImGui.ImGuiTableFlags.NoKeepColumnsVisible, + /// + /// Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + /// + PreciseWidths = (int)Inno.Native.ImGui.ImGuiTableFlags.PreciseWidths, + /// + /// Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + /// + NoClip = (int)Inno.Native.ImGui.ImGuiTableFlags.NoClip, + /// + /// Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. + /// + PadOuterX = (int)Inno.Native.ImGui.ImGuiTableFlags.PadOuterX, + /// + /// Default if BordersOuterV is off. Disable outermost padding. + /// + NoPadOuterX = (int)Inno.Native.ImGui.ImGuiTableFlags.NoPadOuterX, + /// + /// Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + /// + NoPadInnerX = (int)Inno.Native.ImGui.ImGuiTableFlags.NoPadInnerX, + /// + /// Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. + /// + ScrollX = (int)Inno.Native.ImGui.ImGuiTableFlags.ScrollX, + /// + /// Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + /// + ScrollY = (int)Inno.Native.ImGui.ImGuiTableFlags.ScrollY, + /// + /// Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + /// + SortMulti = (int)Inno.Native.ImGui.ImGuiTableFlags.SortMulti, + /// + /// Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + /// + SortTristate = (int)Inno.Native.ImGui.ImGuiTableFlags.SortTristate, + /// + /// Highlight column headers when hovered (may evolve into a fuller highlight) + /// + HighlightHoveredColumn = (int)Inno.Native.ImGui.ImGuiTableFlags.HighlightHoveredColumn, + /// + /// Selects the sizing mask presentation behavior. + /// + SizingMask = (int)Inno.Native.ImGui.ImGuiTableFlags.SizingMask, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs new file mode 100644 index 000000000..c664e0e69 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTableRowFlags.cs @@ -0,0 +1,18 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported table row flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTableRowFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTableRowFlags.None, + /// + /// Identify header row (set default background color + width of its contents accounted differently for auto column width) + /// + Headers = (int)Inno.Native.ImGui.ImGuiTableRowFlags.Headers, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs new file mode 100644 index 000000000..2179ce729 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiTreeNodeFlags.cs @@ -0,0 +1,98 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported tree node flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiTreeNodeFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.None, + /// + /// Draw as selected + /// + Selected = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.Selected, + /// + /// Draw frame with background (e.g. for CollapsingHeader) + /// + Framed = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.Framed, + /// + /// Hit testing to allow subsequent widgets to overlap this one + /// + AllowOverlap = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.AllowOverlap, + /// + /// Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack + /// + NoTreePushOnOpen = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.NoTreePushOnOpen, + /// + /// Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) + /// + NoAutoOpenOnLog = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.NoAutoOpenOnLog, + /// + /// Default node to be open + /// + DefaultOpen = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.DefaultOpen, + /// + /// Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. + /// + OpenOnDoubleClick = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.OpenOnDoubleClick, + /// + /// Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. + /// + OpenOnArrow = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.OpenOnArrow, + /// + /// No collapsing, no arrow (use as a convenience for leaf nodes). + /// + Leaf = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.Leaf, + /// + /// Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! + /// + Bullet = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.Bullet, + /// + /// Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. + /// + FramePadding = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.FramePadding, + /// + /// Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode. + /// + SpanAvailWidth = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.SpanAvailWidth, + /// + /// Extend hit box to the left-most and right-most edges (cover the indent area). + /// + SpanFullWidth = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.SpanFullWidth, + /// + /// Narrow hit box + narrow hovering highlight, will only cover the label text. + /// + SpanLabelWidth = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.SpanLabelWidth, + /// + /// Frame will span all columns of its container table (label will still fit in current column) + /// + SpanAllColumns = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.SpanAllColumns, + /// + /// Label will span all columns of its container table + /// + LabelSpanAllColumns = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.LabelSpanAllColumns, + /// + /// Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining. + /// + NavLeftJumpsToParent = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.NavLeftJumpsToParent, + /// + /// Selects the collapsing header presentation behavior. + /// + CollapsingHeader = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.CollapsingHeader, + /// + /// No lines drawn + /// + DrawLinesNone = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.DrawLinesNone, + /// + /// Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees). + /// + DrawLinesFull = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.DrawLinesFull, + /// + /// Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees). + /// + DrawLinesToNodes = (int)Inno.Native.ImGui.ImGuiTreeNodeFlags.DrawLinesToNodes, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs new file mode 100644 index 000000000..4fc22b7c6 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs @@ -0,0 +1,130 @@ +using System; + +namespace Inno.Editor.ImGui; +/// +/// Defines the supported window flags choices at the Editor presentation boundary. +/// +[Flags] +public enum ImGuiWindowFlags : int +{ + /// + /// Uses the ordinary behavior without additional options. + /// + None = (int)Inno.Native.ImGui.ImGuiWindowFlags.None, + /// + /// Disable title-bar + /// + NoTitleBar = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoTitleBar, + /// + /// Disable user resizing with the lower-right grip + /// + NoResize = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoResize, + /// + /// Disable user moving the window + /// + NoMove = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoMove, + /// + /// Disable scrollbars (window can still scroll with mouse or programmatically) + /// + NoScrollbar = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoScrollbar, + /// + /// Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. + /// + NoScrollWithMouse = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoScrollWithMouse, + /// + /// Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). + /// + NoCollapse = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoCollapse, + /// + /// Resize every window to its content every frame + /// + AlwaysAutoResize = (int)Inno.Native.ImGui.ImGuiWindowFlags.AlwaysAutoResize, + /// + /// Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). + /// + NoBackground = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoBackground, + /// + /// Never load/save settings in .ini file + /// + NoSavedSettings = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoSavedSettings, + /// + /// Disable catching mouse, hovering test with pass through. + /// + NoMouseInputs = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoMouseInputs, + /// + /// Has a menu-bar + /// + MenuBar = (int)Inno.Native.ImGui.ImGuiWindowFlags.MenuBar, + /// + /// Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. + /// + HorizontalScrollbar = (int)Inno.Native.ImGui.ImGuiWindowFlags.HorizontalScrollbar, + /// + /// Disable taking focus when transitioning from hidden to visible state + /// + NoFocusOnAppearing = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoFocusOnAppearing, + /// + /// Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) + /// + NoBringToFrontOnFocus = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoBringToFrontOnFocus, + /// + /// Always show vertical scrollbar (even if ContentSize.y < Size.y) + /// + AlwaysVerticalScrollbar = (int)Inno.Native.ImGui.ImGuiWindowFlags.AlwaysVerticalScrollbar, + /// + /// Always show horizontal scrollbar (even if ContentSize.x < Size.x) + /// + AlwaysHorizontalScrollbar = (int)Inno.Native.ImGui.ImGuiWindowFlags.AlwaysHorizontalScrollbar, + /// + /// No keyboard/gamepad navigation within the window + /// + NoNavInputs = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoNavInputs, + /// + /// No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab) + /// + NoNavFocus = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoNavFocus, + /// + /// Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + /// + UnsavedDocument = (int)Inno.Native.ImGui.ImGuiWindowFlags.UnsavedDocument, + /// + /// Disable docking of this window + /// + NoDocking = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoDocking, + /// + /// Selects the no nav presentation behavior. + /// + NoNav = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoNav, + /// + /// Selects the no decoration presentation behavior. + /// + NoDecoration = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoDecoration, + /// + /// Selects the no inputs presentation behavior. + /// + NoInputs = (int)Inno.Native.ImGui.ImGuiWindowFlags.NoInputs, + /// + /// Don't use! For internal use by Begin()/NewFrame() + /// + DockNodeHost = (int)Inno.Native.ImGui.ImGuiWindowFlags.DockNodeHost, + /// + /// Don't use! For internal use by BeginChild() + /// + ChildWindow = (int)Inno.Native.ImGui.ImGuiWindowFlags.ChildWindow, + /// + /// Don't use! For internal use by BeginTooltip() + /// + Tooltip = (int)Inno.Native.ImGui.ImGuiWindowFlags.Tooltip, + /// + /// Don't use! For internal use by BeginPopup() + /// + Popup = (int)Inno.Native.ImGui.ImGuiWindowFlags.Popup, + /// + /// Don't use! For internal use by BeginPopupModal() + /// + Modal = (int)Inno.Native.ImGui.ImGuiWindowFlags.Modal, + /// + /// Don't use! For internal use by BeginMenu() + /// + ChildMenu = (int)Inno.Native.ImGui.ImGuiWindowFlags.ChildMenu, +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs new file mode 100644 index 000000000..60448fd4e --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs @@ -0,0 +1,204 @@ +using System; +using System.Numerics; +using BGCS.Runtime; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// A pointer-free drawing borrow valid only in its original window, UI session and frame. +/// +public readonly unsafe struct EditorDrawList +{ + private readonly EditorUiSession m_session; + private readonly int m_frame; + private readonly uint m_window; + internal EditorDrawList(EditorUiSession session) + { + session.EnsureDrawing(); + if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope == 0) + throw new InvalidOperationException("Drawing requires an active native frame."); + m_session = session; + m_frame = NativeImGui.GetFrameCount(); + m_window = NativeImGui.Raw.GetCurrentWindow()->ID; + } + + /// + /// Adds a straight screen-space stroke. + /// + /// + /// The first endpoint. + /// + /// + /// The final endpoint. + /// + /// + /// Packed RGBA color. + /// + /// + /// Stroke width in UI pixels. + /// + public void Line( + Vector2 start, + Vector2 end, + uint color, + float thickness = 1f + ) => ImGui.DrawNativeLine(Resolve(), start, end, color, thickness); + /// + /// Adds a filled rectangular surface. + /// + /// + /// The upper-left corner. + /// + /// + /// The lower-right corner. + /// + /// + /// Packed RGBA color. + /// + /// + /// Corner radius. + /// + /// + /// Whether only the two top corners should be rounded. + /// + public void FilledRectangle( + Vector2 minimum, + Vector2 maximum, + uint color, + float rounding = 0f, + bool topCornersOnly = false + ) => ImGui.DrawNativeFilledRectangle(Resolve(), minimum, maximum, color, rounding, topCornersOnly ? (int)ImDrawFlags.RoundCornersTop : 0); + /// + /// Adds a rectangular outline. + /// + /// + /// The upper-left corner. + /// + /// + /// The lower-right corner. + /// + /// + /// Packed RGBA color. + /// + /// + /// Corner radius. + /// + /// + /// Stroke width in UI pixels. + /// + public void Rectangle( + Vector2 minimum, + Vector2 maximum, + uint color, + float rounding = 0f, + float thickness = 1f + ) => ImGui.DrawNativeRectangle(Resolve(), minimum, maximum, color, rounding, 0, thickness); + /// + /// Adds a filled circle with backend-selected tessellation. + /// + /// + /// The screen-space center. + /// + /// + /// The nonnegative radius. + /// + /// + /// Packed RGBA color. + /// + public void FilledCircle( + Vector2 center, + float radius, + uint color + ) { + ImVec2C native = ImGui.ToNative(center); + NativeImGui.Raw.AddCircleFilled(Resolve(), &native, radius, color, 0); + } + + /// + /// Adds literal text using the current font and an explicit scale. + /// + /// + /// Screen-space text origin. + /// + /// + /// Packed RGBA color. + /// + /// + /// Literal managed text; encoded only for this synchronous call. + /// + /// + /// Positive scale relative to the current font size. + /// + public void Text( + Vector2 position, + uint color, + string text, + float scale = 1f + ) { + ImDrawList* draw = Resolve(); + ArgumentNullException.ThrowIfNull(text); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(scale); + ImVec2C native = ImGui.ToNative(position); + using NativeStringLease encoded = Utf8Codec.EncodeNullTerminated(text, allowsNull: false); + NativeImGui.Raw.AddText(draw, NativeImGui.Raw.GetFont(), NativeImGui.GetFontSize() * scale, &native, color, (sbyte*)encoded.pointer, null, 0f, null); + } + + /// + /// Restricts subsequent drawing to one screen rectangle. + /// + /// + /// The clip minimum. + /// + /// + /// The clip maximum. + /// + /// + /// Whether to intersect the enclosing clip. + /// + public void PushClip( + Vector2 minimum, + Vector2 maximum, + bool intersect = true + ) { + ImVec2C first = ImGui.ToNative(minimum), last = ImGui.ToNative(maximum); + NativeImGui.Raw.PushClipRect(Resolve(), &first, &last, intersect ? (byte)1 : (byte)0); + } + + /// + /// Restores the enclosing clip after a matching PushClip. + /// + public void PopClip() => NativeImGui.Raw.PopClipRect(Resolve()); + /// + /// Begins ordered draw channels within this window. + /// + /// + /// The positive number of layers. + /// + public void SplitChannels(int count) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count); + NativeImGui.Raw.ChannelsSplit(Resolve(), count); + } + + /// + /// Selects a channel from the current split. + /// + /// + /// The zero-based channel. + /// + public void SetChannel(int index) => NativeImGui.Raw.ChannelsSetCurrent(Resolve(), index); + /// + /// Merges the current split back into ordered draw commands. + /// + public void MergeChannels() => NativeImGui.Raw.ChannelsMerge(Resolve()); + private ImDrawList* Resolve() + { + if (m_session is null) + throw new InvalidOperationException("A default drawing borrow has no owner."); + m_session.EnsureDrawing(); + if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope == 0 || NativeImGui.GetFrameCount() != m_frame || NativeImGui.Raw.GetCurrentWindow()->ID != m_window) + throw new InvalidOperationException("The drawing borrow belongs to a different window or frame."); + return NativeImGui.Raw.GetWindowDrawList(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs new file mode 100644 index 000000000..6d815af16 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs @@ -0,0 +1,135 @@ +using System.Numerics; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Copies presentation values and creates frame-bounded drawing borrows. +/// +public static unsafe partial class ImGui +{ + /// + /// Copies the current UI layout metrics. + /// + /// + /// Value metrics valid independently from the native style pointer. + /// + public static EditorUiLayout GetLayout() + { + EditorUiSession.current.EnsureDrawing(); + return new EditorUiLayout(NativeImGui.Raw.GetStyle()); + } + + /// + /// Copies the input facts for this frame. + /// + /// + /// A neutral value snapshot; no pointer or callback is retained. + /// + public static EditorUiInput GetInput() + { + EditorUiSession.current.EnsureDrawing(); + return new EditorUiInput(NativeImGui.Raw.GetIO()); + } + + /// + /// Copies an unmodulated style color for compound presentation. + /// + /// + /// The semantic style color slot. + /// + /// + /// The copied linear vector of UI color components. + /// + public static Vector4 GetStyleColorValue(ImGuiCol color) + { + EditorUiSession.current.EnsureDrawing(); + return FromNative(NativeImGui.Raw.GetStyle()->Colors[(int)color]); + } + + /// + /// Gets the logical viewport identity and copied position for the current window. + /// + /// + /// The viewport ID and its screen-space origin. + /// + public static (uint id, Vector2 position) GetViewport() + { + EditorUiSession.current.EnsureDrawing(); + var viewport = NativeImGui.Raw.GetWindowViewport(); + return (viewport->ID, FromNative(viewport->Pos)); + } + + /// + /// Gets the table and current row bounds needed by row overlays. + /// + /// + /// The table's horizontal bounds and current row's vertical bounds. + /// + public static (float minimumX, float maximumX, float rowMinimum, float rowMaximum) GetTableBounds() + { + EditorUiSession.current.EnsureDrawing(); + var table = NativeImGui.Raw.GetCurrentTable(); + if (table is null) + throw new System.InvalidOperationException("A table row layout requires an active table."); + return (table->OuterRect.Min.X, table->OuterRect.Max.X, table->RowPosY1, table->RowPosY2); + } + + /// + /// Gets copied inner and work bounds for the current window. + /// + /// + /// Screen-space horizontal bounds and work maximum. + /// + public static (float minimumX, float maximumX, Vector2 workMaximum, Vector2 padding) GetWindowBounds() + { + EditorUiSession.current.EnsureDrawing(); + var window = NativeImGui.Raw.GetCurrentWindow(); + return (window->InnerRect.Min.X, window->InnerRect.Max.X, FromNative(window->WorkRect.Max), FromNative(window->WindowPadding)); + } + + /// + /// Borrows drawing commands for the current window and frame without exposing native memory. + /// + /// + /// A value that rejects use after a window, frame, context or session change. + /// + public static EditorDrawList GetDrawList() => new(EditorUiSession.current); + /// + /// Checks the active payload's logical protocol without exposing its native buffer. + /// + /// + /// The exact drag protocol name. + /// + /// + /// True when a drag of that protocol is currently active. + /// + public static bool HasDragPayload(string protocol) + { + EditorUiSession.current.EnsureDrawing(); + var payload = NativeImGui.Raw.GetDragDropPayload(); + if (payload is null) + return false; + using var text = BGCS.Runtime.Utf8Codec.EncodeNullTerminated(protocol, allowsNull: false); + return NativeImGui.Raw.IsDataType(payload, (sbyte*)text.pointer) != 0; + } + + /// + /// Measures literal text at a relative font scale. + /// + /// + /// The literal text to measure. + /// + /// + /// Scale relative to the current font. + /// + /// + /// The copied width and height in UI pixels. + /// + public static Vector2 MeasureText( + string text, + float scale + ) { + EditorUiSession.current.EnsureDrawing(); + return NativeImGui.CalcTextSize(text) * scale; + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs index 96c799174..ab32102f4 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs @@ -1,17 +1,15 @@ using System; using System.Numerics; using BGCS.Runtime; - using Inno.Native.ImGui; using RawImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui; - /// /// Exposes a script-safe, pointer-free subset of Dear ImGui for custom Editor tools. /// Every call is valid only while an Editor drawing callback is active. /// -public static unsafe class ImGui +public static unsafe partial class ImGui { /// /// Draws unformatted text. @@ -19,7 +17,11 @@ public static unsafe class ImGui /// /// Literal text to draw; percent characters are displayed without format-string interpretation. /// - public static void Text(string text) => RawImGui.TextUnformatted(text); + public static void Text(string text) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TextUnformatted(text); + } /// /// Draws a standard button. @@ -36,7 +38,10 @@ public static unsafe class ImGui public static bool Button( string label, Vector2 size = default - ) => RawImGui.Button(label, size); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.Button(label, size); + } /// /// Draws a compact button. @@ -47,7 +52,11 @@ public static bool Button( /// /// The visible label and optional ImGui identity suffix. /// - public static bool SmallButton(string label) => RawImGui.SmallButton(label); + public static bool SmallButton(string label) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.SmallButton(label); + } /// /// Draws and edits a Boolean value. @@ -64,7 +73,10 @@ public static bool Button( public static bool Checkbox( string label, ref bool value - ) => RawImGui.Checkbox(label, ref value); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.Checkbox(label, ref value); + } /// /// Draws and edits a bounded UTF-8 text value. @@ -144,10 +156,25 @@ public static bool InputTextWithHint( /// /// The integer displayed by the control and replaced after an accepted edit. /// + /// + /// Increment used by the ordinary step button. + /// + /// + /// Increment used by the accelerated step button. + /// + /// + /// Text editing options for the numeric field. + /// public static bool InputInt( string label, - ref int value - ) => RawImGui.InputInt(label, ref value, 1, 100, 0); + ref int value, + int step = 1, + int fastStep = 100, + ImGuiInputTextFlags flags = ImGuiInputTextFlags.None + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.InputInt(label, ref value, step, fastStep, (int)flags); + } /// /// Draws and edits a floating-point value. @@ -164,7 +191,10 @@ ref int value public static bool InputFloat( string label, ref float value - ) => RawImGui.InputFloat(label, ref value, 0f, 0f, "%.3f", 0); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.InputFloat(label, ref value, 0f, 0f, "%.3f", 0); + } /// /// Draws a floating-point drag control. @@ -193,8 +223,10 @@ public static bool DragFloat( float speed = 1f, float minimum = 0f, float maximum = 0f - ) - => RawImGui.DragFloat(label, ref value, speed, minimum, maximum); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.DragFloat(label, ref value, speed, minimum, maximum); + } /// /// Draws a bounded floating-point slider. @@ -223,8 +255,10 @@ public static bool SliderFloat( float minimum, float maximum, ImGuiSliderFlags flags = ImGuiSliderFlags.None - ) - => RawImGui.SliderFloat(label, ref value, minimum, maximum, (int)flags); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.SliderFloat(label, ref value, minimum, maximum, (int)flags); + } /// /// Draws and edits a four-component color. @@ -286,7 +320,8 @@ public static bool ColorEditLinear4( display.X == original.X ? value.X : SrgbToLinear(display.X), display.Y == original.Y ? value.Y : SrgbToLinear(display.Y), display.Z == original.Z ? value.Z : SrgbToLinear(display.Z), - display.W); + display.W + ); return true; } @@ -305,17 +340,27 @@ public static bool ColorEditLinear4( /// /// when child contents should be submitted. /// + /// + /// Scroll and window policies for the child. + /// public static bool BeginChild( string id, Vector2 size = default, - ImGuiChildFlags flags = ImGuiChildFlags.None - ) - => RawImGui.BeginChild(id, size, (int)(flags)); + ImGuiChildFlags flags = ImGuiChildFlags.None, + ImGuiWindowFlags windowFlags = ImGuiWindowFlags.None + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.BeginChild(id, size, (int)flags, (int)windowFlags); + } /// /// Ends the current child region. /// - public static void EndChild() => RawImGui.EndChild(); + public static void EndChild() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndChild(); + } /// /// Begins a table. @@ -335,18 +380,28 @@ public static bool BeginChild( /// /// when table contents should be submitted. /// + /// + /// Optional inner scrolling width; zero uses the outer size. + /// public static bool BeginTable( string id, int columns, ImGuiTableFlags flags = ImGuiTableFlags.None, - Vector2 size = default - ) - => RawImGui.BeginTable(id, columns, (int)flags, size); + Vector2 size = default, + float innerWidth = 0f + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.BeginTable(id, columns, (int)flags, size, innerWidth); + } /// /// Ends the current table. /// - public static void EndTable() => RawImGui.EndTable(); + public static void EndTable() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndTable(); + } /// /// Declares one table column. @@ -364,18 +419,36 @@ public static void TableSetupColumn( string label, ImGuiTableColumnFlags flags = ImGuiTableColumnFlags.None, float widthOrWeight = 0f - ) - => RawImGui.TableSetupColumn(label, (int)(flags), widthOrWeight); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TableSetupColumn(label, (int)(flags), widthOrWeight); + } /// /// Draws the table header row from configured column labels. /// - public static void TableHeadersRow() => RawImGui.TableHeadersRow(); + public static void TableHeadersRow() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TableHeadersRow(); + } /// /// Advances to the next table row. /// - public static void TableNextRow() => RawImGui.TableNextRow(); + /// + /// Row presentation flags. + /// + /// + /// Minimum row height in UI pixels. + /// + public static void TableNextRow( + ImGuiTableRowFlags flags = ImGuiTableRowFlags.None, + float minimumHeight = 0f + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TableNextRow((int)flags, minimumHeight); + } /// /// Advances to the next table column. @@ -383,7 +456,11 @@ public static void TableSetupColumn( /// /// when the column is visible. /// - public static bool TableNextColumn() => RawImGui.TableNextColumn(); + public static bool TableNextColumn() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.TableNextColumn(); + } /// /// Begins a combo popup. @@ -404,13 +481,19 @@ public static bool BeginCombo( string label, string preview, ImGuiComboFlags flags = ImGuiComboFlags.None - ) - => RawImGui.BeginCombo(label, preview, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.BeginCombo(label, preview, (int)(flags)); + } /// /// Ends the current combo popup. /// - public static void EndCombo() => RawImGui.EndCombo(); + public static void EndCombo() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndCombo(); + } /// /// Draws one selectable item. @@ -427,12 +510,18 @@ public static bool BeginCombo( /// /// when the item is activated. /// + /// + /// The interaction bounds, or zero for the ordinary content size. + /// public static bool Selectable( string label, bool selected = false, - ImGuiSelectableFlags flags = ImGuiSelectableFlags.None - ) - => RawImGui.Selectable(label, selected, (int)flags); + ImGuiSelectableFlags flags = ImGuiSelectableFlags.None, + Vector2 size = default + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.Selectable(label, selected, (int)flags, size); + } /// /// Draws a collapsible section header. @@ -449,8 +538,10 @@ public static bool Selectable( public static bool CollapsingHeader( string label, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.None - ) - => RawImGui.CollapsingHeader(label, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.CollapsingHeader(label, (int)(flags)); + } /// /// Draws a tree node and pushes its tree scope when it is open. @@ -467,12 +558,19 @@ public static bool CollapsingHeader( public static bool TreeNode( string label, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.None - ) => RawImGui.TreeNodeEx(label, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.TreeNodeEx(label, (int)(flags)); + } /// /// Ends the current open tree-node scope. /// - public static void TreePop() => RawImGui.TreePop(); + public static void TreePop() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.TreePop(); + } /// /// Begins a tab bar. @@ -489,12 +587,19 @@ public static bool TreeNode( public static bool BeginTabBar( string id, ImGuiTabBarFlags flags = ImGuiTabBarFlags.None - ) => RawImGui.BeginTabBar(id, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.BeginTabBar(id, (int)(flags)); + } /// /// Ends the current tab bar. /// - public static void EndTabBar() => RawImGui.EndTabBar(); + public static void EndTabBar() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndTabBar(); + } /// /// Begins one tab item. @@ -542,7 +647,11 @@ public static unsafe bool BeginTabItem( /// /// Ends the current tab item. /// - public static void EndTabItem() => RawImGui.EndTabItem(); + public static void EndTabItem() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndTabItem(); + } /// /// Opens a named popup. @@ -556,7 +665,10 @@ public static unsafe bool BeginTabItem( public static void OpenPopup( string id, ImGuiPopupFlags flags = ImGuiPopupFlags.None - ) => RawImGui.OpenPopup(id, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.OpenPopup(id, (int)(flags)); + } /// /// Begins a named popup. @@ -573,17 +685,28 @@ public static void OpenPopup( public static bool BeginPopup( string id, ImGuiWindowFlags flags = ImGuiWindowFlags.None - ) => RawImGui.BeginPopup(id, (int)(flags)); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.BeginPopup(id, (int)(flags)); + } /// /// Ends the current popup. /// - public static void EndPopup() => RawImGui.EndPopup(); + public static void EndPopup() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndPopup(); + } /// /// Closes the current popup. /// - public static void CloseCurrentPopup() => RawImGui.CloseCurrentPopup(); + public static void CloseCurrentPopup() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.CloseCurrentPopup(); + } /// /// Begins a disabled block. @@ -591,22 +714,38 @@ public static bool BeginPopup( /// /// Whether controls in the block are disabled. /// - public static void BeginDisabled(bool disabled = true) => RawImGui.BeginDisabled(disabled); + public static void BeginDisabled(bool disabled = true) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.BeginDisabled(disabled); + } /// /// Ends the current disabled block. /// - public static void EndDisabled() => RawImGui.EndDisabled(); + public static void EndDisabled() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndDisabled(); + } /// /// Begins an item group. /// - public static void BeginGroup() => RawImGui.BeginGroup(); + public static void BeginGroup() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.BeginGroup(); + } /// /// Ends the current item group. /// - public static void EndGroup() => RawImGui.EndGroup(); + public static void EndGroup() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.EndGroup(); + } /// /// Pushes a string identity onto the ImGui ID stack. @@ -614,7 +753,11 @@ public static bool BeginPopup( /// /// The stable identity used to locate the requested value. /// - public static void PushId(string id) => RawImGui.PushID(id); + public static void PushId(string id) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushID(id); + } /// /// Pushes an integer identity onto the ImGui ID stack. @@ -622,12 +765,20 @@ public static bool BeginPopup( /// /// The stable identity used to locate the requested value. /// - public static void PushId(int id) => RawImGui.PushID(id); + public static void PushId(int id) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushID(id); + } /// /// Pops one identity from the ImGui ID stack. /// - public static void PopId() => RawImGui.PopID(); + public static void PopId() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PopID(); + } /// /// Pushes one color override. @@ -641,7 +792,10 @@ public static bool BeginPopup( public static void PushStyleColor( ImGuiCol color, Vector4 value - ) => RawImGui.PushStyleColor((int)(color), value); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushStyleColor((int)(color), value); + } /// /// Pops color overrides. @@ -649,7 +803,11 @@ Vector4 value /// /// The positive number of overrides to pop. /// - public static void PopStyleColor(int count = 1) => RawImGui.PopStyleColor(count); + public static void PopStyleColor(int count = 1) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PopStyleColor(count); + } /// /// Pushes one scalar style override. @@ -663,7 +821,10 @@ Vector4 value public static void PushStyleVar( ImGuiStyleVar style, float value - ) => RawImGui.PushStyleVar((int)(style), value); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushStyleVar((int)(style), value); + } /// /// Pushes one two-component style override. @@ -677,7 +838,10 @@ float value public static void PushStyleVar( ImGuiStyleVar style, Vector2 value - ) => RawImGui.PushStyleVar((int)(style), value); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PushStyleVar((int)(style), value); + } /// /// Pops style-variable overrides. @@ -685,7 +849,11 @@ Vector2 value /// /// The positive number of overrides to pop. /// - public static void PopStyleVar(int count = 1) => RawImGui.PopStyleVar(count); + public static void PopStyleVar(int count = 1) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.PopStyleVar(count); + } /// /// Places the next item on the same line. @@ -699,12 +867,19 @@ Vector2 value public static void SameLine( float offset = 0f, float spacing = -1f - ) => RawImGui.SameLine(offset, spacing); + ) { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SameLine(offset, spacing); + } /// /// Draws a horizontal separator. /// - public static void Separator() => RawImGui.Separator(); + public static void Separator() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.Separator(); + } /// /// Draws a labeled horizontal separator. @@ -712,12 +887,20 @@ public static void SameLine( /// /// The label text validated by the separator text operation. /// - public static void SeparatorText(string label) => RawImGui.SeparatorText(label); + public static void SeparatorText(string label) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SeparatorText(label); + } /// /// Adds one standard vertical spacing unit. /// - public static void Spacing() => RawImGui.Spacing(); + public static void Spacing() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.Spacing(); + } /// /// Advances layout by an invisible size. @@ -725,7 +908,11 @@ public static void SameLine( /// /// The size consumed by dummy; ownership remains with the caller unless explicitly stated otherwise. /// - public static void Dummy(Vector2 size) => RawImGui.Dummy(size); + public static void Dummy(Vector2 size) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.Dummy(size); + } /// /// Sets the width of the next item. @@ -733,7 +920,11 @@ public static void SameLine( /// /// The width in logical units or pixels required by this operation. /// - public static void SetNextItemWidth(float width) => RawImGui.SetNextItemWidth(width); + public static void SetNextItemWidth(float width) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetNextItemWidth(width); + } /// /// Gets the remaining content size in the current region. @@ -741,7 +932,11 @@ public static void SameLine( /// /// The remaining local width and height. /// - public static Vector2 GetContentRegionAvailable() => RawImGui.GetContentRegionAvail(); + public static Vector2 GetContentRegionAvailable() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetContentRegionAvail(); + } /// /// Gets the current local cursor position. @@ -749,7 +944,11 @@ public static void SameLine( /// /// The local cursor position. /// - public static Vector2 GetCursorPosition() => RawImGui.GetCursorPos(); + public static Vector2 GetCursorPosition() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetCursorPos(); + } /// /// Sets the current local cursor position. @@ -757,7 +956,11 @@ public static void SameLine( /// /// The position consumed by set cursor position; ownership remains with the caller unless explicitly stated otherwise. /// - public static void SetCursorPosition(Vector2 position) => RawImGui.SetCursorPos(position); + public static void SetCursorPosition(Vector2 position) + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetCursorPos(position); + } /// /// Gets the current screen-space cursor position. @@ -765,7 +968,11 @@ public static void SameLine( /// /// The screen-space cursor position. /// - public static Vector2 GetCursorScreenPosition() => RawImGui.GetCursorScreenPos(); + public static Vector2 GetCursorScreenPosition() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetCursorScreenPos(); + } /// /// Gets the minimum screen-space corner of the previous item. @@ -773,7 +980,11 @@ public static void SameLine( /// /// The item minimum corner. /// - public static Vector2 GetItemMinimum() => RawImGui.GetItemRectMin(); + public static Vector2 GetItemMinimum() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetItemRectMin(); + } /// /// Gets the maximum screen-space corner of the previous item. @@ -781,7 +992,11 @@ public static void SameLine( /// /// The item maximum corner. /// - public static Vector2 GetItemMaximum() => RawImGui.GetItemRectMax(); + public static Vector2 GetItemMaximum() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetItemRectMax(); + } /// /// Measures text using the current font. @@ -792,7 +1007,11 @@ public static void SameLine( /// /// The text text validated by the measure text operation. /// - public static Vector2 MeasureText(string text) => RawImGui.CalcTextSize(text); + public static Vector2 MeasureText(string text) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.CalcTextSize(text); + } /// /// Gets whether the previous item is hovered. @@ -803,7 +1022,11 @@ public static void SameLine( /// /// when the query succeeds. /// - public static bool IsItemHovered(ImGuiHoveredFlags flags = ImGuiHoveredFlags.None) => RawImGui.IsItemHovered((int)(flags)); + public static bool IsItemHovered(ImGuiHoveredFlags flags = ImGuiHoveredFlags.None) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsItemHovered((int)(flags)); + } /// /// Gets whether the previous item is active. @@ -811,7 +1034,11 @@ public static void SameLine( /// /// while the item owns an active interaction. /// - public static bool IsItemActive() => RawImGui.IsItemActive(); + public static bool IsItemActive() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsItemActive(); + } /// /// Gets whether the previous item was clicked with a mouse button. @@ -822,7 +1049,11 @@ public static void SameLine( /// /// when the item was clicked. /// - public static bool IsItemClicked(ImGuiMouseButton button = ImGuiMouseButton.Left) => RawImGui.IsItemClicked((int)(button)); + public static bool IsItemClicked(ImGuiMouseButton button = ImGuiMouseButton.Left) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsItemClicked((int)(button)); + } /// /// Gets whether a mouse button is held in the current Editor UI frame, including input consumed by UI controls. @@ -833,7 +1064,11 @@ public static void SameLine( /// /// True while the button is held; callers must also check their item's active or hovered state before editing. /// - public static bool IsMouseButtonDown(ImGuiMouseButton button = ImGuiMouseButton.Left) => RawImGui.IsMouseDown((int)button); + public static bool IsMouseButtonDown(ImGuiMouseButton button = ImGuiMouseButton.Left) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsMouseDown((int)button); + } /// /// Gets accumulated pointer displacement from the current UI drag's starting position for timeline and canvas controls. @@ -851,7 +1086,10 @@ public static void SameLine( public static Vector2 GetMouseDragDelta( ImGuiMouseButton button = ImGuiMouseButton.Left, float threshold = -1f - ) => RawImGui.GetMouseDragDelta((int)button, threshold); + ) { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.GetMouseDragDelta((int)button, threshold); + } /// /// Converts a floating-point RGBA color to Dear ImGui packed color order. @@ -862,7 +1100,11 @@ public static Vector2 GetMouseDragDelta( /// /// The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. /// - public static uint ToPackedColor(Vector4 color) => RawImGui.ColorConvertFloat4ToU32(color); + public static uint ToPackedColor(Vector4 color) + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.ColorConvertFloat4ToU32(color); + } /// /// Adds a line to the current window draw list. @@ -884,8 +1126,10 @@ public static void DrawLine( Vector2 end, uint color, float thickness = 1f - ) - => ImGui.DrawNativeLine(RawImGui.Raw.GetWindowDrawList(), start, end, color, thickness); + ) { + EditorUiSession.current.EnsureDrawing(); + ImGui.DrawNativeLine(RawImGui.Raw.GetWindowDrawList(), start, end, color, thickness); + } /// /// Adds a rectangle outline to the current window draw list. @@ -911,8 +1155,10 @@ public static void DrawRectangle( uint color, float rounding = 0f, float thickness = 1f - ) - => ImGui.DrawNativeRectangle(RawImGui.Raw.GetWindowDrawList(), minimum, maximum, color, rounding, 0, thickness); + ) { + EditorUiSession.current.EnsureDrawing(); + ImGui.DrawNativeRectangle(RawImGui.Raw.GetWindowDrawList(), minimum, maximum, color, rounding, 0, thickness); + } /// /// Adds a filled rectangle to the current window draw list. @@ -934,8 +1180,44 @@ public static void DrawFilledRectangle( Vector2 maximum, uint color, float rounding = 0f - ) - => ImGui.DrawNativeFilledRectangle(RawImGui.Raw.GetWindowDrawList(), minimum, maximum, color, rounding, 0); + ) { + EditorUiSession.current.EnsureDrawing(); + ImGui.DrawNativeFilledRectangle(RawImGui.Raw.GetWindowDrawList(), minimum, maximum, color, rounding, 0); + } + + /// + /// Draws a cubic curve without exposing a native draw list or borrowed pointer. + /// + /// + /// The first screen-space endpoint. + /// + /// + /// The control point beside the first endpoint. + /// + /// + /// The control point beside the final endpoint. + /// + /// + /// The final screen-space endpoint. + /// + /// + /// The packed presentation color. + /// + /// + /// The positive stroke width. + /// + public static void DrawCubicCurve( + Vector2 start, + Vector2 firstControl, + Vector2 secondControl, + Vector2 end, + uint color, + float thickness = 1f + ) { + EditorUiSession.current.EnsureDrawing(); + ImVec2C a = ToNative(start), b = ToNative(firstControl), c = ToNative(secondControl), d = ToNative(end); + RawImGui.Raw.AddBezierCubic(RawImGui.Raw.GetWindowDrawList(), &a, &b, &c, &d, color, thickness, 0); + } /// /// Adds text to the current window draw list. @@ -953,16 +1235,25 @@ public static void DrawText( Vector2 position, uint color, string text - ) => ImGui.DrawNativeText(RawImGui.Raw.GetWindowDrawList(), position, color, text); - - internal static ImVec2C ToNative(Vector2 value) => new() { X = value.X, Y = value.Y }; - - internal static ImVec4C ToNative(Vector4 value) => new() { X = value.X, Y = value.Y, Z = value.Z, W = value.W }; + ) { + EditorUiSession.current.EnsureDrawing(); + ImGui.DrawNativeText(RawImGui.Raw.GetWindowDrawList(), position, color, text); + } + internal static ImVec2C ToNative(Vector2 value) => new() + { + X = value.X, + Y = value.Y + }; + internal static ImVec4C ToNative(Vector4 value) => new() + { + X = value.X, + Y = value.Y, + Z = value.Z, + W = value.W + }; internal static Vector2 FromNative(ImVec2C value) => new(value.X, value.Y); - internal static Vector4 FromNative(ImVec4C value) => new(value.X, value.Y, value.Z, value.W); - internal static void DrawNativeLine( ImDrawList* drawList, Vector2 start, @@ -1085,8 +1376,7 @@ internal static bool BeginNativeWindow( bool modal ) { using NativeStringLease nativeLabel = Utf8Codec.EncodeNullTerminated(label, allowsNull: false); - return (modal ? RawImGui.Raw.BeginPopupModal((sbyte*)nativeLabel.pointer, null, (int)flags) - : RawImGui.Raw.Begin((sbyte*)nativeLabel.pointer, null, (int)flags)) != 0; + return (modal ? RawImGui.Raw.BeginPopupModal((sbyte*)nativeLabel.pointer, null, (int)flags) : RawImGui.Raw.Begin((sbyte*)nativeLabel.pointer, null, (int)flags)) != 0; } internal static uint HashNativeId( @@ -1098,6 +1388,5 @@ internal static uint HashNativeId( } private static float LinearToSrgb(float value) => value <= 0.0031308f ? value * 12.92f : 1.055f * MathF.Pow(value, 1f / 2.4f) - 0.055f; - private static float SrgbToLinear(float value) => value <= 0.04045f ? value / 12.92f : MathF.Pow((value + 0.055f) / 1.055f, 2.4f); } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Inno.Editor.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.ImGui/Inno.Editor.ImGui.csproj index 5dfa97cd4..201a6a18e 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Inno.Editor.ImGui.csproj +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Inno.Editor.ImGui.csproj @@ -11,28 +11,22 @@ - - - - + + + + + - - - - + + + + + diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs index 52d83c98f..2ab2d7265 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs @@ -1,27 +1,11 @@ using Inno.Scripting.Api; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -[assembly: ScriptingApiNamespace( - "InnoEditor.ImGui", - "Inno.Editor.ImGui", - ScriptingApiScope.Editor)] -[assembly: ScriptingApiNamespace( - "InnoEditor.ImGui", - "Inno.Editor.ImGui.ImGuiWidget", - ScriptingApiScope.Editor)] -[assembly: ScriptingApiNamespace( - "InnoEditor.ImGui", - "Inno.Native.ImGui", - ScriptingApiScope.Editor)] -[assembly: ScriptingApiNamespace( - "InnoEditor.ImGui", - "Inno.Adapter.Presentation.ImGui", - ScriptingApiScope.Editor)] - +[assembly: ScriptingApiNamespace("InnoEditor.ImGui", "Inno.Editor.ImGui", ScriptingApiScope.Editor)] +[assembly: ScriptingApiNamespace("InnoEditor.ImGui", "Inno.Editor.ImGui.ImGuiWidget", ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorUiSession), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorPalette), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorStyleMetrics), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(Inno.Editor.ImGui.ImGui), ScriptingApiScope.Editor)] @@ -48,3 +32,20 @@ [assembly: ScriptingApiExport(typeof(ImGuiTableFlags), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ImGuiTreeNodeFlags), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(ImGuiWindowFlags), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorDrawList), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorUiLayout), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(EditorUiInput), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(TransformGizmoOperation), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(TransformGizmoSpace), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(TransformGizmoOptions), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(TransformGizmoResult), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiButtonFlags), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiCond), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiFocusedFlags), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiKey), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiMouseCursor), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiTableBgTarget), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiTableRowFlags), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiFont), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiFontScope), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ImGuiFontStyle), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs index 5dbdda45e..b6cfab1b8 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs @@ -1,20 +1,17 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Globalization; - using Inno.Core.Identity; using Inno.Editor.Interactions; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; namespace Inno.Editor.ImGui; - /// /// Bridges managed editor drag sessions to the native ImGui payload API. /// public static class EditorDragDropRenderer { private const string C_IDENTITY_PAYLOAD_PREFIX = "INNO_ID_"; - /// /// Publishes managed drag data for the most recently submitted ImGui item. /// @@ -36,10 +33,7 @@ public static bool Source( Action? drawPreview = null ) { ArgumentNullException.ThrowIfNull(data); - return EditorWidget.DragDropSource( - GetPayloadType(data.sourceIdentity.domainId), - () => interaction.BeginDrag(data).runtimeId, - drawPreview); + return EditorUI.current.widgets.DragDropSource(GetPayloadType(data.sourceIdentity.domainId), () => interaction.BeginDrag(data).runtimeId, drawPreview); } /// @@ -60,25 +54,18 @@ public static EditorDropWidgetResult Target( ) { if (!interaction.TryGetActiveDragIdentity(out RuntimeIdentity activeIdentity)) return EditorDropWidgetResult.none; - bool delivered = EditorWidget.DragDropTarget( - GetPayloadType(activeIdentity.domainId), - out int runtimeId, - out bool isPreviewing, - drawDefaultHighlight: false); + bool delivered = EditorUI.current.widgets.DragDropTarget(GetPayloadType(activeIdentity.domainId), out int runtimeId, out bool isPreviewing, drawDefaultHighlight: false); if (!isPreviewing && !delivered) return EditorDropWidgetResult.none; if (runtimeId <= 0) return EditorDropWidgetResult.none; var identity = new RuntimeIdentity(activeIdentity.domainId, runtimeId); EditorDropStatus status = interaction.QueryDrop(identity, placement); - EditorDropResult result = delivered && status.canDrop - ? interaction.Drop(identity, placement) - : EditorDropResult.rejected; + EditorDropResult result = delivered && status.canDrop ? interaction.Drop(identity, placement) : EditorDropResult.rejected; return new EditorDropWidgetResult(isPreviewing, status, result); } - private static string GetPayloadType(IdentityDomainId domainId) - => C_IDENTITY_PAYLOAD_PREFIX + domainId.value.ToString("X8", CultureInfo.InvariantCulture); + private static string GetPayloadType(IdentityDomainId domainId) => C_IDENTITY_PAYLOAD_PREFIX + domainId.value.ToString("X8", CultureInfo.InvariantCulture); } /// @@ -112,17 +99,14 @@ EditorDropResult result /// Gets whether a compatible native payload is hovering the target. /// public bool isPreviewing { get; } - /// /// Gets the managed drop compatibility status. /// public EditorDropStatus status { get; } - /// /// Gets the delivered drop result. /// public EditorDropResult result { get; } - /// /// Gets an inactive drop target result. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs index 53d90660d..bf661b25f 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs @@ -1,28 +1,33 @@ +using System.Linq; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Numerics; - using Inno.Editor.Interactions; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui; - /// /// Renders immutable editor menu models through ImGui. /// -public static unsafe class EditorMenuRenderer +public sealed unsafe class EditorMenuRenderer { - private static readonly Dictionary m_contextSearches = []; + private readonly EditorUiSession m_session; + private readonly Dictionary<(string Owner, uint Popup), string> m_contextSearches = []; + internal EditorMenuRenderer(EditorUiSession session) => m_session = session; + internal void RetireOwner(string owner) + { + foreach (var key in m_contextSearches.Keys.Where(value => value.Owner == owner).ToArray()) + m_contextSearches.Remove(key); + } + internal void Clear() => m_contextSearches.Clear(); private readonly record struct MenuSection( EditorInteraction interaction, IReadOnlyList items ); - /// /// Draws a resolved right-click menu for the most recently submitted ImGui item. /// @@ -35,21 +40,23 @@ IReadOnlyList items /// /// while the context popup is open and its items were drawn. /// - public static bool ContextMenu( + public bool ContextMenu( string id, EditorInteraction interaction ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); uint popupId = NativeImGui.GetID(id); if (!ShouldResolveItemContextMenu(id)) { - m_contextSearches.Remove(popupId); + m_contextSearches.Remove((m_session.owner, popupId)); return false; } + EditorMenuModel menu = interaction.BuildMenu(); if (menu.items.Count == 0) return false; - if (!EditorWidget.BeginContextMenu(id)) + if (!EditorUI.current.widgets.BeginContextMenu(id)) return false; try { @@ -57,8 +64,9 @@ EditorInteraction interaction } finally { - EditorWidget.EndContextMenu(); + EditorUI.current.widgets.EndContextMenu(); } + return true; } @@ -77,37 +85,35 @@ EditorInteraction interaction /// /// while the context popup is open and at least one command group was drawn. /// - public static bool ContextMenu( + public bool ContextMenu( string id, EditorInteraction scopeInteraction, EditorInteraction itemInteraction ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); uint popupId = NativeImGui.GetID(id); if (!ShouldResolveItemContextMenu(id)) { - m_contextSearches.Remove(popupId); + m_contextSearches.Remove((m_session.owner, popupId)); return false; } + EditorMenuModel scopeMenu = scopeInteraction.BuildMenu(); EditorMenuModel itemMenu = itemInteraction.BuildMenu(); if (scopeMenu.items.Count == 0 && itemMenu.items.Count == 0) return false; - if (!EditorWidget.BeginContextMenu(id)) + if (!EditorUI.current.widgets.BeginContextMenu(id)) return false; try { - DrawContextMenuContents( - popupId, - [ - new MenuSection(scopeInteraction, scopeMenu.items), - new MenuSection(itemInteraction, itemMenu.items) - ]); + DrawContextMenuContents(popupId, [new MenuSection(scopeInteraction, scopeMenu.items), new MenuSection(itemInteraction, itemMenu.items)]); } finally { - EditorWidget.EndContextMenu(); + EditorUI.current.widgets.EndContextMenu(); } + return true; } @@ -123,21 +129,23 @@ EditorInteraction itemInteraction /// /// while the background context popup is open and its items were drawn. /// - public static bool WindowContextMenu( + public bool WindowContextMenu( string id, EditorInteraction interaction ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); uint popupId = NativeImGui.GetID(id); if (!ShouldResolveWindowContextMenu(id)) { - m_contextSearches.Remove(popupId); + m_contextSearches.Remove((m_session.owner, popupId)); return false; } + EditorMenuModel menu = interaction.BuildMenu(); if (menu.items.Count == 0) return false; - if (!EditorWidget.BeginWindowContextMenu(id)) + if (!EditorUI.current.widgets.BeginWindowContextMenu(id)) return false; try { @@ -145,38 +153,31 @@ EditorInteraction interaction } finally { - EditorWidget.EndContextMenu(); + EditorUI.current.widgets.EndContextMenu(); } + return true; } - private static void DrawContextMenuContents( + private void DrawContextMenuContents( uint popupId, IReadOnlyList sections ) { - string search = NativeImGui.IsWindowAppearing() - ? string.Empty - : m_contextSearches.GetValueOrDefault(popupId, string.Empty); - NativeImGui.PushStyleVar( - (int)(ImGuiStyleVar.FramePadding), - EditorWidget.style.menuSearchFramePadding); + string search = NativeImGui.IsWindowAppearing() ? string.Empty : m_contextSearches.GetValueOrDefault((m_session.owner, popupId), string.Empty); + NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.menuSearchFramePadding); try { if (NativeImGui.IsWindowAppearing()) NativeImGui.SetKeyboardFocusHere(); - _ = EditorWidget.SearchInput( - $"context-menu-{popupId}", - "Search commands…", - ref search, - width: -1f); + _ = EditorUI.current.widgets.SearchInput($"context-menu-{popupId}", "Search commands…", ref search, width: -1f); } finally { NativeImGui.PopStyleVar(); } - m_contextSearches[popupId] = search; - NativeImGui.Separator(); + m_contextSearches[(m_session.owner, popupId)] = search; + NativeImGui.Separator(); bool searching = !string.IsNullOrWhiteSpace(search); bool drewSection = false; foreach (MenuSection section in sections) @@ -192,29 +193,28 @@ IReadOnlyList sections NativeImGui.CloseCurrentPopup(); return; } + DrawItems(section.interaction, section.items); drewSection = true; } } - private static bool ShouldResolveItemContextMenu(string id) - => NativeImGui.IsPopupOpen(id) || - NativeImGui.IsItemHovered() && NativeImGui.IsMouseReleased((int)(Inno.Native.ImGui.ImGuiMouseButton.Right)); - - private static bool ShouldResolveWindowContextMenu(string id) - => NativeImGui.IsPopupOpen(id) || - NativeImGui.IsWindowHovered() && - !NativeImGui.IsAnyItemHovered() && - NativeImGui.IsMouseReleased((int)(Inno.Native.ImGui.ImGuiMouseButton.Right)); - + private bool ShouldResolveItemContextMenu(string id) => NativeImGui.IsPopupOpen(id) + || NativeImGui.IsItemHovered() + && NativeImGui.IsMouseReleased((int)(Inno.Native.ImGui.ImGuiMouseButton.Right)); + private bool ShouldResolveWindowContextMenu(string id) => NativeImGui.IsPopupOpen(id) + || NativeImGui.IsWindowHovered() + && !NativeImGui.IsAnyItemHovered() + && NativeImGui.IsMouseReleased((int)(Inno.Native.ImGui.ImGuiMouseButton.Right)); /// /// Draws the complete editor main menu bar for the supplied menu context. /// /// /// The main-menu interaction area. /// - public static void MainMenu(EditorInteraction interaction) + public void MainMenu(EditorInteraction interaction) { + m_session.EnsureDrawing(); if (!NativeImGui.BeginMainMenuBar()) return; try @@ -228,7 +228,7 @@ public static void MainMenu(EditorInteraction interaction) } } - private static void DrawCenteredToolbar( + private void DrawCenteredToolbar( EditorInteraction interaction, IReadOnlyList items ) { @@ -240,7 +240,6 @@ IReadOnlyList items float centeredOffset = (NativeImGui.GetWindowWidth() - width) * 0.5f; float minimumOffset = NativeImGui.GetCursorPosX() + style->ItemSpacing.X; NativeImGui.SameLine(MathF.Max(centeredOffset, minimumOffset), 0f); - for (int i = 0; i < items.Count; i++) { if (i > 0) @@ -249,7 +248,7 @@ IReadOnlyList items } } - private static void DrawToolbarItem( + private void DrawToolbarItem( EditorInteraction interaction, EditorToolbarItem item, Vector2 size @@ -263,14 +262,9 @@ Vector2 size bool active = NativeImGui.IsItemActive(); if (disabled) NativeImGui.EndDisabled(); - if (item.status.isChecked || hovered || active) { - Vector4 background = active - ? EditorPalette.accentActive - : hovered - ? EditorPalette.accentHovered - : EditorPalette.accent; + Vector4 background = active ? EditorPalette.accentActive : hovered ? EditorPalette.accentHovered : EditorPalette.accent; ImGui.DrawNativeFilledRectangle( NativeImGui.Raw.GetWindowDrawList(), minimum, @@ -282,37 +276,27 @@ Vector2 size } uint color = NativeImGui.GetColorU32((int)(disabled ? ImGuiCol.TextDisabled : ImGuiCol.Text)); - EditorWidget.AddGlyphCentered( - NativeImGui.Raw.GetWindowDrawList(), - NativeImGui.Raw.GetFont(), - NativeImGui.GetFontSize(), - GetToolbarIcon(item.icon), - minimum + size * 0.5f, - color); - - if (hovered && EditorWidget.BeginMenuTooltip()) + EditorUI.current.widgets.AddGlyphCentered(NativeImGui.GetFontSize(), GetToolbarIcon(item.icon), minimum + size * 0.5f, color); + if (hovered && EditorUI.current.widgets.BeginMenuTooltip()) { - string tooltip = interaction.TryGetShortcut(item.actionId, out HotKeyGesture shortcut) - ? $"{item.tooltip} ({shortcut})" - : item.tooltip; + string tooltip = interaction.TryGetShortcut(item.actionId, out HotKeyGesture shortcut) ? $"{item.tooltip} ({shortcut})" : item.tooltip; NativeImGui.TextUnformatted(tooltip); - EditorWidget.EndMenuTooltip(); + EditorUI.current.widgets.EndMenuTooltip(); } + if (pressed && item.status.isEnabled) interaction.Enqueue(item.actionId); } - private static string GetToolbarIcon(EditorToolbarIcon icon) - => icon switch - { - EditorToolbarIcon.Play => ImGuiIcon.Play, - EditorToolbarIcon.Stop => ImGuiIcon.Stop, - EditorToolbarIcon.Pause => ImGuiIcon.Pause, - EditorToolbarIcon.Step => ImGuiIcon.ForwardStep, - EditorToolbarIcon.Edit => ImGuiIcon.Pen, - _ => string.Empty - }; - + private string GetToolbarIcon(EditorToolbarIcon icon) => icon switch + { + EditorToolbarIcon.Play => ImGuiIcon.Play, + EditorToolbarIcon.Stop => ImGuiIcon.Stop, + EditorToolbarIcon.Pause => ImGuiIcon.Pause, + EditorToolbarIcon.Step => ImGuiIcon.ForwardStep, + EditorToolbarIcon.Edit => ImGuiIcon.Pen, + _ => string.Empty + }; /// /// Recursively draws resolved menu nodes into the currently open popup or menu. /// @@ -322,10 +306,11 @@ private static string GetToolbarIcon(EditorToolbarIcon icon) /// /// The immutable menu nodes to draw in display order. /// - public static void DrawItems( + public void DrawItems( EditorInteraction interaction, IReadOnlyList items ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(items); for (int i = 0; i < items.Count; i++) { @@ -347,20 +332,17 @@ IReadOnlyList items { NativeImGui.EndMenu(); } + continue; } - string shortcut = interaction.TryGetShortcut(item.actionId, out HotKeyGesture gesture) - ? gesture.ToString() - : string.Empty; - if (NativeImGui.MenuItem( - item.label, - shortcut, - item.status.isChecked, - item.status.isEnabled)) + string shortcut = interaction.TryGetShortcut(item.actionId, out HotKeyGesture gesture) ? gesture.ToString() : string.Empty; + if (NativeImGui.MenuItem(item.label, shortcut, item.status.isChecked, item.status.isEnabled)) { interaction.Enqueue(item.actionId, item.argument); } + + DrawUnavailableReason(item.status); } } @@ -379,16 +361,17 @@ IReadOnlyList items /// /// when a command was selected. /// - public static bool DrawSearchItems( + public bool DrawSearchItems( EditorInteraction interaction, IReadOnlyList items, string search ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(items); return DrawSearchItems(interaction, items, search ?? string.Empty, string.Empty); } - private static bool DrawSearchItems( + private bool DrawSearchItems( EditorInteraction interaction, IReadOnlyList items, string search, @@ -397,26 +380,53 @@ string parentPath for (int i = 0; i < items.Count; i++) { EditorMenuItem item = items[i]; - string path = string.IsNullOrEmpty(parentPath) - ? item.label - : $"{parentPath}/{item.label}"; + string path = string.IsNullOrEmpty(parentPath) ? item.label : $"{parentPath}/{item.label}"; if (item.children.Count > 0) { if (DrawSearchItems(interaction, item.children, search, path)) return true; continue; } - if (!item.status.isEnabled || - !string.IsNullOrWhiteSpace(search) && - path.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0) + + if (!string.IsNullOrWhiteSpace(search) && path.IndexOf(search, StringComparison.OrdinalIgnoreCase) < 0) { continue; } - if (!NativeImGui.Selectable(path)) - continue; - interaction.Enqueue(item.actionId, item.argument); - return true; + + bool chosen; + NativeImGui.BeginDisabled(!item.status.isEnabled); + try + { + chosen = NativeImGui.Selectable(path); + } + finally + { + NativeImGui.EndDisabled(); + } + + DrawUnavailableReason(item.status); + if (chosen && item.status.isEnabled) + { + interaction.Enqueue(item.actionId, item.argument); + return true; + } } + return false; } + + private void DrawUnavailableReason(EditorActionState state) + { + if (state.isEnabled || string.IsNullOrWhiteSpace(state.unavailableReason) || !NativeImGui.IsItemHovered((int)ImGuiHoveredFlags.AllowWhenDisabled)) + return; + NativeImGui.BeginTooltip(); + try + { + EditorUI.current.widgets.WrappedText(state.unavailableReason); + } + finally + { + NativeImGui.EndTooltip(); + } + } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalHost.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalHost.cs index 54cef0f5a..d3f9772a8 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalHost.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalHost.cs @@ -1,17 +1,14 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; - using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Interactions; namespace Inno.Editor.ImGui; - internal sealed class EditorModalHost { private readonly Dictionary m_transitions = new(StringComparer.Ordinal); - internal bool Update( IReadOnlyList modals, double now @@ -19,13 +16,13 @@ double now bool newBlockingModalRequested = false; for (int i = 0; i < modals.Count; i++) { - if (modals[i].TryGetPresentation(out EditorModalExtension.Presentation candidate) && - candidate.isVisible && candidate.blocksInteraction) + if (modals[i].TryGetPresentation(out EditorModalExtension.Presentation candidate) && candidate.isVisible && candidate.blocksInteraction) { newBlockingModalRequested = true; break; } } + bool blocksInteraction = false; for (int i = 0; i < modals.Count; i++) { @@ -40,6 +37,7 @@ double now if (presentation.blocksInteraction && transition.isVisible) blocksInteraction = true; } + return blocksInteraction; } @@ -56,24 +54,19 @@ double now EditorModalRenderer.Close(extension.id, extension.title); continue; } + Transition transition = GetTransition(extension.id); if (!transition.isVisible) { EditorModalRenderer.Close(extension.id, extension.title); continue; } - EditorModalRenderer.Draw( - extension.id, - extension.title, - transition.GetAlpha(now), - extension, - presentation, - context); + + EditorModalRenderer.Draw(extension.id, extension.title, transition.GetAlpha(now), extension, presentation, context); } } internal void Clear() => m_transitions.Clear(); - private Transition GetTransition(string id) { if (m_transitions.TryGetValue(id, out Transition? transition)) @@ -88,7 +81,6 @@ private sealed class Transition private double m_visibleAt; private double m_hideAt; private bool m_requested; - internal bool isVisible { get; private set; } internal void HideImmediately() @@ -110,17 +102,16 @@ double now m_visibleAt = now; isVisible = true; } + m_hideAt = double.PositiveInfinity; } else if (m_requested) { - m_hideAt = Math.Max( - now, - m_visibleAt + EditorWidget.style.modalMinimumVisibleSeconds); + m_hideAt = Math.Max(now, m_visibleAt + EditorUI.current.widgets.style.modalMinimumVisibleSeconds); } + m_requested = requested; - if (isVisible && !requested && - now >= m_hideAt + EditorWidget.style.modalFadeOutSeconds) + if (isVisible && !requested && now >= m_hideAt + EditorUI.current.widgets.style.modalFadeOutSeconds) { isVisible = false; } @@ -130,10 +121,11 @@ internal float GetAlpha(double now) { if (m_requested || now <= m_hideAt) { - double fadeIn = (now - m_visibleAt) / EditorWidget.style.modalFadeInSeconds; + double fadeIn = (now - m_visibleAt) / EditorUI.current.widgets.style.modalFadeInSeconds; return (float)Math.Clamp(fadeIn, EditorPalette.opacityFaint, EditorPalette.opacityOpaque); } - double fadeOut = (now - m_hideAt) / EditorWidget.style.modalFadeOutSeconds; + + double fadeOut = (now - m_hideAt) / EditorUI.current.widgets.style.modalFadeOutSeconds; return (float)Math.Clamp(1.0 - fadeOut, EditorPalette.opacityFaint, EditorPalette.opacityOpaque); } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalRenderer.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalRenderer.cs index af09e970c..b3b110ed8 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalRenderer.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorModalRenderer.cs @@ -1,15 +1,13 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; - using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Interactions; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui; - /// /// Renders centered editor modals consistently on the main viewport. /// @@ -48,38 +46,31 @@ EditorContext context ArgumentException.ThrowIfNullOrWhiteSpace(title); ArgumentNullException.ThrowIfNull(modal); ArgumentNullException.ThrowIfNull(context); - string popupId = $"{title}##{id}"; ImGuiViewport* viewport = NativeImGui.Raw.GetMainViewport(); Vector2 center = ImGui.FromNative(viewport->WorkPos) + ImGui.FromNative(viewport->WorkSize) * 0.5f; if (presentation.blocksInteraction) NativeImGui.OpenPopup(popupId, (int)(ImGuiPopupFlags.NoReopen)); NativeImGui.SetNextWindowViewport(viewport->ID); - ImGuiCond placementCondition = presentation.canMove || presentation.canResize - ? ImGuiCond.Appearing - : ImGuiCond.Always; + ImGuiCond placementCondition = presentation.canMove || presentation.canResize ? ImGuiCond.Appearing : ImGuiCond.Always; NativeImGui.SetNextWindowPos(center, (int)(placementCondition), new Vector2(0.5f, 0.5f)); Vector2 initialSize = presentation.initialSize; if (initialSize.X > 0f && initialSize.Y > 0f) { - NativeImGui.SetNextWindowSize( - initialSize * EditorWidget.style.zoom, - (int)(placementCondition)); + NativeImGui.SetNextWindowSize(initialSize * EditorUI.current.widgets.style.zoom, (int)(placementCondition)); } else if (!presentation.canResize) { - NativeImGui.SetNextWindowSize( - new Vector2(EditorWidget.style.modalWidth, 0f), - (int)(ImGuiCond.Always)); + NativeImGui.SetNextWindowSize(new Vector2(EditorUI.current.widgets.style.modalWidth, 0f), (int)(ImGuiCond.Always)); } + if (presentation.canResize) { Vector2 maximumSize = Vector2.Max(Vector2.One, ImGui.FromNative(viewport->WorkSize)); - Vector2 minimumSize = Vector2.Min( - presentation.minimumSize * EditorWidget.style.zoom, - maximumSize); + Vector2 minimumSize = Vector2.Min(presentation.minimumSize * EditorUI.current.widgets.style.zoom, maximumSize); ImGui.ConstrainNativeWindowSize(minimumSize, maximumSize, null, null); } + NativeImGui.PushStyleVar((int)(ImGuiStyleVar.Alpha), alpha); ImGuiWindowFlags flags = ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoCollapse; if (!presentation.allowScrolling) @@ -92,12 +83,19 @@ EditorContext context if (presentation.initialSize.X <= 0f || presentation.initialSize.Y <= 0f) flags |= ImGuiWindowFlags.AlwaysAutoResize; } + bool beganWindow = false; try { - bool visible = presentation.blocksInteraction - ? ImGui.BeginNativeWindow(popupId, flags, modal: true) - : ImGui.BeginNativeWindow(popupId, flags | ImGuiWindowFlags.NoSavedSettings, modal: false); + bool visible = presentation.blocksInteraction ? ImGui.BeginNativeWindow( + popupId, + flags, + modal: true + ) : ImGui.BeginNativeWindow( + popupId, + flags | ImGuiWindowFlags.NoSavedSettings, + modal: false + ); beganWindow = !presentation.blocksInteraction || visible; if (visible) { @@ -113,6 +111,7 @@ EditorContext context else NativeImGui.End(); } + NativeImGui.PopStyleVar(); } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs new file mode 100644 index 000000000..762ee89ca --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs @@ -0,0 +1,59 @@ +using System.Numerics; +using Inno.Native.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Copies frame input facts used by presentation without exposing native input storage. +/// +public readonly struct EditorUiInput +{ + /// + /// Gets whether Alt is held. + /// + public bool alt { get; } + /// + /// Gets whether Control is held. + /// + public bool control { get; } + /// + /// Gets whether Shift is held. + /// + public bool shift { get; } + /// + /// Gets whether the platform super key is held. + /// + public bool super { get; } + /// + /// Gets whether an active control owns text input. + /// + public bool wantsText { get; } + /// + /// Gets the current pointer movement in UI units. + /// + public Vector2 mouseDelta { get; } + /// + /// Gets the vertical scroll movement. + /// + public float wheel { get; } + /// + /// Gets the frame duration in seconds. + /// + public float deltaTime { get; } + /// + /// Gets the drawable pixel scale relative to logical UI coordinates. + /// + public Vector2 framebufferScale { get; } + + internal unsafe EditorUiInput(ImGuiIO* io) + { + alt = io->KeyAlt != 0; + control = io->KeyCtrl != 0; + shift = io->KeyShift != 0; + super = io->KeySuper != 0; + wantsText = io->WantTextInput != 0; + mouseDelta = ImGui.FromNative(io->MouseDelta); + framebufferScale = ImGui.FromNative(io->DisplayFramebufferScale); + wheel = io->MouseWheel; + deltaTime = io->DeltaTime; + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs new file mode 100644 index 000000000..547b723f8 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs @@ -0,0 +1,185 @@ +using Inno.Core.Events; +using Inno.Editor.Interactions; +using EditorControls = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using System; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Owns editor controls and transient presentation state for one host, independently from other ImGui contexts. +/// +public sealed unsafe class EditorUiSession : IDisposable +{ + [ThreadStatic] + private static EditorUiSession? s_current; + private readonly int m_ownerThread = Environment.CurrentManagedThreadId; + private EventHub? m_lifecycleHub; + private Scope? m_scope; + private OwnerScope? m_ownerScope; + private nint m_nativeContext; + private bool m_disposed; + private string m_owner = "root"; + /// + /// Creates independent controls and style state; the presentation backend retains its native context ownership. + /// + public EditorUiSession() + { + widgets = new EditorControls(this); + menus = new EditorMenuRenderer(this); + } + + /// + /// Gets the current synchronous drawing scope; this accessor never creates or owns a session. + /// + /// + /// No UI drawing scope is active on the calling thread. + /// + public static EditorUiSession current => s_current ?? throw new InvalidOperationException("Editor drawing requires an active UI session scope."); + /// + /// Gets the controls whose transient state belongs exclusively to this session. + /// + public EditorControls widgets { get; } + + /// + /// Gets the menu presenter whose search state belongs exclusively to this session. + /// + [Inno.Scripting.Api.ScriptingApiIgnore] + public EditorMenuRenderer menus { get; } + /// + /// Gets whether this session has successfully retired its presentation state. + /// + public bool isDisposed => m_disposed; + internal string owner => m_owner; + + /// + /// Activates this session for a synchronous callback while preserving the enclosing scope. + /// + /// + /// An owner-thread scope that must be released in reverse activation order. + /// + /// + /// The session has retired. + /// + /// + /// The caller is not the owner thread. + /// + public IDisposable Enter() + { + EnsureOwner(); + ObjectDisposedException.ThrowIf(m_disposed, this); + var scope = new Scope(this, s_current, m_scope); + m_scope = scope; + s_current = this; + return scope; + } + + /// + /// Releases presentation state after all drawing scopes and domain edit gestures have ended. + /// + /// + /// A drawing scope is active or the caller is not the owner thread. + /// + public void Dispose() + { + EnsureOwner(); + if (m_disposed) + return; + if (m_scope is not null) + throw new InvalidOperationException("The UI session still owns an active drawing scope."); + widgets.Retire(); + menus.Clear(); + m_lifecycleHub?.Dispose(); + m_lifecycleHub = null; + m_disposed = true; + GC.SuppressFinalize(this); + } + + internal void Connect(EditorInteractions interactions) + { + EnsureOwner(); + if (m_lifecycleHub is not null) + throw new InvalidOperationException("The UI session already belongs to an interaction runtime."); + m_lifecycleHub = interactions.events.CreateHub(int.MaxValue); + m_lifecycleHub.Listen(_ => widgets.CommitEdits()); + } + + internal IDisposable EnterOwner(string owner) + { + EnsureDrawing(); + ArgumentException.ThrowIfNullOrWhiteSpace(owner); + var scope = new OwnerScope(this, m_owner, m_ownerScope); + m_owner = owner; + m_ownerScope = scope; + return scope; + } + + internal void RetireOwner(string owner) + { + widgets.RetireOwner(owner); + menus.RetireOwner(owner); + } + + internal void EnsureDrawing() + { + EnsureOwner(); + ObjectDisposedException.ThrowIf(m_disposed, this); + if (!ReferenceEquals(s_current, this)) + throw new InvalidOperationException("Controls must be drawn within their owning UI session scope."); + nint context = (nint)NativeImGui.Raw.GetCurrentContext(); + if (context == 0) + throw new InvalidOperationException("Editor controls require a live ImGui context."); + if (m_nativeContext == 0) + m_nativeContext = context; + else if (m_nativeContext != context) + throw new InvalidOperationException("The UI session belongs to a different ImGui context."); + } + + private void EnsureOwner() + { + if (Environment.CurrentManagedThreadId != m_ownerThread) + throw new InvalidOperationException("Editor UI sessions must be used and retired on their owner thread."); + } + + private sealed class Scope( + EditorUiSession owner, + EditorUiSession? previous, + Scope? previousScope + ) : IDisposable + { + private bool m_disposed; + /// + public void Dispose() + { + owner.EnsureOwner(); + if (m_disposed) + return; + if (!ReferenceEquals(s_current, owner) || !ReferenceEquals(owner.m_scope, this)) + throw new InvalidOperationException("UI drawing scopes must retire in reverse activation order."); + owner.m_scope = previousScope; + s_current = previous; + m_disposed = true; + } + } + + private sealed class OwnerScope( + EditorUiSession session, + string previous, + OwnerScope? previousScope + ) : IDisposable + { + private bool m_disposed; + /// + public void Dispose() + { + if (m_disposed) + return; + session.EnsureOwner(); + if (!ReferenceEquals(session.m_ownerScope, this)) + throw new InvalidOperationException("UI owner scopes must retire in reverse activation order."); + session.m_owner = previous; + session.m_ownerScope = previousScope; + m_disposed = true; + } + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs index 221f7b09b..baeb3650e 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs @@ -1,8 +1,8 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; - using Inno.Core.Events; using Inno.Core.Execution; using Inno.Core.Logging; @@ -10,13 +10,11 @@ using Inno.Extensibility.Types; using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using Inno.Editor.Interactions; using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui; - /// /// Presents the backend-independent editor interaction runtime through ImGui. /// @@ -24,11 +22,14 @@ public sealed unsafe class ImGuiEditorRuntime : EditorRuntime { private readonly Stopwatch m_timer = Stopwatch.StartNew(); private readonly EditorInteractionRuntime m_runtime; + private readonly EditorUiSession m_ui = new(); private readonly EditorModalHost m_modals = new(); + private readonly HashSet m_openOwners = new(StringComparer.Ordinal); + private readonly HashSet m_currentOwners = new(StringComparer.Ordinal); + private readonly List m_retiredOwners = []; private uint m_dockspaceId; private Vector2 m_dockspaceSize; private bool m_disposed; - /// /// Creates an ImGui editor runtime with stable host-owned extension services. /// @@ -45,7 +46,7 @@ public sealed unsafe class ImGuiEditorRuntime : EditorRuntime /// Services available to discovered extension constructors. /// /// - /// Thrown when , , or + /// Thrown when , , or /// is . /// public ImGuiEditorRuntime( @@ -53,20 +54,25 @@ public ImGuiEditorRuntime( TypeCatalog types, LogRouter logs, IEnumerable hostServices - ) - : base(context ?? throw new ArgumentNullException(nameof(context))) + ) : base(context ?? throw new ArgumentNullException(nameof(context))) { ArgumentNullException.ThrowIfNull(types); ArgumentNullException.ThrowIfNull(logs); ArgumentNullException.ThrowIfNull(hostServices); - m_runtime = new EditorInteractionRuntime(context, types, logs, hostServices); + var services = new List(hostServices) + { + m_ui + }; + m_runtime = new EditorInteractionRuntime(context, types, logs, services); + m_ui.Connect(m_runtime.interactions); + using IDisposable scope = m_ui.Enter(); + m_ui.widgets.SetupStyle(); } /// /// Gets the active presentation-independent interaction entry point. /// public EditorInteractions interactions => m_runtime.interactions; - /// /// Gets the number of active dockable panels. /// @@ -75,7 +81,11 @@ IEnumerable hostServices /// /// Starts value processing after validating the current state. /// - public override void Start() => m_runtime.Start(); + public override void Start() + { + using IDisposable scope = m_ui.Enter(); + m_runtime.Start(); + } /// /// Recomputes owned state from the current validated inputs. @@ -83,7 +93,11 @@ IEnumerable hostServices /// /// The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. /// - public override void Update(EditorFrame frame) => m_runtime.Update(frame); + public override void Update(EditorFrame frame) + { + using IDisposable scope = m_ui.Enter(); + m_runtime.Update(frame); + } /// /// Captures all stateful active modules and panels and flushes their project state to disk. @@ -93,6 +107,7 @@ IEnumerable hostServices /// public void SaveState() { + using IDisposable uiScope = m_ui.Enter(); ObjectDisposedException.ThrowIf(m_disposed, this); m_runtime.SaveState(); } @@ -110,7 +125,9 @@ public void SaveState() /// public void PrepareShutdown() { + using IDisposable uiScope = m_ui.Enter(); ObjectDisposedException.ThrowIf(m_disposed, this); + m_ui.widgets.CommitEdits(); m_runtime.PrepareShutdown(); } @@ -119,12 +136,13 @@ public void PrepareShutdown() /// public void Draw() { + using IDisposable uiScope = m_ui.Enter(); ObjectDisposedException.ThrowIf(m_disposed, this); - EditorWidget.ApplyPendingStyle(); + m_ui.widgets.BeginEditingFrame(); + EditorUI.current.widgets.ApplyPendingStyle(); IReadOnlyList modals = m_runtime.modals; double now = m_timer.Elapsed.TotalSeconds; bool blocksInteraction = m_modals.Update(modals, now); - if (blocksInteraction) NativeImGui.BeginDisabled(true); try @@ -132,7 +150,7 @@ public void Draw() // The main menu must reserve the viewport work area before the dockspace snapshots it. // Reversing this order makes the dockspace overlap the bottom of the menu and clip the // first docked tab bar row. - EditorMenuRenderer.MainMenu(interactions.For(ImGuiInteractionIds.C_MAIN_MENU_AREA)); + EditorUI.current.menus.MainMenu(interactions.For(ImGuiInteractionIds.C_MAIN_MENU_AREA)); DrawDockSpace(); DrawPanels(m_runtime.panels); } @@ -142,6 +160,7 @@ public void Draw() NativeImGui.EndDisabled(); } + m_ui.widgets.CompleteEditingFrame(); m_runtime.Flush(); // Menu actions can change modal visibility after the pre-draw transition update. modals = m_runtime.modals; @@ -162,9 +181,7 @@ private void DrawDockSpace() if (loaded != null) ResizeDockSplits(loaded, size, style->DockingSeparatorSize, ImGui.FromNative(style->WindowMinSize)); } - else if (m_dockspaceSize != Vector2.Zero - && (MathF.Abs(size.X - m_dockspaceSize.X) >= 0.5f - || MathF.Abs(size.Y - m_dockspaceSize.Y) >= 0.5f)) + else if (m_dockspaceSize != Vector2.Zero && (MathF.Abs(size.X - m_dockspaceSize.X) >= 0.5f || MathF.Abs(size.Y - m_dockspaceSize.Y) >= 0.5f)) { ImGuiDockNode* root = NativeImGui.Raw.DockBuilderGetNode(m_dockspaceId); if (root != null) @@ -183,12 +200,14 @@ private void DrawDockSpace() { NativeImGui.PopStyleColor(2); } + if (m_dockspaceSize == Vector2.Zero) { ImGuiDockNode* root = NativeImGui.Raw.DockBuilderGetNode(m_dockspaceId); if (root != null) ResizeDockSplits(root, size, style->DockingSeparatorSize, ImGui.FromNative(style->WindowMinSize)); } + m_dockspaceSize = size; } @@ -200,27 +219,23 @@ Vector2 minimumWindowSize ) { ImGuiDockNode* first = node->ChildNodes[0]; ImGuiDockNode* second = node->ChildNodes[1]; - if (first == null || second == null - || node->SplitAxis is not (ImGuiAxis.X or ImGuiAxis.Y)) + if (first == null || second == null || node->SplitAxis is not (ImGuiAxis.X or ImGuiAxis.Y)) return; bool horizontal = node->SplitAxis == ImGuiAxis.X; float firstReference = horizontal ? first->Size.X : first->Size.Y; float secondReference = horizontal ? second->Size.X : second->Size.Y; float previousAvailable = (horizontal ? node->Size.X : node->Size.Y) - separator; - if (firstReference <= 0f || secondReference <= 0f - || MathF.Abs(firstReference + secondReference - previousAvailable) > MathF.Max(1f, separator)) + if (firstReference <= 0f || secondReference <= 0f || MathF.Abs(firstReference + secondReference - previousAvailable) > MathF.Max(1f, separator)) { firstReference = horizontal ? first->SizeRef.X : first->SizeRef.Y; secondReference = horizontal ? second->SizeRef.X : second->SizeRef.Y; } + float available = (horizontal ? size.X : size.Y) - separator; - if (!float.IsFinite(firstReference) || !float.IsFinite(secondReference) - || firstReference <= 0f || secondReference <= 0f || available <= 1f) + if (!float.IsFinite(firstReference) || !float.IsFinite(secondReference) || firstReference <= 0f || secondReference <= 0f || available <= 1f) return; - float minimum = MathF.Min(available * 0.5f, - horizontal ? minimumWindowSize.X : minimumWindowSize.Y); - float firstExtent = Math.Clamp(available * firstReference / (firstReference + secondReference), - minimum, available - minimum); + float minimum = MathF.Min(available * 0.5f, horizontal ? minimumWindowSize.X : minimumWindowSize.Y); + float firstExtent = Math.Clamp(available * firstReference / (firstReference + secondReference), minimum, available - minimum); Vector2 firstSize = size; Vector2 secondSize = size; if (horizontal) @@ -233,6 +248,7 @@ Vector2 minimumWindowSize firstSize.Y = firstExtent; secondSize.Y = available - firstExtent; } + first->SizeRef = ImGui.ToNative(firstSize); second->SizeRef = ImGui.ToNative(secondSize); ResizeDockSplits(first, firstSize, separator, minimumWindowSize); @@ -247,6 +263,7 @@ Vector2 minimumWindowSize /// public void HandleKeyPressed(KeyPressedEvent keyEvent) { + using IDisposable uiScope = m_ui.Enter(); ArgumentNullException.ThrowIfNull(keyEvent); if (m_modals.Update(m_runtime.modals, m_timer.Elapsed.TotalSeconds)) return; @@ -255,6 +272,7 @@ public void HandleKeyPressed(KeyPressedEvent keyEvent) HotKeyGesture save = HotKeyGesture.Primary(KeyCode.S, context.keyboard); if (NativeImGui.Raw.GetIO()->WantTextInput != 0 && (keyEvent.key != save.key || keyEvent.modifiers != save.modifiers)) return; + m_ui.widgets.CommitEdits(); m_runtime.HandleKeyPressed(keyEvent); } @@ -271,7 +289,13 @@ public override void Dispose() m_modals.Clear(); try { - m_runtime.Dispose(); + using (m_ui.Enter()) + { + m_ui.widgets.CommitEdits(); + m_runtime.Dispose(); + } + + m_ui.Dispose(); } catch (Exception pendingRetirement) when (RetirementPendingException.Find(pendingRetirement) is not null) { @@ -282,19 +306,28 @@ public override void Dispose() m_disposed = true; throw; } + m_disposed = true; GC.SuppressFinalize(this); } private void DrawPanels(IReadOnlyList panels) { + m_currentOwners.Clear(); for (int i = 0; i < panels.Count; i++) { EditorPanelExtension extension = panels[i]; - if (!extension.isOpen || !extension.TryGetWindowPresentation( - out bool useWindowPadding, - out bool allowScrolling, - out Vector2 initialSize)) + m_currentOwners.Add(extension.id); + if (!extension.isOpen) + { + if (m_openOwners.Remove(extension.id)) + m_ui.RetireOwner(extension.id); + continue; + } + + m_openOwners.Add(extension.id); + using IDisposable ownerScope = m_ui.EnterOwner(extension.id); + if (!extension.TryGetWindowPresentation(out bool useWindowPadding, out bool allowScrolling, out Vector2 initialSize)) continue; bool isOpen = extension.isOpen; if (initialSize.X > 0 && initialSize.Y > 0) @@ -304,17 +337,26 @@ private void DrawPanels(IReadOnlyList panels) flags |= ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse; if (extension.TakeFocusRequest()) NativeImGui.SetNextWindowFocus(); - EditorWidget.PanelWindow(extension.title, ref isOpen, () => + EditorUI.current.widgets.PanelWindow(extension.title, ref isOpen, () => { - if (extension.Draw(context) && - NativeImGui.IsWindowFocused((int)(Inno.Native.ImGui.ImGuiFocusedFlags.RootAndChildWindows))) + if (extension.Draw(context) && NativeImGui.IsWindowFocused((int)(Inno.Native.ImGui.ImGuiFocusedFlags.RootAndChildWindows))) { - interactions.For( - $"panel/{extension.id}", - interactions.selection.selectedTarget).Focus(); + interactions.For($"panel/{extension.id}", interactions.selection.selectedTarget).Focus(); } }, flags, useWindowPadding); extension.isOpen = isOpen; } + + m_retiredOwners.Clear(); + foreach (string owner in m_openOwners) + if (!m_currentOwners.Contains(owner)) + m_retiredOwners.Add(owner); + foreach (string owner in m_retiredOwners) + { + m_ui.RetireOwner(owner); + m_openOwners.Remove(owner); + } + + m_retiredOwners.Clear(); } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs new file mode 100644 index 000000000..f26d83e9c --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs @@ -0,0 +1,54 @@ +using System.Numerics; +using Inno.Native.ImGui; + +namespace Inno.Editor.ImGui; +/// +/// Copies the presentation metrics needed by custom layouts without retaining native style memory. +/// +public readonly struct EditorUiLayout +{ + /// + /// Gets spacing between neighboring controls. + /// + public Vector2 itemSpacing { get; } + /// + /// Gets the current window inset. + /// + public Vector2 windowPadding { get; } + /// + /// Gets table cell padding. + /// + public Vector2 cellPadding { get; } + /// + /// Gets spacing between parts of a compound control. + /// + public Vector2 itemInnerSpacing { get; } + /// + /// Gets padding inside a control. + /// + public Vector2 framePadding { get; } + /// + /// Gets dock splitter thickness. + /// + public float dockingSeparatorSize { get; } + /// + /// Gets scrollbar width. + /// + public float scrollbarSize { get; } + /// + /// Gets control corner radius. + /// + public float frameRounding { get; } + + internal unsafe EditorUiLayout(ImGuiStyle* style) + { + itemSpacing = ImGui.FromNative(style->ItemSpacing); + windowPadding = ImGui.FromNative(style->WindowPadding); + cellPadding = ImGui.FromNative(style->CellPadding); + itemInnerSpacing = ImGui.FromNative(style->ItemInnerSpacing); + framePadding = ImGui.FromNative(style->FramePadding); + dockingSeparatorSize = style->DockingSeparatorSize; + scrollbarSize = style->ScrollbarSize; + frameRounding = style->FrameRounding; + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs new file mode 100644 index 000000000..511dc310f --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/EditorIconCatalog.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Inno.Editor.ImGui; +/// +/// Describes the host's complete standard glyph vocabulary as immutable presentation values. +/// +public static class EditorIconCatalog +{ + /// + /// Gets all named glyphs supplied by the installed UI font, in deterministic name order. + /// + public static IReadOnlyList<(string Name, string Glyph)> standard { get; } = System.Array.AsReadOnly(typeof(Inno.Adapter.Presentation.ImGui.ImGuiIcon).GetFields(BindingFlags.Public | BindingFlags.Static).Where(static field => field.IsLiteral && field.FieldType == typeof(string)).Select(static field => (field.Name, (string)field.GetRawConstantValue()!)).OrderBy(static value => value.Name, System.StringComparer.Ordinal).ToArray()); +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs new file mode 100644 index 000000000..f65210df6 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiFont.cs @@ -0,0 +1,102 @@ +using System; +using NativeImGui = Inno.Native.ImGui.ImGui; +using HostFont = Inno.Adapter.Presentation.ImGui.ImGuiFont; +using HostStyle = Inno.Adapter.Presentation.ImGui.ImGuiFontStyle; + +namespace Inno.Editor.ImGui; +/// +/// Chooses composable font emphasis while leaving actual font ownership with the presentation host. +/// +[Flags] +public enum ImGuiFontStyle +{ + /// + /// Uses the ordinary face. + /// + Regular = 0, + /// + /// Emphasizes text with the bold face. + /// + Bold = 1, + /// + /// Emphasizes text with the italic face. + /// + Italic = 2 +} + +/// +/// Uses the host's context-owned font catalog without exposing backend types to Editor features. +/// +public static unsafe class ImGuiFont +{ + /// + /// Checks whether the current host registered an exact face. + /// + /// + /// The requested emphasis. + /// + /// + /// True when the exact face is available. + /// + public static bool IsAvailable(ImGuiFontStyle style) + { + EditorUiSession.current.EnsureDrawing(); + return HostFont.IsAvailable((HostStyle)style); + } + + /// + /// Pushes a host-owned font face for one synchronous control group. + /// + /// + /// The requested emphasis. + /// + /// + /// An owner-window/frame scope; the host may use a registered nearest face. + /// + public static ImGuiFontScope PushStyle(ImGuiFontStyle style) + { + EditorUiSession session = EditorUiSession.current; + session.EnsureDrawing(); + var scope = HostFont.PushStyle((HostStyle)style); + return new ImGuiFontScope(session, scope, NativeImGui.GetFrameCount(), NativeImGui.Raw.GetCurrentContext()->FontStack.Size); + } +} + +/// +/// Restores a font push only in its original session, frame and native stack position. +/// +public unsafe ref struct ImGuiFontScope : IDisposable +{ + private EditorUiSession? m_session; + private Inno.Adapter.Presentation.ImGui.ImGuiFontScope m_scope; + private readonly int m_frame; + private readonly int m_depth; + internal ImGuiFontScope( + EditorUiSession session, + Inno.Adapter.Presentation.ImGui.ImGuiFontScope scope, + int frame, + int depth + ) { + m_session = session; + m_scope = scope; + m_frame = frame; + m_depth = depth; + } + + /// + /// Restores the preceding font after validating scope order; default and retired scopes are inert. + /// + /// + /// The scope is used in another context, frame or stack position. + /// + public void Dispose() + { + if (m_session is null) + return; + m_session.EnsureDrawing(); + if (NativeImGui.GetFrameCount() != m_frame || NativeImGui.Raw.GetCurrentContext()->FontStack.Size != m_depth) + throw new InvalidOperationException("Font scopes must retire in their original frame and reverse stack order."); + m_scope.Dispose(); + m_session = null; + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs new file mode 100644 index 000000000..42c966df8 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiIcon.cs @@ -0,0 +1,179 @@ +namespace Inno.Editor.ImGui; +/// +/// Provides the standard Editor glyph vocabulary; extension controls may also supply their own glyph text. +/// +public static class ImGuiIcon +{ + /// + /// Gets the angle left glyph used by standard Editor controls. + /// + public const string AngleLeft = Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleLeft; + /// + /// Gets the angle right glyph used by standard Editor controls. + /// + public const string AngleRight = Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleRight; + /// + /// Gets the arrow rotate left glyph used by standard Editor controls. + /// + public const string ArrowRotateLeft = Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRotateLeft; + /// + /// Gets the arrows up down left right glyph used by standard Editor controls. + /// + public const string ArrowsUpDownLeftRight = Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpDownLeftRight; + /// + /// Gets the bug glyph used by standard Editor controls. + /// + public const string Bug = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bug; + /// + /// Gets the camera glyph used by standard Editor controls. + /// + public const string Camera = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Camera; + /// + /// Gets the circle info glyph used by standard Editor controls. + /// + public const string CircleInfo = Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleInfo; + /// + /// Gets the circle question glyph used by standard Editor controls. + /// + public const string CircleQuestion = Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleQuestion; + /// + /// Gets the circle xmark glyph used by standard Editor controls. + /// + public const string CircleXmark = Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleXmark; + /// + /// Gets the cube glyph used by standard Editor controls. + /// + public const string Cube = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cube; + /// + /// Gets the cubes glyph used by standard Editor controls. + /// + public const string Cubes = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cubes; + /// + /// Gets the dice d6 glyph used by standard Editor controls. + /// + public const string DiceD6 = Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceD6; + /// + /// Gets the eye glyph used by standard Editor controls. + /// + public const string Eye = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eye; + /// + /// Gets the eye slash glyph used by standard Editor controls. + /// + public const string EyeSlash = Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeSlash; + /// + /// Gets the file glyph used by standard Editor controls. + /// + public const string File = Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + /// + /// Gets the file code glyph used by standard Editor controls. + /// + public const string FileCode = Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCode; + /// + /// Gets the file lines glyph used by standard Editor controls. + /// + public const string FileLines = Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileLines; + /// + /// Gets the folder glyph used by standard Editor controls. + /// + public const string Folder = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Folder; + /// + /// Gets the forward step glyph used by standard Editor controls. + /// + public const string ForwardStep = Inno.Adapter.Presentation.ImGui.ImGuiIcon.ForwardStep; + /// + /// Gets the gears glyph used by standard Editor controls. + /// + public const string Gears = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gears; + /// + /// Gets the globe glyph used by standard Editor controls. + /// + public const string Globe = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Globe; + /// + /// Gets the grip vertical glyph used by standard Editor controls. + /// + public const string GripVertical = Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripVertical; + /// + /// Gets the layer group glyph used by standard Editor controls. + /// + public const string LayerGroup = Inno.Adapter.Presentation.ImGui.ImGuiIcon.LayerGroup; + /// + /// Gets the lightbulb glyph used by standard Editor controls. + /// + public const string Lightbulb = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lightbulb; + /// + /// Gets the lock glyph used by standard Editor controls. + /// + public const string Lock = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lock; + /// + /// Gets the lock open glyph used by standard Editor controls. + /// + public const string LockOpen = Inno.Adapter.Presentation.ImGui.ImGuiIcon.LockOpen; + /// + /// Gets the magnifying glass glyph used by standard Editor controls. + /// + public const string MagnifyingGlass = Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlass; + /// + /// Gets the palette glyph used by standard Editor controls. + /// + public const string Palette = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Palette; + /// + /// Gets the pause glyph used by standard Editor controls. + /// + public const string Pause = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pause; + /// + /// Gets the pen glyph used by standard Editor controls. + /// + public const string Pen = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pen; + /// + /// Gets the play glyph used by standard Editor controls. + /// + public const string Play = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Play; + /// + /// Gets the plus glyph used by standard Editor controls. + /// + public const string Plus = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plus; + /// + /// Gets the rotate glyph used by standard Editor controls. + /// + public const string Rotate = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rotate; + /// + /// Gets the rupee sign glyph used by standard Editor controls. + /// + public const string RupeeSign = Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupeeSign; + /// + /// Gets the rupiah sign glyph used by standard Editor controls. + /// + public const string RupiahSign = Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupiahSign; + /// + /// Gets the shader glyph used by standard Editor controls. + /// + public const string Shader = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shader; + /// + /// Gets the skull crossbones glyph used by standard Editor controls. + /// + public const string SkullCrossbones = Inno.Adapter.Presentation.ImGui.ImGuiIcon.SkullCrossbones; + /// + /// Gets the stop glyph used by standard Editor controls. + /// + public const string Stop = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stop; + /// + /// Gets the triangle exclamation glyph used by standard Editor controls. + /// + public const string TriangleExclamation = Inno.Adapter.Presentation.ImGui.ImGuiIcon.TriangleExclamation; + /// + /// Gets the up down left right glyph used by standard Editor controls. + /// + public const string UpDownLeftRight = Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpDownLeftRight; + /// + /// Gets the up right and down left from center glyph used by standard Editor controls. + /// + public const string UpRightAndDownLeftFromCenter = Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpRightAndDownLeftFromCenter; + /// + /// Gets the vector square glyph used by standard Editor controls. + /// + public const string VectorSquare = Inno.Adapter.Presentation.ImGui.ImGuiIcon.VectorSquare; + /// + /// Gets the xmark glyph used by standard Editor controls. + /// + public const string Xmark = Inno.Adapter.Presentation.ImGui.ImGuiIcon.Xmark; +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiUtf8Buffer.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiUtf8Buffer.cs index 4f9dd1545..9d8bab8b6 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiUtf8Buffer.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/TextInput/ImGuiUtf8Buffer.cs @@ -1,12 +1,12 @@ using System; using System.Text; +using System.Buffers; +using System.Text.Unicode; using BGCS.Runtime; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui; - internal static unsafe class ImGuiUtf8Buffer { internal static bool InputText( @@ -19,25 +19,29 @@ ImGuiInputTextFlags flags ArgumentException.ThrowIfNullOrWhiteSpace(label); if (capacity is 0 or > int.MaxValue) throw new ArgumentOutOfRangeException(nameof(capacity)); - - byte[] buffer = new byte[(int)capacity]; - Encoding.UTF8.GetEncoder().Convert( - value.AsSpan(), - buffer.AsSpan(0, buffer.Length - 1), - flush: true, - out _, - out int bytesUsed, - out _); - buffer[bytesUsed] = 0; - - bool changed; - using NativeStringLease nativeLabel = Utf8Codec.EncodeNullTerminated(label, allowsNull: false); - using NativeStringLease nativeHint = Utf8Codec.EncodeNullTerminated(hint, allowsNull: true); - fixed (byte* nativeBuffer = buffer) + const ImGuiInputTextFlags callbacks = ImGuiInputTextFlags.CallbackCompletion | ImGuiInputTextFlags.CallbackHistory | ImGuiInputTextFlags.CallbackAlways | ImGuiInputTextFlags.CallbackCharFilter | ImGuiInputTextFlags.CallbackResize | ImGuiInputTextFlags.CallbackEdit; + if ((flags & callbacks) != 0) + throw new ArgumentException("Managed text controls do not accept native callback flags.", nameof(flags)); + int length = (int)capacity; + byte[]? rented = length > 1024 ? ArrayPool.Shared.Rent(length) : null; + Span buffer = rented is null ? stackalloc byte[length] : rented.AsSpan(0, length); + try { - changed = hint is null - ? NativeImGui.Raw.InputText((sbyte*)nativeLabel.pointer, (sbyte*)nativeBuffer, (uint)capacity, (int)flags, null, null) != 0 - : NativeImGui.Raw.InputTextWithHint( + _ = Utf8.FromUtf16(value.AsSpan(), buffer[..^1], out _, out int bytesUsed, replaceInvalidSequences: true, isFinalBlock: true); + buffer[bytesUsed] = 0; + bool changed; + using NativeStringLease nativeLabel = Utf8Codec.EncodeNullTerminated(label, allowsNull: false); + using NativeStringLease nativeHint = Utf8Codec.EncodeNullTerminated(hint, allowsNull: true); + fixed (byte* nativeBuffer = buffer) + { + changed = hint is null ? NativeImGui.Raw.InputText( + (sbyte*)nativeLabel.pointer, + (sbyte*)nativeBuffer, + (uint)capacity, + (int)flags, + null, + null + ) != 0 : NativeImGui.Raw.InputTextWithHint( (sbyte*)nativeLabel.pointer, (sbyte*)nativeHint.pointer, (sbyte*)nativeBuffer, @@ -46,13 +50,20 @@ ImGuiInputTextFlags flags null, null ) != 0; - } + } + + if (changed) + { + int terminator = buffer.IndexOf((byte)0); + value = Encoding.UTF8.GetString(buffer[..(terminator < 0 ? buffer.Length : terminator)]); + } - if (changed) + return changed; + } + finally { - int terminator = Array.IndexOf(buffer, (byte)0); - value = Encoding.UTF8.GetString(buffer, 0, terminator < 0 ? buffer.Length : terminator); + if (rented is not null) + ArrayPool.Shared.Return(rented, clearArray: true); } - return changed; } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs index 5be096be9..50c2a2335 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs @@ -1,15 +1,13 @@ using System; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { /// /// Draws a component-style collapsible card header. @@ -41,7 +39,7 @@ public static unsafe partial class ImGuiWidget /// /// when card content should be drawn; otherwise, . /// - public static bool CollapsingCard( + public bool CollapsingCard( string id, string title, Action? drawLeadingControl = null, @@ -51,21 +49,17 @@ public static bool CollapsingCard( float trailingControlWidth = 0f, Action? drawContextMenu = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(title); if (trailingControlWidth < 0f) { - throw new ArgumentOutOfRangeException( - nameof(trailingControlWidth), - trailingControlWidth, - "Trailing control width cannot be negative."); + throw new ArgumentOutOfRangeException(nameof(trailingControlWidth), trailingControlWidth, "Trailing control width cannot be negative."); } - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.SpanAvailWidth - | ImGuiTreeNodeFlags.AllowOverlap - | ImGuiTreeNodeFlags.OpenOnArrow; + + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.SpanAvailWidth | ImGuiTreeNodeFlags.AllowOverlap | ImGuiTreeNodeFlags.OpenOnArrow; if (defaultOpen) flags |= ImGuiTreeNodeFlags.DefaultOpen; - NativeImGui.PushStyleColor((int)(ImGuiCol.Header), EditorPalette.inspectorCardHeader); NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderHovered), EditorPalette.inspectorCardHeader); NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderActive), EditorPalette.inspectorCardHeader); @@ -111,24 +105,14 @@ public static bool CollapsingCard( NativeImGui.PopStyleVar(); NativeImGui.PopStyleColor(4); } + try { drawContextMenu?.Invoke(); - float contentX = itemHeaderMin.X + NativeImGui.GetTreeNodeToLabelSpacing(); - DrawDisclosureIndicator( - new Vector2(itemHeaderMin.X, headerMin.Y), - new Vector2(contentX, headerMax.Y), - open, - dimmed); - float contentY = headerMin.Y + MathF.Max( - 0f, - (headerMax.Y - headerMin.Y - NativeImGui.GetFrameHeight()) * 0.5f); - float trailingWidth = drawTrailingControl is null - ? 0f - : trailingControlWidth > 0f - ? trailingControlWidth - : NativeImGui.GetFrameHeight(); + DrawDisclosureIndicator(new Vector2(itemHeaderMin.X, headerMin.Y), new Vector2(contentX, headerMax.Y), open, dimmed); + float contentY = headerMin.Y + MathF.Max(0f, (headerMax.Y - headerMin.Y - NativeImGui.GetFrameHeight()) * 0.5f); + float trailingWidth = drawTrailingControl is null ? 0f : trailingControlWidth > 0f ? trailingControlWidth : NativeImGui.GetFrameHeight(); float titleRight = headerMax.X - trailingWidth; if (drawTrailingControl is not null) titleRight -= style.inspectorHeaderControlSpacing; @@ -142,6 +126,7 @@ public static bool CollapsingCard( NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.inspectorCardDisabledText); dimmedColorPushed = true; } + NativeImGui.BeginGroup(); groupStarted = true; if (drawLeadingControl is not null) @@ -149,16 +134,12 @@ public static bool CollapsingCard( drawLeadingControl(); NativeImGui.SameLine(0f, style.inspectorHeaderControlSpacing); } + NativeImGui.AlignTextToFramePadding(); Vector2 titleCursor = NativeImGui.GetCursorScreenPos(); float titleWidth = MathF.Max(1f, titleRight - titleCursor.X); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - ImGui.PushNativeClipRectangle( - drawList, - titleCursor, - new Vector2(titleCursor.X + titleWidth, headerMax.Y), - 1 - ); + ImGui.PushNativeClipRectangle(drawList, titleCursor, new Vector2(titleCursor.X + titleWidth, headerMax.Y), 1); try { ImGui.DrawNativeText(drawList, titleCursor, NativeImGui.GetColorU32((int)(ImGuiCol.Text)), title); @@ -167,15 +148,13 @@ public static bool CollapsingCard( { NativeImGui.Raw.PopClipRect(drawList); } + NativeImGui.Dummy(new Vector2(titleWidth, NativeImGui.GetFrameHeight())); NativeImGui.EndGroup(); groupStarted = false; - if (drawTrailingControl is not null) { - NativeImGui.SetCursorScreenPos(new Vector2( - MathF.Max(contentX, headerMax.X - trailingWidth), - contentY)); + NativeImGui.SetCursorScreenPos(new Vector2(MathF.Max(contentX, headerMax.X - trailingWidth), contentY)); drawTrailingControl(); } } @@ -187,6 +166,7 @@ public static bool CollapsingCard( NativeImGui.PopStyleColor(); NativeImGui.SetCursorScreenPos(contentCursor); } + return open; } catch @@ -212,21 +192,19 @@ public static bool CollapsingCard( /// /// Whether the indicator uses the inactive text color. /// - public static void DrawDisclosureIndicator( + public void DrawDisclosureIndicator( Vector2 min, Vector2 max, bool open, bool dimmed = false ) { + m_session.EnsureDrawing(); Vector2 availableSize = Vector2.Max(Vector2.Zero, max - min); - float buttonSize = MathF.Max( - 1f, - MathF.Min(availableSize.X, availableSize.Y) - style.disclosureButtonInset * 2f); + float buttonSize = MathF.Max(1f, MathF.Min(availableSize.X, availableSize.Y) - style.disclosureButtonInset * 2f); Vector2 buttonMin = min + (availableSize - new Vector2(buttonSize)) * 0.5f; Vector2 buttonMax = buttonMin + new Vector2(buttonSize); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - if (!IsPopupBlockingInteraction() && - NativeImGui.IsMouseHoveringRect(buttonMin, buttonMax)) + if (!IsPopupBlockingInteraction() && NativeImGui.IsMouseHoveringRect(buttonMin, buttonMax)) { ImGui.DrawNativeFilledRectangle( drawList, @@ -241,10 +219,7 @@ public static void DrawDisclosureIndicator( string indicator = open ? "▼" : "▶"; Vector2 indicatorSize = NativeImGui.CalcTextSize(indicator); Vector2 indicatorPosition = buttonMin + (new Vector2(buttonSize) - indicatorSize) * 0.5f; - uint color = NativeImGui.ColorConvertFloat4ToU32( - dimmed - ? EditorPalette.inspectorCardDisabledText - : EditorPalette.text); + uint color = NativeImGui.ColorConvertFloat4ToU32(dimmed ? EditorPalette.inspectorCardDisabledText : EditorPalette.text); ImGui.DrawNativeText(drawList, indicatorPosition, color, indicator); } @@ -260,14 +235,14 @@ public static void DrawDisclosureIndicator( /// /// Whether content is visually disabled and non-interactive. /// - public static void CardBody( + public void CardBody( string id, Action drawContent, bool dimmed = false ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(drawContent); - Vector2 originalCursor = NativeImGui.GetCursorScreenPos(); (float cardLeft, float cardRight) = GetFullWidthCardBounds(originalCursor); NativeImGui.SetCursorScreenPos(new Vector2(cardLeft, originalCursor.Y)); @@ -277,14 +252,8 @@ public static void CardBody( NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), style.borderSize); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inspectorCardBodyPadding); ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | ImGuiChildFlags.AutoResizeY; - ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoScrollWithMouse | - ImGuiWindowFlags.NoSavedSettings; - bool visible = NativeImGui.BeginChild( - $"##card_body_{id}", - new Vector2(MathF.Max(1f, cardRight - cardLeft), 0f), - (int)(childFlags), - (int)(windowFlags)); + ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; + bool visible = NativeImGui.BeginChild($"##card_body_{id}", new Vector2(MathF.Max(1f, cardRight - cardLeft), 0f), (int)(childFlags), (int)(windowFlags)); bool disabled = false; try { @@ -293,6 +262,7 @@ public static void CardBody( NativeImGui.BeginDisabled(true); disabled = true; } + if (visible) drawContent(); } @@ -308,7 +278,7 @@ public static void CardBody( } } - private static (float left, float right) GetFullWidthCardBounds(Vector2 cursor) + private (float left, float right) GetFullWidthCardBounds(Vector2 cursor) { float horizontalPadding = NativeImGui.Raw.GetCurrentWindow()->WindowPadding.X; float left = cursor.X - horizontalPadding; diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs index 98d609ea5..818aee0d6 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs @@ -2,20 +2,17 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { private const float C_TOOLTIP_MINIMUM_WIDTH = 300f; private const float C_TOOLTIP_WRAP_WIDTH = 440f; - /// /// Begins an explicitly opened popup using the editor context-menu presentation contract. /// The popup stays in its parent viewport, sizes itself to submitted content, and scrolls when @@ -31,13 +28,13 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when is empty. /// - public static bool BeginMenuPopup(string id) + public bool BeginMenuPopup(string id) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); PushContextMenuStyle(); NativeImGui.SetNextWindowViewport(NativeImGui.Raw.GetWindowViewport()->ID); - ImGuiWindowFlags flags = ImGuiWindowFlags.AlwaysAutoResize | - ImGuiWindowFlags.NoSavedSettings; + ImGuiWindowFlags flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings; if (NativeImGui.BeginPopup(id, (int)(flags))) return true; PopContextMenuStyle(); @@ -47,8 +44,9 @@ public static bool BeginMenuPopup(string id) /// /// Ends a popup opened by and restores the previous style. /// - public static void EndMenuPopup() + public void EndMenuPopup() { + m_session.EnsureDrawing(); NativeImGui.EndPopup(); PopContextMenuStyle(); } @@ -60,8 +58,9 @@ public static void EndMenuPopup() /// when tooltip content should be submitted; otherwise, /// . /// - public static bool BeginMenuTooltip() + public bool BeginMenuTooltip() { + m_session.EnsureDrawing(); PushContextMenuStyle(); NativeImGui.SetNextWindowViewport(NativeImGui.Raw.GetWindowViewport()->ID); if (NativeImGui.BeginTooltip()) @@ -73,8 +72,9 @@ public static bool BeginMenuTooltip() /// /// Ends a tooltip opened by and restores the previous style. /// - public static void EndMenuTooltip() + public void EndMenuTooltip() { + m_session.EnsureDrawing(); NativeImGui.EndTooltip(); PopContextMenuStyle(); } @@ -85,10 +85,10 @@ public static void EndMenuTooltip() /// /// Tooltip text. Empty values do not draw a tooltip. /// - public static void DrawItemTooltip(string? text) + public void DrawItemTooltip(string? text) { - if (string.IsNullOrWhiteSpace(text) || - !NativeImGui.IsItemHovered((int)(ImGuiHoveredFlags.AllowWhenDisabled))) + m_session.EnsureDrawing(); + if (string.IsNullOrWhiteSpace(text) || !NativeImGui.IsItemHovered((int)(ImGuiHoveredFlags.AllowWhenDisabled))) { return; } @@ -102,11 +102,11 @@ public static void DrawItemTooltip(string? text) /// /// Tooltip contents. The caller owns hit testing; empty text draws nothing. /// - public static void DrawTooltip(string? text) + public void DrawTooltip(string? text) { + m_session.EnsureDrawing(); if (string.IsNullOrWhiteSpace(text)) return; - ImGuiViewport* viewport = NativeImGui.Raw.GetWindowViewport(); Vector2 margin = new(6f * style.zoom); Vector2 available = Vector2.Max(Vector2.One, ImGui.FromNative(viewport->WorkSize) - margin * 2f); @@ -114,9 +114,7 @@ public static void DrawTooltip(string? text) float outerWidth = MathF.Min(C_TOOLTIP_WRAP_WIDTH * style.zoom, available.X); float innerWidth = MathF.Max(1f, outerWidth - padding.X * 2f - style.menuBorderSize * 2f); Vector2 textSize = NativeImGui.CalcTextSize(text, false, innerWidth); - outerWidth = MathF.Min(outerWidth, MathF.Max( - MathF.Min(C_TOOLTIP_MINIMUM_WIDTH * style.zoom, available.X), - textSize.X + padding.X * 2f + style.menuBorderSize * 2f)); + outerWidth = MathF.Min(outerWidth, MathF.Max(MathF.Min(C_TOOLTIP_MINIMUM_WIDTH * style.zoom, available.X), textSize.X + padding.X * 2f + style.menuBorderSize * 2f)); innerWidth = MathF.Max(1f, outerWidth - padding.X * 2f - style.menuBorderSize * 2f); textSize = NativeImGui.CalcTextSize(text, false, innerWidth); Vector2 size = new(outerWidth, MathF.Min(available.Y, textSize.Y + padding.Y * 2f + style.menuBorderSize * 2f)); @@ -160,8 +158,9 @@ public static void DrawTooltip(string? text) /// /// when context-menu content should be drawn; otherwise, . /// - public static bool BeginContextMenu(string id) + public bool BeginContextMenu(string id) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); PushContextMenuStyle(); NativeImGui.SetNextWindowViewport(NativeImGui.Raw.GetWindowViewport()->ID); @@ -181,56 +180,45 @@ public static bool BeginContextMenu(string id) /// /// when context-menu content should be drawn; otherwise, . /// - public static bool BeginWindowContextMenu(string id) + public bool BeginWindowContextMenu(string id) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); PushContextMenuStyle(); NativeImGui.SetNextWindowViewport(NativeImGui.Raw.GetWindowViewport()->ID); ConstrainContextMenu(); - if (NativeImGui.BeginPopupContextWindow( - id, - (int)(ImGuiPopupFlags.MouseButtonRight | ImGuiPopupFlags.NoOpenOverItems))) + if (NativeImGui.BeginPopupContextWindow(id, (int)(ImGuiPopupFlags.MouseButtonRight | ImGuiPopupFlags.NoOpenOverItems))) { return true; } + PopContextMenuStyle(); return false; } /// - /// Ends a context menu opened by or . + /// Ends a context menu opened by or . /// - public static void EndContextMenu() + public void EndContextMenu() { + m_session.EnsureDrawing(); EndMenuPopup(); } - private static bool IsPopupBlockingInteraction() - => NativeImGui.IsPopupOpen( - string.Empty, - (int)(ImGuiPopupFlags.AnyPopupId | ImGuiPopupFlags.AnyPopupLevel)); - - private static void ConstrainContextMenu() + private bool IsPopupBlockingInteraction() => NativeImGui.IsPopupOpen(string.Empty, (int)(ImGuiPopupFlags.AnyPopupId | ImGuiPopupFlags.AnyPopupLevel)); + private void ConstrainContextMenu() { Vector2 workSize = ImGui.FromNative(NativeImGui.Raw.GetWindowViewport()->WorkSize); - float minimumWidth = MathF.Min( - workSize.X, - style.searchPopupWidth + style.menuWindowPadding.X * 2f + style.menuBorderSize * 2f); - SetMenuPopupSizeConstraints( - new Vector2(minimumWidth, 0f), - Vector2.Max(workSize, Vector2.One)); + float minimumWidth = MathF.Min(workSize.X, style.searchPopupWidth + style.menuWindowPadding.X * 2f + style.menuBorderSize * 2f); + SetMenuPopupSizeConstraints(new Vector2(minimumWidth, 0f), Vector2.Max(workSize, Vector2.One)); } - private static unsafe void SetMenuPopupSizeConstraints( + private unsafe void SetMenuPopupSizeConstraints( Vector2 minimum, Vector2 maximum ) { - Vector2 pixelMaximum = Vector2.Max(Vector2.One, new Vector2( - MathF.Floor(maximum.X), - MathF.Floor(maximum.Y))); - Vector2 pixelMinimum = Vector2.Min(pixelMaximum, new Vector2( - MathF.Ceiling(minimum.X), - MathF.Ceiling(minimum.Y))); + Vector2 pixelMaximum = Vector2.Max(Vector2.One, new Vector2(MathF.Floor(maximum.X), MathF.Floor(maximum.Y))); + Vector2 pixelMinimum = Vector2.Min(pixelMaximum, new Vector2(MathF.Ceiling(minimum.X), MathF.Ceiling(minimum.Y))); ImGui.ConstrainNativeWindowSize(pixelMinimum, pixelMaximum, &RoundMenuPopupSize, null); } @@ -239,12 +227,10 @@ private static unsafe void RoundMenuPopupSize(ImGuiSizeCallbackData* data) { // ImGui truncates constrained sizes. Round content upward within integer bounds so // fractional padding cannot turn a fully fitting menu into a scrolling window. - data->DesiredSize = ImGui.ToNative(new Vector2( - MathF.Ceiling(data->DesiredSize.X), - MathF.Ceiling(data->DesiredSize.Y))); + data->DesiredSize = ImGui.ToNative(new Vector2(MathF.Ceiling(data->DesiredSize.X), MathF.Ceiling(data->DesiredSize.Y))); } - private static void PushContextMenuStyle() + private void PushContextMenuStyle() { NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.menuText); NativeImGui.PushStyleColor((int)(ImGuiCol.PopupBg), EditorPalette.menuBackground); @@ -260,7 +246,7 @@ private static void PushContextMenuStyle() NativeImGui.PushStyleVar((int)(ImGuiStyleVar.PopupBorderSize), style.menuBorderSize); } - private static void PopContextMenuStyle() + private void PopContextMenuStyle() { NativeImGui.PopStyleVar(5); NativeImGui.PopStyleColor(7); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs index e66a1d050..e7f17b691 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs @@ -1,15 +1,13 @@ using System; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { /// /// Gets the layout size of a compact colored label chip using the centralized editor style. @@ -23,15 +21,12 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when is . /// - public static Vector2 GetLabelChipSize(string label) + public Vector2 GetLabelChipSize(string label) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); Vector2 textSize = NativeImGui.CalcTextSize(label); - return new Vector2( - textSize.X + style.labelChipPadding.X * 2f, - MathF.Max( - NativeImGui.GetFrameHeight(), - textSize.Y + style.labelChipPadding.Y * 2f)); + return new Vector2(textSize.X + style.labelChipPadding.X * 2f, MathF.Max(NativeImGui.GetFrameHeight(), textSize.Y + style.labelChipPadding.Y * 2f)); } /// @@ -46,23 +41,17 @@ public static Vector2 GetLabelChipSize(string label) /// /// Thrown when is . /// - public static void LabelChip( + public void LabelChip( string label, Vector4 background ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); Vector2 size = GetLabelChipSize(label); Vector2 minimum = NativeImGui.GetCursorScreenPos(); Vector2 textSize = NativeImGui.CalcTextSize(label); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - ImGui.DrawNativeFilledRectangle( - drawList, - minimum, - minimum + size, - NativeImGui.ColorConvertFloat4ToU32(background), - style.labelChipRounding, - 0 - ); + ImGui.DrawNativeFilledRectangle(drawList, minimum, minimum + size, NativeImGui.ColorConvertFloat4ToU32(background), style.labelChipRounding, 0); ImGui.DrawNativeText(drawList, minimum + (size - textSize) * 0.5f, NativeImGui.GetColorU32((int)(ImGuiCol.Text)), label); NativeImGui.Dummy(size); } @@ -79,8 +68,9 @@ Vector4 background /// /// Thrown when is . /// - public static Vector2 GetTypeBadgeSize(string label) + public Vector2 GetTypeBadgeSize(string label) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); Vector2 textSize = NativeImGui.CalcTextSize(label); return textSize + style.typeBadgePadding * 2f; @@ -101,11 +91,12 @@ public static Vector2 GetTypeBadgeSize(string label) /// /// Thrown when is . /// - public static void TypeBadge( + public void TypeBadge( string label, Vector4 accent, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); Vector2 size = GetTypeBadgeSize(label); Vector2 minimum = NativeImGui.GetCursorScreenPos(); @@ -115,23 +106,8 @@ public static void TypeBadge( background.W = EditorPalette.opacityOpaque; Vector4 outline = accent; outline.W = EditorPalette.opacityStrong; - ImGui.DrawNativeFilledRectangle( - draw, - minimum, - minimum + size, - NativeImGui.ColorConvertFloat4ToU32(background), - style.typeBadgeRounding, - 0 - ); - ImGui.DrawNativeRectangle( - draw, - minimum, - minimum + size, - NativeImGui.ColorConvertFloat4ToU32(outline), - style.typeBadgeRounding, - (int)(ImDrawFlags.None), - style.borderSize - ); + ImGui.DrawNativeFilledRectangle(draw, minimum, minimum + size, NativeImGui.ColorConvertFloat4ToU32(background), style.typeBadgeRounding, 0); + ImGui.DrawNativeRectangle(draw, minimum, minimum + size, NativeImGui.ColorConvertFloat4ToU32(outline), style.typeBadgeRounding, (int)(ImDrawFlags.None), style.borderSize); ImGui.DrawNativeText(draw, minimum + (size - textSize) * 0.5f, NativeImGui.ColorConvertFloat4ToU32(accent), label); NativeImGui.Dummy(size); DrawItemTooltip(tooltip); @@ -155,12 +131,13 @@ public static void TypeBadge( /// /// when the value changed. /// - public static bool CompactCheckbox( + public bool CompactCheckbox( string id, ref bool value, float size = -1f, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); if (size < 0f) size = style.compactCheckboxSize; @@ -173,9 +150,7 @@ public static bool CompactCheckbox( float frameHeight = NativeImGui.GetFrameHeight(); float verticalOffset = MathF.Max(0f, (frameHeight - size) * 0.5f); Vector2 min = initialCursor + new Vector2(0f, verticalOffset); - bool changed = NativeImGui.InvisibleButton( - $"##compact_checkbox_{id}", - new Vector2(size, frameHeight)); + bool changed = NativeImGui.InvisibleButton($"##compact_checkbox_{id}", new Vector2(size, frameHeight)); if (changed) { value = !value; @@ -189,20 +164,8 @@ public static bool CompactCheckbox( { ImGui.DrawNativeFilledRectangle(drawList, min, max, textColor, 1f, 0); uint checkColor = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardHeader); - ImGui.DrawNativeLine( - drawList, - min + new Vector2(size * 0.22f, size * 0.52f), - min + new Vector2(size * 0.43f, size * 0.72f), - checkColor, - 1.5f - ); - ImGui.DrawNativeLine( - drawList, - min + new Vector2(size * 0.43f, size * 0.72f), - min + new Vector2(size * 0.80f, size * 0.28f), - checkColor, - 1.5f - ); + ImGui.DrawNativeLine(drawList, min + new Vector2(size * 0.22f, size * 0.52f), min + new Vector2(size * 0.43f, size * 0.72f), checkColor, 1.5f); + ImGui.DrawNativeLine(drawList, min + new Vector2(size * 0.43f, size * 0.72f), min + new Vector2(size * 0.80f, size * 0.28f), checkColor, 1.5f); } else { @@ -211,19 +174,10 @@ public static bool CompactCheckbox( if (hovered) { - ImGui.DrawNativeRectangle( - drawList, - min - Vector2.One, - max + Vector2.One, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered), - 1f, - 0, - 1.5f - ); + ImGui.DrawNativeRectangle(drawList, min - Vector2.One, max + Vector2.One, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered), 1f, 0, 1.5f); } DrawItemTooltip(tooltip); - return changed; } @@ -245,11 +199,12 @@ public static bool CompactCheckbox( /// /// Thrown when is empty or whitespace. /// - public static bool Checkbox( + public bool Checkbox( string label, ref bool value, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(label); bool changed = NativeImGui.Checkbox(label, ref value); DrawItemTooltip(tooltip); @@ -268,10 +223,11 @@ public static bool Checkbox( /// /// when the button is pressed. /// - public static bool CenteredButton( + public bool CenteredButton( string label, float topPadding = 0f ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(label); if (topPadding < 0f) { @@ -304,20 +260,18 @@ public static bool CenteredButton( /// /// Thrown when is . /// - public static void CenteredProgressBar( + public void CenteredProgressBar( float fraction, Vector2 size, string overlay ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(overlay); - NativeImGui.ProgressBar(fraction, size, string.Empty); - Vector2 minimum = NativeImGui.GetItemRectMin(); Vector2 maximum = NativeImGui.GetItemRectMax(); Vector2 textSize = NativeImGui.CalcTextSize(overlay); Vector2 textPosition = minimum + (maximum - minimum - textSize) * 0.5f; ImGui.DrawNativeText(NativeImGui.Raw.GetWindowDrawList(), textPosition, NativeImGui.GetColorU32((int)(ImGuiCol.Text)), overlay); } - } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs index 7009da145..2ed173cbb 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.DragDrop.cs @@ -1,16 +1,14 @@ using System; using BGCS.Runtime; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { /// /// Publishes an unmanaged drag payload for the most recently submitted item. @@ -36,7 +34,7 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when is empty or whitespace. /// - public static bool DragDropSource( + public bool DragDropSource( string payloadType, in TPayload payload, Action? drawPreview = null, @@ -45,9 +43,7 @@ public static bool DragDropSource( where TPayload : unmanaged { ArgumentException.ThrowIfNullOrWhiteSpace(payloadType); - ImGuiDragDropFlags sourceFlags = allowHoldToOpenOthers - ? ImGuiDragDropFlags.None - : ImGuiDragDropFlags.SourceNoHoldToOpenOthers; + ImGuiDragDropFlags sourceFlags = allowHoldToOpenOthers ? ImGuiDragDropFlags.None : ImGuiDragDropFlags.SourceNoHoldToOpenOthers; if (!NativeImGui.BeginDragDropSource((int)(sourceFlags))) { return false; @@ -86,7 +82,7 @@ public static bool DragDropSource( /// /// Thrown when is . /// - public static bool DragDropSource( + public bool DragDropSource( string payloadType, Func payloadFactory, Action? drawPreview = null, @@ -96,9 +92,7 @@ public static bool DragDropSource( { ArgumentException.ThrowIfNullOrWhiteSpace(payloadType); ArgumentNullException.ThrowIfNull(payloadFactory); - ImGuiDragDropFlags sourceFlags = allowHoldToOpenOthers - ? ImGuiDragDropFlags.None - : ImGuiDragDropFlags.SourceNoHoldToOpenOthers; + ImGuiDragDropFlags sourceFlags = allowHoldToOpenOthers ? ImGuiDragDropFlags.None : ImGuiDragDropFlags.SourceNoHoldToOpenOthers; if (!NativeImGui.BeginDragDropSource((int)(sourceFlags))) { return false; @@ -126,7 +120,7 @@ public static bool DragDropSource( /// /// only when a compatible payload is delivered. /// - public static bool DragDropTarget( + public bool DragDropTarget( string payloadType, out TPayload payload ) @@ -153,7 +147,7 @@ out TPayload payload /// /// only when a compatible payload is delivered. /// - public static bool DragDropTarget( + public bool DragDropTarget( string payloadType, out TPayload payload, out bool isPreviewing @@ -184,7 +178,7 @@ out bool isPreviewing /// /// only when a compatible payload is delivered. /// - public static bool DragDropTarget( + public bool DragDropTarget( string payloadType, out TPayload payload, out bool isPreviewing, @@ -208,7 +202,6 @@ bool drawDefaultHighlight bool compatible = TryReadDragDropPayload(nativePayload, out payload); if (compatible) isPreviewing = nativePayload->Preview != 0; - bool delivered = compatible && nativePayload->Delivery != 0; NativeImGui.EndDragDropTarget(); return delivered; @@ -247,7 +240,7 @@ bool drawDefaultHighlight /// /// Thrown when is empty or whitespace. /// - public static bool DragDropTarget( + public bool DragDropTarget( string payloadType, Vector2 minimum, Vector2 maximum, @@ -263,7 +256,11 @@ public static bool DragDropTarget( isPreviewing = false; if (maximum.X <= minimum.X || maximum.Y <= minimum.Y) return false; - ImRect bounds = new() { Min = ImGui.ToNative(minimum), Max = ImGui.ToNative(maximum) }; + ImRect bounds = new() + { + Min = ImGui.ToNative(minimum), + Max = ImGui.ToNative(maximum) + }; if (NativeImGui.Raw.BeginDragDropTargetCustom(&bounds, targetId) == 0) return false; try @@ -284,7 +281,7 @@ public static bool DragDropTarget( } } - private static unsafe void SetDragDropPayload( + private unsafe void SetDragDropPayload( string payloadType, in TPayload payload ) @@ -295,7 +292,7 @@ in TPayload payload _ = NativeImGui.Raw.SetDragDropPayload((sbyte*)nativeType.pointer, &payloadCopy, (uint)sizeof(TPayload), 0); } - private static unsafe bool TryReadDragDropPayload( + private unsafe bool TryReadDragDropPayload( ImGuiPayload* nativePayload, out TPayload payload ) diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs new file mode 100644 index 000000000..c8600bfa4 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Editing.cs @@ -0,0 +1,152 @@ +using System; +using Inno.Editor.Interactions; +using Inno.Scripting.Api; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui.ImGuiWidget; +/// +/// Owns session-local controls, native adaptation and reversible edit gestures. +/// +public sealed unsafe partial class ImGuiWidget +{ + private EditorEditGesture? m_propertyGesture; + private (string Owner, string Path) m_propertyEditKey; + private bool m_propertyEditTouched; + /// + /// Gets the current continuous-edit merge boundary, or null for a discrete edit. + /// + [ScriptingApiIgnore] + public string? editMergeKey => m_propertyGesture?.mergeKey; + + /// + /// Starts a History boundary only when a changed control is actively producing continuous input. + /// + /// + /// The authoritative domain History receiving the control's existing mutation records. + /// + /// + /// The stable owner-qualified property path. + /// + /// + /// The operation name shown by Undo and Redo. + /// + /// + /// A previous gesture cannot complete or History rejects the transaction. + /// + [ScriptingApiIgnore] + public void BeginPropertyEdit( + IEditorHistory history, + string path, + string name + ) { + m_session.EnsureDrawing(); + ArgumentException.ThrowIfNullOrWhiteSpace(path); + if (NativeImGui.IsAnyItemActive()) + BeginEdit(history, path, name); + } + + /// + /// Begins or touches an explicitly captured continuous edit, such as a viewport manipulation. + /// + /// + /// The authoritative domain History. + /// + /// + /// The stable owner-qualified edit identity. + /// + /// + /// The user-facing Undo name. + /// + [ScriptingApiIgnore] + public void BeginEdit( + IEditorHistory history, + string path, + string name + ) { + m_session.EnsureDrawing(); + ArgumentNullException.ThrowIfNull(history); + ArgumentException.ThrowIfNullOrWhiteSpace(path); + var key = (m_session.owner, path); + if (m_propertyGesture is not null && m_propertyEditKey != key) + CommitEdits(); + if (m_propertyGesture is null) + { + m_propertyGesture = new EditorEditGesture(history, name); + m_propertyEditKey = key; + } + + m_propertyEditTouched = true; + } + + /// + /// Checks whether this owner still owns a particular edit, including after a close or generation boundary. + /// + /// + /// The stable edit identity. + /// + /// + /// True only while the matching owner still has an active transaction. + /// + [ScriptingApiIgnore] + public bool IsEditing(string path) => m_propertyGesture is not null && m_propertyEditKey == (m_session.owner, path); + /// + /// Observes the current property after drawing, including release and cancellation without a new value sample. + /// + /// + /// The owner-qualified path passed to the property edit boundary. + /// + [ScriptingApiIgnore] + public void CompletePropertyEdit(string path) + { + m_session.EnsureDrawing(); + if (m_propertyGesture is null || m_propertyEditKey != (m_session.owner, path)) + return; + m_propertyEditTouched = true; + if (NativeImGui.IsKeyPressed((Inno.Native.ImGui.ImGuiKey)ImGuiKey.Escape, false)) + CancelEdits(); + else if (!NativeImGui.IsAnyItemActive()) + CommitEdits(); + } + + /// + /// Completes the current control edit before a document command, reload, Play transition or shutdown. + /// + /// + /// A nested transaction prevents completion; ownership is retained. + /// + [ScriptingApiIgnore] + public void CommitEdits() + { + if (m_propertyGesture is null) + return; + m_propertyGesture.Commit(); + m_propertyGesture.Dispose(); + m_propertyGesture = null; + } + + /// + /// Restores the control's initial domain values before discarding its transient presentation state. + /// + /// + /// Rollback is unavailable; the gesture remains owned for recovery. + /// + [ScriptingApiIgnore] + public void CancelEdits() + { + if (m_propertyGesture is null) + return; + EditorHistoryResult result = m_propertyGesture.Cancel(); + if (!result.succeeded) + throw new InvalidOperationException($"The property gesture could not roll back: {result.message}"); + m_propertyGesture.Dispose(); + m_propertyGesture = null; + } + + internal void BeginEditingFrame() => m_propertyEditTouched = false; + internal void CompleteEditingFrame() + { + if (m_propertyGesture is not null && !m_propertyEditTouched) + CancelEdits(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs new file mode 100644 index 000000000..34816bca5 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs @@ -0,0 +1,257 @@ +using System; +using System.Numerics; +using Matrix = Inno.Core.Mathematics.Matrix; +using NativeImGui = Inno.Native.ImGui.ImGui; +using NativeGizmo = Inno.Native.ImGuizmo.ImGuizmo; +using NativeOperation = Inno.Native.ImGuizmo.ImGuizmoOperation; +using NativeSpace = Inno.Native.ImGuizmo.ImGuizmoMode; + +namespace Inno.Editor.ImGui.ImGuiWidget; +/// +/// Chooses the transform handles rendered by the viewport presenter. +/// +[Flags] +public enum TransformGizmoOperation +{ + /// + /// Renders translation handles. + /// + Translate = 1, + /// + /// Renders rotation handles. + /// + Rotate = 2, + /// + /// Renders the X scale handle. + /// + ScaleX = 4, + /// + /// Renders the Y scale handle. + /// + ScaleY = 8, + /// + /// Renders the Z scale handle. + /// + ScaleZ = 16, + /// + /// Renders all local scale handles. + /// + Scale = ScaleX | ScaleY | ScaleZ +} + +/// +/// Chooses how the manipulation axes are oriented. +/// +public enum TransformGizmoSpace +{ + /// + /// Uses the object's local orientation. + /// + Local, + /// + /// Uses the world's orientation. + /// + World +} + +/// +/// Supplies frame-local matrices and interaction policy without native handles. +/// +public readonly record struct TransformGizmoOptions +{ + /// + /// Gets the viewport's screen-space minimum. + /// + public Vector2 minimum { get; init; } + /// + /// Gets the viewport's screen-space maximum. + /// + public Vector2 maximum { get; init; } + /// + /// Gets the column-vector view matrix. + /// + public Matrix view { get; init; } + /// + /// Gets the projection matrix supplied by the viewport owner. + /// + public Matrix projection { get; init; } + /// + /// Gets whether this viewport uses an orthographic projection. + /// + public bool orthographic { get; init; } + /// + /// Gets whether manipulation can consume pointer input. + /// + public bool enabled { get; init; } + /// + /// Gets the requested handles. + /// + public TransformGizmoOperation operation { get; init; } + /// + /// Gets the handle coordinate space. + /// + public TransformGizmoSpace space { get; init; } + /// + /// Gets whether the X translation axis is excluded by the viewport plane. + /// + public bool maskX { get; init; } + /// + /// Gets whether the Y translation axis is excluded by the viewport plane. + /// + public bool maskY { get; init; } + /// + /// Gets whether the Z translation axis is excluded by the viewport plane. + /// + public bool maskZ { get; init; } +} + +/// +/// Returns copied manipulation output; the domain owner decides how to apply and record it. +/// +/// +/// The resulting world matrix. +/// +/// +/// Whether the native presenter reported an edited value. +/// +/// +/// Whether the handle currently captures the pointer. +/// +/// +/// Whether the pointer is over a handle. +/// +public readonly record struct TransformGizmoResult( + Matrix matrix, + bool changed, + bool isUsing, + bool isOver +); +/// +/// Owns session-local controls, native adaptation and reversible edit gestures. +/// +public sealed unsafe partial class ImGuiWidget +{ + /// + /// Draws a transform handle using stack-owned ABI buffers and restores native global style and masks before returning. + /// + /// + /// The current world matrix, copied into the native call. + /// + /// + /// The explicit viewport and interaction policy. + /// + /// + /// Copied output with no native pointer or domain object. + /// + public TransformGizmoResult TransformGizmo( + Matrix world, + TransformGizmoOptions options + ) { + m_session.EnsureDrawing(); + float* view = stackalloc float[16]; + float* projection = stackalloc float[16]; + float* model = stackalloc float[16]; + WriteColumnMajor(options.view, view); + WriteColumnMajor(options.projection, projection); + WriteColumnMajor(world, model); + NativeOperation operation = ToNativeOperation(options.operation); + NativeGizmo.BeginFrame(); + NativeGizmo.Raw.SetDrawlist((Inno.Native.ImGuizmo.ImDrawList*)(nint)NativeImGui.Raw.GetWindowDrawList()); + NativeGizmo.SetRect(options.minimum.X, options.minimum.Y, MathF.Max(1f, options.maximum.X - options.minimum.X), MathF.Max(1f, options.maximum.Y - options.minimum.Y)); + NativeGizmo.SetOrthographic(options.orthographic); + NativeGizmo.SetGizmoSizeClipSpace(0.21f); + var style = NativeGizmo.Raw.GetStyle(); + var previousStyle = *style; + try + { + style->TranslationLineThickness = 3.5f; + style->TranslationLineArrowSize = 9f; + style->RotationLineThickness = 3f; + style->RotationOuterLineThickness = 4f; + style->ScaleLineThickness = 3.5f; + style->ScaleLineCircleSize = 9f; + style->CenterCircleSize = 8f; + NativeGizmo.Enable(options.enabled); + NativeGizmo.SetAxisMask(options.maskX, options.maskY, options.maskZ); + NativeGizmo.SetAxisLimit(options.maskX || options.maskY || options.maskZ ? float.PositiveInfinity : 0.0025f); + bool changed = NativeGizmo.Raw.Manipulate( + view, + projection, + operation, + options.space == TransformGizmoSpace.Local ? NativeSpace.Local : NativeSpace.World, + model, + null, + null, + null, + null + ) != 0; + return new TransformGizmoResult(ReadColumnMajor(model), changed, NativeGizmo.IsUsing(), NativeGizmo.IsOver(operation)); + } + finally + { + *style = previousStyle; + NativeGizmo.Enable(true); + NativeGizmo.SetAxisMask(false, false, false); + NativeGizmo.SetAxisLimit(0.0025f); + } + } + + private static NativeOperation ToNativeOperation(TransformGizmoOperation operation) + { + if (operation == TransformGizmoOperation.Translate) + return NativeOperation.Translate; + if (operation == TransformGizmoOperation.Rotate) + return NativeOperation.Rotate; + NativeOperation result = 0; + if (operation.HasFlag(TransformGizmoOperation.ScaleX)) + result |= NativeOperation.ScaleX; + if (operation.HasFlag(TransformGizmoOperation.ScaleY)) + result |= NativeOperation.ScaleY; + if (operation.HasFlag(TransformGizmoOperation.ScaleZ)) + result |= NativeOperation.ScaleZ; + if (result == 0) + throw new ArgumentOutOfRangeException(nameof(operation)); + return result; + } + + private static unsafe void WriteColumnMajor( + Matrix matrix, + float* destination + ) { + destination[0] = matrix.m11; + destination[1] = matrix.m21; + destination[2] = matrix.m31; + destination[3] = matrix.m41; + destination[4] = matrix.m12; + destination[5] = matrix.m22; + destination[6] = matrix.m32; + destination[7] = matrix.m42; + destination[8] = matrix.m13; + destination[9] = matrix.m23; + destination[10] = matrix.m33; + destination[11] = matrix.m43; + destination[12] = matrix.m14; + destination[13] = matrix.m24; + destination[14] = matrix.m34; + destination[15] = matrix.m44; + } + + private static unsafe Matrix ReadColumnMajor(float* source) => new( + source[0], + source[4], + source[8], + source[12], + source[1], + source[5], + source[9], + source[13], + source[2], + source[6], + source[10], + source[14], + source[3], + source[7], + source[11], + source[15] + ); +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs index e3f57f496..36f11df63 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs @@ -4,15 +4,12 @@ using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides standard help box presentation in the Editor UI. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { - [ThreadStatic] - private static SectionLayoutState? s_sectionLayout; - + private SectionLayoutState? m_sectionLayout; /// /// Draws a wrapped contextual message with a semantic icon and a subdued status surface. /// @@ -25,11 +22,12 @@ public static unsafe partial class ImGuiWidget /// /// Semantic accent for the icon, outline, and leading stripe. /// - public static void HelpBox( + public void HelpBox( string text, string icon, Vector4 color ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); ArgumentNullException.ThrowIfNull(icon); Vector2 origin = NativeImGui.GetCursorScreenPos(); @@ -45,31 +43,9 @@ Vector4 color background.W = EditorPalette.opacityOpaque; Vector4 outline = color; outline.W = EditorPalette.opacityMuted; - ImGui.DrawNativeFilledRectangle( - draw, - origin, - origin + new Vector2(width, height), - NativeImGui.ColorConvertFloat4ToU32(background), - 4f * style.zoom, - 0 - ); - ImGui.DrawNativeRectangle( - draw, - origin, - origin + new Vector2(width, height), - NativeImGui.ColorConvertFloat4ToU32(outline), - 4f * style.zoom, - 0, - 1f - ); - ImGui.DrawNativeFilledRectangle( - draw, - origin, - origin + new Vector2(2f * style.zoom, height), - NativeImGui.ColorConvertFloat4ToU32(color), - 0, - 0 - ); + ImGui.DrawNativeFilledRectangle(draw, origin, origin + new Vector2(width, height), NativeImGui.ColorConvertFloat4ToU32(background), 4f * style.zoom, 0); + ImGui.DrawNativeRectangle(draw, origin, origin + new Vector2(width, height), NativeImGui.ColorConvertFloat4ToU32(outline), 4f * style.zoom, 0, 1f); + ImGui.DrawNativeFilledRectangle(draw, origin, origin + new Vector2(2f * style.zoom, height), NativeImGui.ColorConvertFloat4ToU32(color), 0, 0); ImGui.DrawNativeText(draw, origin + padding, NativeImGui.ColorConvertFloat4ToU32(color), icon); NativeImGui.SetCursorScreenPos(origin + padding + new Vector2(iconWidth + gap, 0f)); NativeImGui.PushTextWrapPos(NativeImGui.GetCursorPosX() + textWidth); @@ -81,6 +57,7 @@ Vector4 color { NativeImGui.PopTextWrapPos(); } + NativeImGui.SetCursorScreenPos(origin); NativeImGui.Dummy(new Vector2(width, height)); } @@ -95,8 +72,9 @@ Vector4 color /// /// Thrown when is . /// - public static void CollectionSectionHeader(string title) + public void CollectionSectionHeader(string title) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(title); Vector2 origin = NativeImGui.GetCursorScreenPos(); float width = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); @@ -129,12 +107,13 @@ public static void CollectionSectionHeader(string title) /// /// Thrown when is . /// - public static void SectionLayout(Action drawContent) + public void SectionLayout(Action drawContent) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(drawContent); - SectionLayoutState? parent = s_sectionLayout; + SectionLayoutState? parent = m_sectionLayout; var current = new SectionLayoutState(); - s_sectionLayout = current; + m_sectionLayout = current; try { drawContent(); @@ -142,7 +121,7 @@ public static void SectionLayout(Action drawContent) finally { CompleteSection(current); - s_sectionLayout = parent; + m_sectionLayout = parent; } } @@ -153,7 +132,7 @@ public static void SectionLayout(Action drawContent) /// Attribute-driven drawers use this value to skip properties until the next section header /// when the current section is collapsed. /// - public static bool isSectionContentVisible => s_sectionLayout?.contentVisible ?? true; + public bool isSectionContentVisible => m_sectionLayout?.contentVisible ?? true; /// /// Ensures that subsequently drawn default properties belong to a framed section. @@ -171,17 +150,16 @@ public static void SectionLayout(Action drawContent) /// /// Thrown when is . /// - public static bool EnsureSection( + public bool EnsureSection( string title = "Properties", string? description = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(title); - SectionLayoutState? layout = s_sectionLayout; + SectionLayoutState? layout = m_sectionLayout; if (layout is null) return true; - return layout.sectionIndex == 0 - ? SectionHeader(title, description) - : layout.contentVisible; + return layout.sectionIndex == 0 ? SectionHeader(title, description) : layout.contentVisible; } /// @@ -203,13 +181,14 @@ public static bool EnsureSection( /// when the section content should be drawn; otherwise, /// when the section is collapsed. /// - public static bool SectionHeader( + public bool SectionHeader( string title, string? description = null, Action? drawLeadingControl = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(title); - SectionLayoutState? layout = s_sectionLayout; + SectionLayoutState? layout = m_sectionLayout; if (layout is null) { NativeImGui.SeparatorText(title); @@ -227,7 +206,6 @@ public static bool SectionHeader( uint stateId = NativeImGui.GetID($"##inspector_section_{sectionIndex}"); ImGuiStorage* storage = NativeImGui.Raw.GetStateStorage(); bool open = NativeImGui.Raw.GetBool(storage, stateId, 1) != 0; - NativeImGui.SetCursorScreenPos(new Vector2(origin.X + legendInset, origin.Y)); bool titlePressed = false; Vector2 titleSize = GetClickableTextSize(title, Vector2.Zero); @@ -240,11 +218,7 @@ public static bool SectionHeader( NativeImGui.SameLine(0f, style.inspectorHeaderControlSpacing); } - titlePressed = ClickableText( - $"inspector_section_title_{sectionIndex}", - title, - new Vector2(MathF.Max(1f, titleSize.X), frameHeight), - description); + titlePressed = ClickableText($"inspector_section_title_{sectionIndex}", title, new Vector2(MathF.Max(1f, titleSize.X), frameHeight), description); } finally { @@ -264,18 +238,11 @@ public static bool SectionHeader( float top = origin.Y + frameHeight * 0.5f; float legendLeft = legendMin.X - style.inspectorSectionLegendGap; float legendRight = legendMax.X + style.inspectorSectionLegendGap; - NativeImGui.SetCursorScreenPos(contentCursor); layout.contentVisible = open; if (!open) { - DrawCollapsedSectionBorder( - NativeImGui.Raw.GetWindowDrawList(), - origin.X, - origin.X + width, - top, - legendLeft, - legendRight); + DrawCollapsedSectionBorder(NativeImGui.Raw.GetWindowDrawList(), origin.X, origin.X + width, top, legendLeft, legendRight); NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionSpacing)); return false; } @@ -291,7 +258,7 @@ public static bool SectionHeader( return true; } - private static void DrawCollapsedSectionBorder( + private void DrawCollapsedSectionBorder( ImDrawList* draw, float left, float right, @@ -300,23 +267,11 @@ private static void DrawCollapsedSectionBorder( float legendRight ) { uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorSectionBorder); - DrawCappedSectionSegment( - draw, - left, - MathF.Max(left, legendLeft), - top, - capAtStart: true, - color); - DrawCappedSectionSegment( - draw, - MathF.Min(right, legendRight), - right, - top, - capAtStart: false, - color); + DrawCappedSectionSegment(draw, left, MathF.Max(left, legendLeft), top, capAtStart: true, color); + DrawCappedSectionSegment(draw, MathF.Min(right, legendRight), right, top, capAtStart: false, color); } - private static void DrawCappedSectionSegment( + private void DrawCappedSectionSegment( ImDrawList* draw, float from, float to, @@ -328,71 +283,32 @@ uint color float halfThickness = thickness * 0.5f; float halfCap = style.inspectorCollapsedSectionCapLength * 0.5f; float capX = capAtStart ? from : to; - float innerX = capAtStart - ? MathF.Min(to, capX + halfThickness) - : MathF.Max(from, capX - halfThickness); - ImGui.DrawNativeLine( - draw, - new Vector2(capX, centerY - halfCap), - new Vector2(capX, centerY + halfCap), - color, - thickness - ); - ImGui.DrawNativeLine( - draw, - new Vector2(innerX, centerY), - new Vector2(capAtStart ? to : from, centerY), - color, - thickness - ); + float innerX = capAtStart ? MathF.Min(to, capX + halfThickness) : MathF.Max(from, capX - halfThickness); + ImGui.DrawNativeLine(draw, new Vector2(capX, centerY - halfCap), new Vector2(capX, centerY + halfCap), color, thickness); + ImGui.DrawNativeLine(draw, new Vector2(innerX, centerY), new Vector2(capAtStart ? to : from, centerY), color, thickness); } - private static void CompleteSection(SectionLayoutState layout) + private void CompleteSection(SectionLayoutState layout) { if (!layout.active) return; - NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionPadding.Y)); NativeImGui.Unindent(style.inspectorSectionPadding.X); - float bottom = MathF.Max( - NativeImGui.GetCursorScreenPos().Y, - layout.top + NativeImGui.GetFrameHeight()); + float bottom = MathF.Max(NativeImGui.GetCursorScreenPos().Y, layout.top + NativeImGui.GetFrameHeight()); uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorSectionBorder); ImDrawList* draw = NativeImGui.Raw.GetWindowDrawList(); - float radius = MathF.Min( - style.inspectorSectionRounding, - MathF.Max(0f, MathF.Min(layout.right - layout.left, bottom - layout.top) * 0.5f)); + float radius = MathF.Min(style.inspectorSectionRounding, MathF.Max(0f, MathF.Min(layout.right - layout.left, bottom - layout.top) * 0.5f)); float leftLegend = MathF.Max(layout.left + radius, layout.legendLeft); float rightLegend = MathF.Min(layout.right - radius, layout.legendRight); ImGui.DrawNativePathLine(draw, new Vector2(leftLegend, layout.top)); ImGui.DrawNativePathLine(draw, new Vector2(layout.left + radius, layout.top)); - ImGui.DrawNativePathQuadratic( - draw, - new Vector2(layout.left, layout.top), - new Vector2(layout.left, layout.top + radius), - 0 - ); + ImGui.DrawNativePathQuadratic(draw, new Vector2(layout.left, layout.top), new Vector2(layout.left, layout.top + radius), 0); ImGui.DrawNativePathLine(draw, new Vector2(layout.left, bottom - radius)); - ImGui.DrawNativePathQuadratic( - draw, - new Vector2(layout.left, bottom), - new Vector2(layout.left + radius, bottom), - 0 - ); + ImGui.DrawNativePathQuadratic(draw, new Vector2(layout.left, bottom), new Vector2(layout.left + radius, bottom), 0); ImGui.DrawNativePathLine(draw, new Vector2(layout.right - radius, bottom)); - ImGui.DrawNativePathQuadratic( - draw, - new Vector2(layout.right, bottom), - new Vector2(layout.right, bottom - radius), - 0 - ); + ImGui.DrawNativePathQuadratic(draw, new Vector2(layout.right, bottom), new Vector2(layout.right, bottom - radius), 0); ImGui.DrawNativePathLine(draw, new Vector2(layout.right, layout.top + radius)); - ImGui.DrawNativePathQuadratic( - draw, - new Vector2(layout.right, layout.top), - new Vector2(layout.right - radius, layout.top), - 0 - ); + ImGui.DrawNativePathQuadratic(draw, new Vector2(layout.right, layout.top), new Vector2(layout.right - radius, layout.top), 0); ImGui.DrawNativePathLine(draw, new Vector2(rightLegend, layout.top)); NativeImGui.Raw.PathStroke(draw, color, (int)(ImDrawFlags.None), style.borderSize); NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionSpacing)); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs index db1eec291..db25fd6ab 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs @@ -1,18 +1,15 @@ using System; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { private const float C_INLINE_RENAME_FOCUS_OFFSET = 1f; - /// /// Draws a compact single-line rename editor inside an existing row. /// @@ -44,7 +41,7 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when is not finite or is not positive. /// - public static InlineRenameResult InlineRename( + public InlineRenameResult InlineRename( string id, ref string text, ref bool requestFocus, @@ -52,17 +49,14 @@ public static InlineRenameResult InlineRename( nuint capacity = 512, float width = -1f ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); if (!float.IsFinite(rowHeight) || rowHeight <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(rowHeight), - rowHeight, - "Inline rename row height must be finite and positive."); + throw new ArgumentOutOfRangeException(nameof(rowHeight), rowHeight, "Inline rename row height must be finite and positive."); } - float fieldWidth = MathF.Max( - 1f, - width < 0f ? NativeImGui.GetContentRegionAvail().X : width); + + float fieldWidth = MathF.Max(1f, width < 0f ? NativeImGui.GetContentRegionAvail().X : width); string controlId = $"##rename_{id}"; bool selectAll = requestFocus; NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inlineRenameFramePadding); @@ -72,9 +66,7 @@ public static InlineRenameResult InlineRename( { float fieldHeight = NativeImGui.GetFrameHeight(); Vector2 cursor = NativeImGui.GetCursorScreenPos(); - NativeImGui.SetCursorScreenPos(new Vector2( - cursor.X, - cursor.Y + MathF.Max(0f, (rowHeight - fieldHeight) * 0.5f))); + NativeImGui.SetCursorScreenPos(new Vector2(cursor.X, cursor.Y + MathF.Max(0f, (rowHeight - fieldHeight) * 0.5f))); NativeImGui.SetNextItemWidth(fieldWidth); if (selectAll) { @@ -85,22 +77,17 @@ public static InlineRenameResult InlineRename( NativeImGui.PushStyleColor((int)(ImGuiCol.NavCursor), EditorPalette.transparent); try { - submitted = ImGuiUtf8Buffer.InputText( - controlId, - null, - ref text, - (nuint)capacity, - ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.AutoSelectAll); + submitted = ImGuiUtf8Buffer.InputText(controlId, null, ref text, (nuint)capacity, ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.AutoSelectAll); } finally { NativeImGui.PopStyleColor(); } + bool deactivated = NativeImGui.IsItemDeactivated(); - bool escapePressed = NativeImGui.IsKeyPressed(ImGuiKey.Escape); + bool escapePressed = NativeImGui.IsKeyPressed((Inno.Native.ImGui.ImGuiKey)ImGuiKey.Escape); bool active = NativeImGui.IsItemActive(); - bool contextMenuRequested = active && - NativeImGui.IsMouseReleased((int)(ImGuiMouseButton.Right)); + bool contextMenuRequested = active && NativeImGui.IsMouseReleased((int)(ImGuiMouseButton.Right)); if (selectAll && active) { uint inputId = NativeImGui.GetItemID(); @@ -111,6 +98,7 @@ public static InlineRenameResult InlineRename( requestFocus = false; } } + if (active) { Vector2 focusOffset = new(C_INLINE_RENAME_FOCUS_OFFSET); @@ -137,9 +125,8 @@ public static InlineRenameResult InlineRename( NativeImGui.Raw.ClearActiveID(); return InlineRenameResult.FocusLost; } - return deactivated - ? InlineRenameResult.FocusLost - : InlineRenameResult.None; + + return deactivated ? InlineRenameResult.FocusLost : InlineRenameResult.None; } finally { @@ -172,7 +159,7 @@ public sealed class InlineRenamePresentation /// Thrown when is empty. /// /// - /// Thrown when or is not finite and + /// Thrown when or is not finite and /// positive, or when is zero. /// public InlineRenamePresentation( @@ -185,25 +172,19 @@ public InlineRenamePresentation( throw new ArgumentException("An inline rename identifier is required.", nameof(id)); if (!float.IsFinite(width) || width <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(width), - width, - "The inline rename width must be finite and positive."); + throw new ArgumentOutOfRangeException(nameof(width), width, "The inline rename width must be finite and positive."); } + if (!float.IsFinite(rowHeight) || rowHeight <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(rowHeight), - rowHeight, - "The inline rename row height must be finite and positive."); + throw new ArgumentOutOfRangeException(nameof(rowHeight), rowHeight, "The inline rename row height must be finite and positive."); } + if (bufferSize == 0) { - throw new ArgumentOutOfRangeException( - nameof(bufferSize), - bufferSize, - "The inline rename buffer size must be positive."); + throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, "The inline rename buffer size must be positive."); } + this.id = id; this.width = width; this.rowHeight = rowHeight; @@ -214,17 +195,14 @@ public InlineRenamePresentation( /// Gets the stable ImGui identifier used by the input field. /// public string id { get; } - /// /// Gets the requested input width in logical pixels. /// public float width { get; } - /// /// Gets the height of the row area in which the input field is centered. /// public float rowHeight { get; } - /// /// Gets the maximum UTF-8 buffer size accepted by the input. /// @@ -240,17 +218,14 @@ public enum InlineRenameResult /// Indicates that the interaction remains active. /// None, - /// /// Indicates that the edited text should be committed. /// Commit, - /// /// Indicates that the input lost focus and its valid value should be committed before closing. /// FocusLost, - /// /// Indicates that the edited text should be discarded. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs new file mode 100644 index 000000000..ec76258fe --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Inno.Editor.ImGui.ImGuiWidget; +/// +/// Owns and retires transient controls by session and stable presentation owner. +/// +public sealed unsafe partial class ImGuiWidget +{ + private readonly EditorUiSession m_session; + internal ImGuiWidget(EditorUiSession session) + { + m_session = session ?? throw new ArgumentNullException(nameof(session)); + } + + internal void RetireOwner(string owner) + { + if (m_propertyGesture is not null && m_propertyEditKey.Owner == owner) + CancelEdits(); + var retired = new List<(string Owner, uint Window)>(); + foreach (var key in m_treeStatesByWindow.Keys) + { + if (string.Equals(key.Owner, owner, StringComparison.Ordinal)) + retired.Add(key); + } + + foreach (var key in retired) + m_treeStatesByWindow.Remove(key); + } + + internal void Retire() + { + CancelEdits(); + m_treeStatesByWindow.Clear(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs new file mode 100644 index 000000000..4b2880b0a --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.MixedValue.cs @@ -0,0 +1,46 @@ +using System; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui.ImGuiWidget; +/// +/// Presents mixed values using the same controls and shared History. +/// +public sealed partial class ImGuiWidget +{ + private bool m_isMixedValue; + + /// + /// Gets whether the currently drawn control group represents differing selected values. + /// + public bool isMixedValue => m_isMixedValue; + + /// + /// Draws one control group with the native mixed-value state, restoring the previous item flags on failure. + /// + /// + /// Whether the current selection contains different values. + /// + /// + /// Synchronous control drawing; never retained. + /// + public void MixedValue( + bool mixed, + Action draw + ) { + m_session.EnsureDrawing(); + ArgumentNullException.ThrowIfNull(draw); + bool previous = m_isMixedValue; + m_isMixedValue = mixed || previous; + NativeImGui.PushItemFlag((int)ImGuiItemFlagsPrivate.MixedValue, mixed); + try + { + draw(); + } + finally + { + NativeImGui.PopItemFlag(); + m_isMixedValue = previous; + } + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs index 2b3d9832b..e26b27f68 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs @@ -1,16 +1,14 @@ using System; using System.Numerics; - using Inno.Scripting.Api; using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Reusable editor widgets built on top of . /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { /// /// Opens a standard panel window and executes panel body. @@ -31,16 +29,16 @@ public static unsafe partial class ImGuiWidget /// Whether the panel body should use the current standard window padding. /// [ScriptingApiIgnore] - public static void PanelWindow( + public void PanelWindow( string title, ref bool isOpen, Action drawBody, ImGuiWindowFlags flags = ImGuiWindowFlags.NoCollapse, bool useWindowPadding = true ) { + m_session.EnsureDrawing(); if (!isOpen) return; - bool pushedPadding = false; bool beganWindow = false; try @@ -50,6 +48,7 @@ public static void PanelWindow( NativeImGui.PushStyleVar((int)(ImGuiStyleVar.WindowPadding), Vector2.Zero); pushedPadding = true; } + // Dock tabs need their own vertical breathing room. Applying the metric only while // Begin builds the window decorations keeps normal inputs compact without clipping // the first tab row against the main-menu work rect. @@ -66,17 +65,18 @@ public static void PanelWindow( { NativeImGui.PopStyleVar(2); } + beganWindow = true; if (pushedPadding) { NativeImGui.PopStyleVar(); pushedPadding = false; } + if (visible) { if (DrawPanelCloseButton(title)) isOpen = false; - if (isOpen) drawBody(); } @@ -109,26 +109,21 @@ public static void PanelWindow( /// /// Thrown when is . /// - public static void ConstrainedContent( + public void ConstrainedContent( string id, Action drawContent, bool useWindowPadding = true ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(drawContent); - float width = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); - Vector2 padding = useWindowPadding - ? ImGui.FromNative(NativeImGui.Raw.GetStyle()->WindowPadding) - : Vector2.Zero; + Vector2 padding = useWindowPadding ? ImGui.FromNative(NativeImGui.Raw.GetStyle()->WindowPadding) : Vector2.Zero; float contentWidth = MathF.Max(1f, width - padding.X * 2f); - ImGuiChildFlags childFlags = ImGuiChildFlags.AutoResizeY | - ImGuiChildFlags.AlwaysAutoResize; + ImGuiChildFlags childFlags = ImGuiChildFlags.AutoResizeY | ImGuiChildFlags.AlwaysAutoResize; if (useWindowPadding) childFlags |= ImGuiChildFlags.AlwaysUseWindowPadding; - ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoScrollWithMouse | - ImGuiWindowFlags.NoSavedSettings; + ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; NativeImGui.SetNextWindowContentSize(new Vector2(contentWidth, 0f)); bool visible = NativeImGui.BeginChild(id, new Vector2(width, 0f), (int)(childFlags), (int)(windowFlags)); try @@ -161,25 +156,20 @@ public static void ConstrainedContent( /// /// Thrown when is . /// - public static void HeaderSurface( + public void HeaderSurface( string id, Action drawContent, bool spanWindowPadding = false ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(drawContent); - ImGuiWindow* parentWindow = NativeImGui.Raw.GetCurrentWindow(); Vector2 contentCursor = NativeImGui.GetCursorScreenPos(); - Vector2 parentPadding = spanWindowPadding - ? ImGui.FromNative(parentWindow->WindowPadding) - : Vector2.Zero; + Vector2 parentPadding = spanWindowPadding ? ImGui.FromNative(parentWindow->WindowPadding) : Vector2.Zero; Vector2 headerOrigin = contentCursor - parentPadding; - float width = MathF.Max( - 1f, - NativeImGui.GetContentRegionAvail().X + parentPadding.X * 2f); + float width = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X + parentPadding.X * 2f); NativeImGui.SetCursorScreenPos(headerOrigin); - NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), EditorPalette.inspectorTargetHeader); NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.inspectorTargetHeaderBorder); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inspectorTargetHeaderPadding); @@ -188,9 +178,7 @@ public static void HeaderSurface( try { ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | ImGuiChildFlags.AutoResizeY; - ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoScrollWithMouse | - ImGuiWindowFlags.NoSavedSettings; + ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; bool visible = NativeImGui.BeginChild(id, new Vector2(width, 0f), (int)(childFlags), (int)(windowFlags)); // FrameStyle consumed the square outer-container rounding in BeginChild. Restore the // normal editor rounding before drawing controls inside the header. @@ -211,21 +199,17 @@ public static void HeaderSurface( NativeImGui.PopStyleColor(2); } - NativeImGui.SetCursorScreenPos(new Vector2( - contentCursor.X, - NativeImGui.GetCursorScreenPos().Y)); + NativeImGui.SetCursorScreenPos(new Vector2(contentCursor.X, NativeImGui.GetCursorScreenPos().Y)); } - private static bool DrawPanelCloseButton(string title) + private bool DrawPanelCloseButton(string title) { if (!NativeImGui.IsWindowDocked()) return DrawFloatingPanelCloseButton(title); - uint dockId = NativeImGui.GetWindowDockID(); ImGuiDockNode* dockNode = NativeImGui.Raw.DockBuilderGetNode(dockId); if (dockNode == null || NativeImGui.Raw.DockNodeBeginAmendTabBar(dockNode) == 0) return false; - try { ImGuiTabBar* tabBar = dockNode->TabBar; @@ -234,9 +218,7 @@ private static bool DrawPanelCloseButton(string title) ImRect tabBarBounds = tabBar->BarRect; float tabBarHeight = MathF.Max(1f, tabBarBounds.Max.Y - tabBarBounds.Min.Y); float iconSlotSize = MathF.Min(GetCompactIconSize().X, tabBarHeight); - Vector2 itemCenter = new( - tabBarBounds.Max.X - iconSlotSize * 0.5f, - (tabBarBounds.Min.Y + tabBarBounds.Max.Y) * 0.5f); + Vector2 itemCenter = new(tabBarBounds.Max.X - iconSlotSize * 0.5f, (tabBarBounds.Min.Y + tabBarBounds.Max.Y) * 0.5f); Vector2 itemMinimum = itemCenter - new Vector2(iconSlotSize * 0.5f); Vector2 itemMaximum = itemCenter + new Vector2(iconSlotSize * 0.5f); ImRect itemBounds = new() @@ -247,29 +229,16 @@ private static bool DrawPanelCloseButton(string title) uint itemId = NativeImGui.GetID($"##panel_close_{dockId}"); bool hovered = false; bool held = false; - ImGuiButtonFlags buttonFlags = (ImGuiButtonFlags)( - (int)ImGuiButtonFlagsPrivate.AllowOverlap | - (int)ImGuiButtonFlagsPrivate.NoNavFocus | - (int)ImGuiButtonFlagsPrivate.PressedOnClickRelease); + ImGuiButtonFlags buttonFlags = (ImGuiButtonFlags)((int)ImGuiButtonFlagsPrivate.AllowOverlap | (int)ImGuiButtonFlagsPrivate.NoNavFocus | (int)ImGuiButtonFlagsPrivate.PressedOnClickRelease); ImGuiWindow* hoveredWindow = NativeImGui.Raw.GetCurrentContext()->HoveredWindow; - bool hostHovered = hoveredWindow != null && - hoveredWindow->RootWindowDockTree == dockNode->HostWindow->RootWindowDockTree; + bool hostHovered = hoveredWindow != null && hoveredWindow->RootWindowDockTree == dockNode->HostWindow->RootWindowDockTree; bool mouseHovered = hostHovered && NativeImGui.IsMouseHoveringRect(itemMinimum, itemMaximum); bool itemAdded = NativeImGui.Raw.ItemAdd(&itemBounds, itemId, null, 0) != 0; - bool pressed = hostHovered && itemAdded && - NativeImGui.ButtonBehavior(itemBounds, itemId, out hovered, out held, (int)buttonFlags); + bool pressed = hostHovered && itemAdded && NativeImGui.ButtonBehavior(itemBounds, itemId, out hovered, out held, (int)buttonFlags); hovered |= mouseHovered; pressed |= mouseHovered && NativeImGui.IsMouseClicked((int)(ImGuiMouseButton.Left)); - - uint iconColor = hovered || held - ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) - : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); - DrawPanelCloseMark( - NativeImGui.Raw.GetWindowDrawList(), - itemCenter, - iconSlotSize, - iconColor); - + uint iconColor = hovered || held ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); + DrawPanelCloseMark(NativeImGui.Raw.GetWindowDrawList(), itemCenter, iconSlotSize, iconColor); if (hovered && BeginMenuTooltip()) { NativeImGui.TextUnformatted($"Close {title}"); @@ -284,32 +253,47 @@ private static bool DrawPanelCloseButton(string title) } } - private static bool DrawFloatingPanelCloseButton(string title) + private bool DrawFloatingPanelCloseButton(string title) { ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); if ((window->Flags & (int)ImGuiWindowFlags.NoTitleBar) != 0) return false; ImGuiStyle* nativeStyle = NativeImGui.Raw.GetStyle(); float size = GetCompactIconSize().X; - Vector2 maximum = new(window->Pos.X + window->Size.X - nativeStyle->WindowBorderSize - nativeStyle->FramePadding.X, - window->Pos.Y + (window->TitleBarHeight + size) * 0.5f); + Vector2 maximum = new(window->Pos.X + window->Size.X - nativeStyle->WindowBorderSize - nativeStyle->FramePadding.X, window->Pos.Y + (window->TitleBarHeight + size) * 0.5f); Vector2 minimum = maximum - new Vector2(size); - ImRect bounds = new() { Min = ImGui.ToNative(minimum), Max = ImGui.ToNative(maximum) }; + ImRect bounds = new() + { + Min = ImGui.ToNative(minimum), + Max = ImGui.ToNative(maximum) + }; ImRect previousClip = window->ClipRect; ImDrawList* draw = NativeImGui.Raw.GetWindowDrawList(); Vector2 windowMaximum = ImGui.FromNative(window->Pos) + ImGui.FromNative(window->Size); - window->ClipRect = new ImRect { Min = window->Pos, Max = ImGui.ToNative(windowMaximum) }; + window->ClipRect = new ImRect + { + Min = window->Pos, + Max = ImGui.ToNative(windowMaximum) + }; ImGui.PushNativeClipRectangle(draw, ImGui.FromNative(window->Pos), windowMaximum, 0); try { uint id = NativeImGui.GetID("##floating_panel_close"); bool hovered = false, held = false; - bool pressed = NativeImGui.Raw.ItemAdd(&bounds, id, null, 0) != 0 && NativeImGui.ButtonBehavior( - bounds, id, out hovered, out held, - (int)ImGuiButtonFlagsPrivate.NoNavFocus | (int)ImGuiButtonFlagsPrivate.PressedOnClickRelease); - uint iconColor = hovered || held - ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) - : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); + bool pressed = NativeImGui.Raw.ItemAdd( + &bounds, + id, + null, + 0 + ) != 0 + && NativeImGui.ButtonBehavior( + bounds, + id, + out hovered, + out held, + (int)ImGuiButtonFlagsPrivate.NoNavFocus | (int)ImGuiButtonFlagsPrivate.PressedOnClickRelease + ); + uint iconColor = hovered || held ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); DrawPanelCloseMark(draw, minimum + new Vector2(size * 0.5f), size, iconColor); if (hovered) DrawItemTooltip($"Close {title}"); @@ -322,25 +306,17 @@ private static bool DrawFloatingPanelCloseButton(string title) } } - private static void DrawPanelCloseMark( + private void DrawPanelCloseMark( ImDrawList* drawList, Vector2 center, float slotSize, uint color ) { float thickness = MathF.Max(1f, style.borderSize); - float extent = MathF.Max( - thickness, - slotSize * 0.5f * 0.7071f - thickness); + float extent = MathF.Max(thickness, slotSize * 0.5f * 0.7071f - thickness); var diagonal = new Vector2(extent); ImGui.DrawNativeLine(drawList, center - diagonal, center + diagonal, color, thickness); - ImGui.DrawNativeLine( - drawList, - center + new Vector2(-extent, extent), - center + new Vector2(extent, -extent), - color, - thickness - ); + ImGui.DrawNativeLine(drawList, center + new Vector2(-extent, extent), center + new Vector2(extent, -extent), color, thickness); } /// @@ -352,14 +328,14 @@ uint color /// /// Thrown when is . /// - public static void Hint(string text) + public void Hint(string text) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); NativeImGui.BeginDisabled(true); try { - NativeImGui.PushTextWrapPos( - NativeImGui.GetCursorPosX() + MathF.Max(1f, NativeImGui.GetContentRegionAvail().X)); + NativeImGui.PushTextWrapPos(NativeImGui.GetCursorPosX() + MathF.Max(1f, NativeImGui.GetContentRegionAvail().X)); try { NativeImGui.TextUnformatted(text); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs index 2a6ae67a0..5c4439f5a 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs @@ -1,20 +1,16 @@ using System; using System.Numerics; -using System.Text; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { private const string C_COMPACT_FLOAT_FORMAT = "%.1f"; private const string C_PRECISE_FLOAT_FORMAT = "%.9g"; - /// /// Draws a two-column property row with a stable internal identifier. /// @@ -34,19 +30,17 @@ public static unsafe partial class ImGuiWidget /// Thrown when is empty or whitespace. /// /// - /// Thrown when or is . + /// Thrown when or is . /// - public static void PropertyRow( + public void PropertyRow( string id, string label, Action drawValue, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); - PropertyRow( - id, - () => PropertyLabel(label, tooltip), - drawValue); + PropertyRow(id, () => PropertyLabel(label, tooltip), drawValue); } /// @@ -65,20 +59,18 @@ public static void PropertyRow( /// Thrown when is empty or whitespace. /// /// - /// Thrown when or is . + /// Thrown when or is . /// - public static void PropertyRow( + public void PropertyRow( string id, Action drawLabel, Action drawValue ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(drawLabel); ArgumentNullException.ThrowIfNull(drawValue); - - ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp - | ImGuiTableFlags.NoSavedSettings - | ImGuiTableFlags.NoPadOuterX; + ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.NoPadOuterX; if (!NativeImGui.BeginTable($"##property_{id}", 2, (int)flags, Vector2.Zero, 0f)) { return; @@ -105,8 +97,9 @@ Action drawValue /// Configures the label and input columns of an active table with a shared two-to-three ratio. /// A following fixed-width action column may be added by the caller. /// - public static void SetupPropertyColumns() + public void SetupPropertyColumns() { + m_session.EnsureDrawing(); NativeImGui.TableSetupColumn("##label", (int)(ImGuiTableColumnFlags.WidthStretch), style.propertyLabelRatio); NativeImGui.TableSetupColumn("##value", (int)(ImGuiTableColumnFlags.WidthStretch), 1f - style.propertyLabelRatio); } @@ -123,10 +116,11 @@ public static void SetupPropertyColumns() /// /// Thrown when is null. /// - public static void PropertyLabel( + public void PropertyLabel( string label, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(label); NativeImGui.AlignTextToFramePadding(); WrappedText(label); @@ -146,14 +140,15 @@ public static void PropertyLabel( /// Optional explanation displayed while the metadata is hovered. /// /// - /// Thrown when or is + /// Thrown when or is /// . /// - public static void MetadataValue( + public void MetadataValue( string metadata, Action drawValue, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(metadata); ArgumentNullException.ThrowIfNull(drawValue); NativeImGui.AlignTextToFramePadding(); @@ -179,14 +174,15 @@ public static void MetadataValue( /// Optional explanation displayed while the combined value is hovered. /// /// - /// Thrown when or is + /// Thrown when or is /// . /// - public static void MetadataValue( + public void MetadataValue( string metadata, string value, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(metadata); ArgumentNullException.ThrowIfNull(value); NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.textDisabled); @@ -222,13 +218,14 @@ public static void MetadataValue( /// /// when the value changed. /// - public static bool AxisDragFloat( + public bool AxisDragFloat( string id, string axis, ref float value, float width, float speed = 0.1f ) { + m_session.EnsureDrawing(); DrawAxisPrefix(id, axis, width); return CompactDragFloat($"##axis_float_{id}_{axis}", ref value, speed); } @@ -254,29 +251,20 @@ public static bool AxisDragFloat( /// /// when the exact value changed. /// - public static bool CompactDragFloat( + public bool CompactDragFloat( string label, ref float value, float speed = 0.1f, float? minimum = null, float? maximum = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(label); ValidateFloatBounds(minimum, maximum); bool bounded = minimum.HasValue; - ImGuiSliderFlags flags = ImGuiSliderFlags.NoRoundToFormat | - (bounded ? ImGuiSliderFlags.AlwaysClamp : ImGuiSliderFlags.None); - string format = WantsPreciseFloatInput(label) - ? C_PRECISE_FLOAT_FORMAT - : C_COMPACT_FLOAT_FORMAT; - return NativeImGui.DragFloat( - label, - ref value, - speed, - minimum ?? 0f, - maximum ?? 0f, - format, - (int)(flags)); + ImGuiSliderFlags flags = ImGuiSliderFlags.NoRoundToFormat | (bounded ? ImGuiSliderFlags.AlwaysClamp : ImGuiSliderFlags.None); + string format = WantsPreciseFloatInput(label) ? C_PRECISE_FLOAT_FORMAT : C_COMPACT_FLOAT_FORMAT; + return NativeImGui.DragFloat(label, ref value, speed, minimum ?? 0f, maximum ?? 0f, format, (int)(flags)); } /// @@ -297,34 +285,23 @@ public static bool CompactDragFloat( /// /// when the exact value changed. /// - public static bool CompactSliderFloat( + public bool CompactSliderFloat( string label, ref float value, float minimum, float maximum ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(label); ValidateFloatBounds(minimum, maximum); ImGuiSliderFlags flags = ImGuiSliderFlags.NoRoundToFormat | ImGuiSliderFlags.AlwaysClamp; if (WantsPreciseFloatInput(label)) { float speed = MathF.Max((maximum - minimum) * 0.01f, 0.0001f); - return NativeImGui.DragFloat( - label, - ref value, - speed, - minimum, - maximum, - C_PRECISE_FLOAT_FORMAT, - (int)(flags)); + return NativeImGui.DragFloat(label, ref value, speed, minimum, maximum, C_PRECISE_FLOAT_FORMAT, (int)(flags)); } - return NativeImGui.SliderFloat( - label, - ref value, - minimum, - maximum, - C_COMPACT_FLOAT_FORMAT, - (int)(flags)); + + return NativeImGui.SliderFloat(label, ref value, minimum, maximum, C_COMPACT_FLOAT_FORMAT, (int)(flags)); } /// @@ -348,13 +325,14 @@ float maximum /// /// when the value changed. /// - public static bool AxisDragInt( + public bool AxisDragInt( string id, string axis, ref int value, float width, float speed = 1f ) { + m_session.EnsureDrawing(); DrawAxisPrefix(id, axis, width); return NativeImGui.DragInt($"##axis_int_{id}_{axis}", ref value, speed, 0, 0, "%d", 0); } @@ -368,10 +346,11 @@ public static bool AxisDragInt( /// /// Drawing callback. /// - public static void Disabled( + public void Disabled( bool disabled, Action draw ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(draw); NativeImGui.BeginDisabled(disabled); try @@ -396,19 +375,14 @@ Action draw /// /// Marker Y coordinate. /// - public static void InsertionLine( + public void InsertionLine( float fromX, float toX, float y ) { + m_session.EnsureDrawing(); uint color = NativeImGui.GetColorU32((int)(ImGuiCol.DragDropTarget)); - ImGui.DrawNativeLine( - NativeImGui.Raw.GetForegroundDrawList((ImGuiViewport*)null), - new Vector2(fromX, y), - new Vector2(toX, y), - color, - style.interactionOverlayThickness - ); + ImGui.DrawNativeLine(NativeImGui.Raw.GetForegroundDrawList((ImGuiViewport*)null), new Vector2(fromX, y), new Vector2(toX, y), color, style.interactionOverlayThickness); } /// @@ -421,10 +395,11 @@ float y /// /// Maximum target coordinate. /// - public static void DropTargetHighlight( + public void DropTargetHighlight( Vector2 min, Vector2 max ) { + m_session.EnsureDrawing(); ImGui.DrawNativeRectangle( NativeImGui.Raw.GetForegroundDrawList((ImGuiViewport*)null), min, @@ -436,98 +411,39 @@ Vector2 max ); } - /// - /// Converts an identifier into a readable editor label. - /// - /// - /// Source identifier. - /// - /// - /// A label with separators and title casing. - /// - public static string NicifyName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return string.Empty; - } - - var builder = new StringBuilder(name.Length + 8); - char previous = '\0'; - for (int i = 0; i < name.Length; i++) - { - char current = name[i]; - if (current == '_' || current == '-') - { - if (builder.Length > 0 && builder[builder.Length - 1] != ' ') - { - builder.Append(' '); - } - - previous = current; - continue; - } - - if (i > 0 && char.IsUpper(current) && char.IsLower(previous) && builder[builder.Length - 1] != ' ') - { - builder.Append(' '); - } - - builder.Append(builder.Length == 0 ? char.ToUpperInvariant(current) : current); - previous = current; - } - - return builder.ToString(); - } - - private static void DrawAxisPrefix( + private void DrawAxisPrefix( string id, string axis, float width ) { ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentException.ThrowIfNullOrWhiteSpace(axis); - float height = NativeImGui.GetFrameHeight(); - float axisWidth = MathF.Min( - MathF.Max(style.axisPrefixMinimumWidth, height), - width * style.axisPrefixWidthRatio); + float axisWidth = MathF.Min(MathF.Max(style.axisPrefixMinimumWidth, height), width * style.axisPrefixWidthRatio); Vector2 prefixMin = NativeImGui.GetCursorScreenPos(); Vector2 prefixMax = prefixMin + new Vector2(axisWidth, height); - ImGui.DrawNativeFilledRectangle( - NativeImGui.Raw.GetWindowDrawList(), - prefixMin, - prefixMax, - NativeImGui.ColorConvertFloat4ToU32(GetAxisColor(axis)), - 1f, - 0 - ); - + ImGui.DrawNativeFilledRectangle(NativeImGui.Raw.GetWindowDrawList(), prefixMin, prefixMax, NativeImGui.ColorConvertFloat4ToU32(GetAxisColor(axis)), 1f, 0); Vector2 textSize = NativeImGui.CalcTextSize(axis); Vector2 textPosition = prefixMin + (new Vector2(axisWidth, height) - textSize) * 0.5f; ImGui.DrawNativeText(NativeImGui.Raw.GetWindowDrawList(), textPosition, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.text), axis); - NativeImGui.Dummy(new Vector2(axisWidth, height)); NativeImGui.SameLine(0f, 0f); NativeImGui.SetNextItemWidth(MathF.Max(1f, width - axisWidth)); } - private static bool WantsPreciseFloatInput(string label) + private bool WantsPreciseFloatInput(string label) { - bool inputRequested = NativeImGui.IsMouseDoubleClicked((int)(ImGuiMouseButton.Left)) || - (NativeImGui.Raw.GetIO()->KeyCtrl != 0 && - NativeImGui.IsMouseClicked((int)(ImGuiMouseButton.Left))); + bool inputRequested = NativeImGui.IsMouseDoubleClicked((int)(ImGuiMouseButton.Left)) + || (NativeImGui.Raw.GetIO()->KeyCtrl != 0 + && NativeImGui.IsMouseClicked((int)(ImGuiMouseButton.Left))); if (!inputRequested) return false; Vector2 minimum = NativeImGui.GetCursorScreenPos(); - Vector2 maximum = minimum + new Vector2( - MathF.Max(1f, NativeImGui.CalcItemWidth()), - NativeImGui.GetFrameHeight()); - return NativeImGui.IsWindowHovered((int)(ImGuiHoveredFlags.RootAndChildWindows)) - && NativeImGui.IsMouseHoveringRect(minimum, maximum, true); + Vector2 maximum = minimum + new Vector2(MathF.Max(1f, NativeImGui.CalcItemWidth()), NativeImGui.GetFrameHeight()); + return NativeImGui.IsWindowHovered((int)(ImGuiHoveredFlags.RootAndChildWindows)) && NativeImGui.IsMouseHoveringRect(minimum, maximum, true); } - private static void ValidateFloatBounds( + private void ValidateFloatBounds( float? minimum, float? maximum ) { @@ -535,13 +451,14 @@ private static void ValidateFloatBounds( { throw new ArgumentException("Floating-point bounds must either both be supplied or both be omitted."); } + if (minimum is not float lower || maximum is not float upper) return; if (!float.IsFinite(lower) || !float.IsFinite(upper) || lower > upper) throw new ArgumentOutOfRangeException(nameof(maximum), "Floating-point bounds must be finite and ordered."); } - private static Vector4 GetAxisColor(string axis) + private Vector4 GetAxisColor(string axis) { return axis.ToUpperInvariant() switch { diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs index 47d3cb16e..11d479d53 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs @@ -1,14 +1,12 @@ using System; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static partial class ImGuiWidget +public sealed partial class ImGuiWidget { /// /// Draws a single-line search field with a stable identifier. @@ -31,13 +29,14 @@ public static partial class ImGuiWidget /// /// when the query changed; otherwise, . /// - public static bool SearchInput( + public bool SearchInput( string id, string hint, ref string query, nuint capacity = 256, float width = -1f ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); NativeImGui.SetNextItemWidth(width < 0f ? MathF.Max(1f, NativeImGui.GetContentRegionAvail().X) : width); return ImGuiUtf8Buffer.InputText($"##search_{id}", hint, ref query, capacity, ImGuiInputTextFlags.None); @@ -64,13 +63,14 @@ public static bool SearchInput( /// /// when popup content should be drawn; otherwise, . /// - public static bool BeginSearchPopup( + public bool BeginSearchPopup( string id, ref string query, string hint, nuint capacity = 256, float width = -1f ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); if (width < 0f) width = style.searchPopupWidth; @@ -86,5 +86,5 @@ public static bool BeginSearchPopup( /// /// Ends a popup opened by . /// - public static void EndSearchPopup() => EndMenuPopup(); + public void EndSearchPopup() => EndMenuPopup(); } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs new file mode 100644 index 000000000..993ecc401 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using Inno.Core.Identity; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui.ImGuiWidget; +/// +/// Applies ordered selection gestures through the authoritative interaction owner. +/// +public sealed unsafe partial class ImGuiWidget +{ + /// + /// Applies ordinary, additive or visible-range selection using the active keyboard modifiers. + /// + /// + /// The session's authoritative interaction service. + /// + /// + /// The freshly resolved clicked target. + /// + /// + /// Current view order; collapsed or filtered items must be omitted. + /// + public void SelectTarget( + EditorInteractions interactions, + IdentityObject target, + IReadOnlyList visibleTargets + ) { + m_session.EnsureDrawing(); + ArgumentNullException.ThrowIfNull(interactions); + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(visibleTargets); + ImGuiIO* io = NativeImGui.Raw.GetIO(); + bool additive = io->KeyCtrl != 0 || io->KeySuper != 0; + if (io->KeyShift != 0) + interactions.SelectRange(visibleTargets, target, additive); + else if (additive) + interactions.ToggleSelection(target); + else + interactions.SetSelection(target); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs index 6ce56b59b..70cf5fe24 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs @@ -1,16 +1,13 @@ using System; using System.Numerics; - using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { /// /// Begins a combo whose popup opens below the control and stays within its containing window. @@ -37,28 +34,25 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when contains a presentation mode unsupported by the editor selector. /// - public static bool BeginBoundedCombo( + public bool BeginBoundedCombo( string id, string preview, ImGuiComboFlags flags = ImGuiComboFlags.None ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(preview); if ((flags & ~ImGuiComboFlags.WidthFitPreview) != 0) throw new ArgumentOutOfRangeException(nameof(flags), flags, "The combo flag is not supported by the editor selector."); - ImGuiStyle* nativeStyle = NativeImGui.Raw.GetStyle(); - float width = (flags & ImGuiComboFlags.WidthFitPreview) != 0 - ? NativeImGui.CalcTextSize(preview).X + NativeImGui.GetFrameHeight() + nativeStyle->FramePadding.X * 2f - : NativeImGui.CalcItemWidth(); + float width = (flags & ImGuiComboFlags.WidthFitPreview) != 0 ? NativeImGui.CalcTextSize(preview).X + NativeImGui.GetFrameHeight() + nativeStyle->FramePadding.X * 2f : NativeImGui.CalcItemWidth(); return BeginMenuSelector(id, preview, MathF.Max(1f, width), MathF.Max(1f, width)); } /// /// Ends a combo opened by . /// - public static void EndBoundedCombo() => EndMenuSelector(); - + public void EndBoundedCombo() => EndMenuSelector(); /// /// Draws a compact selector control and begins a work-area-bounded menu popup. /// @@ -85,24 +79,22 @@ public static bool BeginBoundedCombo( /// Thrown when is . /// /// - /// Thrown when or is not positive. + /// Thrown when or is not positive. /// - public static bool BeginMenuSelector( + public bool BeginMenuSelector( string id, string preview, float width, float minimumPopupWidth ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(preview); if (width <= 0f) throw new ArgumentOutOfRangeException(nameof(width), width, "Selector width must be positive."); if (minimumPopupWidth <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(minimumPopupWidth), - minimumPopupWidth, - "Popup width must be positive."); + throw new ArgumentOutOfRangeException(nameof(minimumPopupWidth), minimumPopupWidth, "Popup width must be positive."); } string popupId = $"##menu_selector_popup_{id}"; @@ -115,25 +107,20 @@ float minimumPopupWidth bool open = NativeImGui.IsPopupOpen(popupId); if (pressed) NativeImGui.OpenPopup(popupId); - DrawMenuSelectorFrame(minimum, size, preview, hovered, active || open); Vector2 popupOrigin = new(minimum.X, minimum.Y + height); NativeImGui.SetNextWindowPos(popupOrigin, (int)ImGuiCond.Always, Vector2.Zero); ImGuiViewport* parentViewport = NativeImGui.Raw.GetWindowViewport(); NativeImGui.SetNextWindowViewport(parentViewport->ID); - SetWorkAreaPopupSize( - MathF.Max(width, minimumPopupWidth), - popupOrigin, - parentViewport); + SetWorkAreaPopupSize(MathF.Max(width, minimumPopupWidth), popupOrigin, parentViewport); return BeginMenuPopup(popupId); } /// /// Ends a selector popup opened by . /// - public static void EndMenuSelector() => EndMenuPopup(); - - private static void SetWorkAreaPopupSize( + public void EndMenuSelector() => EndMenuPopup(); + private void SetWorkAreaPopupSize( float requestedWidth, Vector2 popupOrigin, ImGuiViewport* viewport @@ -145,12 +132,10 @@ private static void SetWorkAreaPopupSize( float maximumHeight = MathF.Min(availableHeight, containingWindow->Size.Y * 0.45f); float availableWidth = MathF.Max(1f, MathF.Min(windowMaximum.X, viewportMaximum.X) - popupOrigin.X); float popupWidth = Math.Clamp(requestedWidth, 1f, availableWidth); - SetMenuPopupSizeConstraints( - new Vector2(popupWidth, 0f), - new Vector2(popupWidth, MathF.Max(1f, maximumHeight))); + SetMenuPopupSizeConstraints(new Vector2(popupWidth, 0f), new Vector2(popupWidth, MathF.Max(1f, maximumHeight))); } - private static void DrawMenuSelectorFrame( + private void DrawMenuSelectorFrame( Vector2 minimum, Vector2 size, string preview, @@ -158,25 +143,14 @@ private static void DrawMenuSelectorFrame( bool active ) { ImGuiStyle* nativeStyle = NativeImGui.Raw.GetStyle(); - uint background = NativeImGui.GetColorU32( - (int)(active ? ImGuiCol.FrameBgActive : hovered ? ImGuiCol.FrameBgHovered : ImGuiCol.FrameBg)); + uint background = NativeImGui.GetColorU32((int)(active ? ImGuiCol.FrameBgActive : hovered ? ImGuiCol.FrameBgHovered : ImGuiCol.FrameBg)); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); Vector2 maximum = minimum + size; float arrowSlot = size.Y; float arrowMinimumX = MathF.Max(minimum.X, maximum.X - arrowSlot); - ImDrawFlags previewRounding = size.X <= arrowSlot - ? ImDrawFlags.RoundCornersAll - : ImDrawFlags.RoundCornersLeft; - ImGui.DrawNativeFilledRectangle( - drawList, - minimum, - new Vector2(arrowMinimumX, maximum.Y), - background, - nativeStyle->FrameRounding, - (int)(previewRounding) - ); - uint arrowBackground = NativeImGui.GetColorU32( - (int)(active || hovered ? ImGuiCol.ButtonHovered : ImGuiCol.Button)); + ImDrawFlags previewRounding = size.X <= arrowSlot ? ImDrawFlags.RoundCornersAll : ImDrawFlags.RoundCornersLeft; + ImGui.DrawNativeFilledRectangle(drawList, minimum, new Vector2(arrowMinimumX, maximum.Y), background, nativeStyle->FrameRounding, (int)(previewRounding)); + uint arrowBackground = NativeImGui.GetColorU32((int)(active || hovered ? ImGuiCol.ButtonHovered : ImGuiCol.Button)); ImGui.DrawNativeFilledRectangle( drawList, new Vector2(arrowMinimumX, minimum.Y), @@ -185,7 +159,6 @@ bool active nativeStyle->FrameRounding, (int)(size.X <= arrowSlot ? ImDrawFlags.RoundCornersAll : ImDrawFlags.RoundCornersRight) ); - Vector2 textMinimum = minimum + ImGui.FromNative(nativeStyle->FramePadding); Vector2 textMaximum = new(arrowMinimumX, maximum.Y); ImGui.PushNativeClipRectangle(drawList, textMinimum, textMaximum, 1); @@ -196,7 +169,8 @@ bool active new Vector2(arrowMinimumX + nativeStyle->FramePadding.Y, minimum.Y + nativeStyle->FramePadding.Y), NativeImGui.GetColorU32((int)(ImGuiCol.Text)), ImGuiDir.Down, - 1f); + 1f + ); NativeImGui.RenderFrameBorder(minimum, maximum, nativeStyle->FrameRounding); } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs index c1cd3daee..31c078b1e 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs @@ -1,45 +1,41 @@ using System; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { - private static float s_appliedZoom = float.NaN; - private static bool? s_appliedCompactMode; - + private float m_appliedZoom = float.NaN; + private bool? m_appliedCompactMode; /// /// Gets the centralized editor layout metrics shared by every widget and feature panel. /// - public static EditorStyleMetrics style { get; } = new(); + public EditorStyleMetrics style { get; } = new(); /// /// Applies the centralized editor layout metrics and palette to the current ImGui context. /// - public static void SetupStyle() + public void SetupStyle() { + m_session.EnsureDrawing(); ImGuiStyle* nativeStyle = NativeImGui.Raw.GetStyle(); ApplyLayoutStyle(nativeStyle); EditorPalette.Apply(nativeStyle); - s_appliedZoom = style.zoom; - s_appliedCompactMode = style.isCompact; + m_appliedZoom = style.zoom; + m_appliedCompactMode = style.isCompact; } - internal static void ApplyPendingStyle() + internal void ApplyPendingStyle() { - if (float.IsNaN(s_appliedZoom) - || MathF.Abs(s_appliedZoom - style.zoom) > 0.0001f - || s_appliedCompactMode != style.isCompact) + if (float.IsNaN(m_appliedZoom) || MathF.Abs(m_appliedZoom - style.zoom) > 0.0001f || m_appliedCompactMode != style.isCompact) SetupStyle(); } - private static void ApplyLayoutStyle(ImGuiStyle* nativeStyle) + private void ApplyLayoutStyle(ImGuiStyle* nativeStyle) { nativeStyle->Alpha = EditorPalette.opacityOpaque; nativeStyle->DisabledAlpha = style.disabledAlpha; diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs index d3f09720f..2a4058778 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs @@ -1,18 +1,90 @@ using System; using System.Numerics; - using Inno.Scripting.Api; using Inno.Native.ImGui; -using Inno.Adapter.Presentation.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { + private int m_iconCatalogFrame = -1; + private float m_iconCatalogWidth; + /// + /// Measures the standard glyph column once per native frame in this UI session. + /// + /// + /// The widest visible standard glyph at the current font size, at least one text-line height. + /// + public float GetIconCatalogWidth() + { + m_session.EnsureDrawing(); + int frame = NativeImGui.GetFrameCount(); + if (m_iconCatalogFrame == frame) + return m_iconCatalogWidth; + float fontSize = NativeImGui.GetFontSize(); + float width = NativeImGui.GetTextLineHeight(); + foreach (var entry in EditorIconCatalog.standard) + { + Vector4 bounds = GetGlyphVisualBounds(fontSize, entry.Glyph); + width = MathF.Max(width, bounds.Z - bounds.X); + } + + m_iconCatalogWidth = width; + m_iconCatalogFrame = frame; + return width; + } + + /// + /// Measures a glyph using the current font without exposing its native font handle. + /// + /// + /// The positive baked font size. + /// + /// + /// A nonempty string whose first Unicode scalar identifies the glyph. + /// + /// + /// The glyph's visible left, top, right and bottom offsets. + /// + /// + /// The glyph text is empty. + /// + public Vector4 GetGlyphVisualBounds( + float fontSize, + string text + ) { + m_session.EnsureDrawing(); + return GetGlyphVisualBoundsNative(NativeImGui.Raw.GetFont(), fontSize, text); + } + + /// + /// Centers a glyph in the current window without exposing a native draw list. + /// + /// + /// The positive baked font size. + /// + /// + /// The glyph text. + /// + /// + /// The requested visible center in screen coordinates. + /// + /// + /// Packed text color. + /// + public void AddGlyphCentered( + float fontSize, + string text, + Vector2 center, + uint color + ) { + m_session.EnsureDrawing(); + AddGlyphCenteredNative(NativeImGui.Raw.GetWindowDrawList(), NativeImGui.Raw.GetFont(), fontSize, text, center, color); + } + /// /// Gets the visible bounds of the first glyph in a string at a requested font size. /// @@ -32,23 +104,19 @@ public static unsafe partial class ImGuiWidget /// /// Thrown when is empty. /// - [ScriptingApiIgnore] - public static Vector4 GetGlyphVisualBounds( + private Vector4 GetGlyphVisualBoundsNative( ImFont* font, float fontSize, string text ) { + m_session.EnsureDrawing(); if (string.IsNullOrEmpty(text)) throw new ArgumentException("Glyph text cannot be empty.", nameof(text)); - int codepoint = char.ConvertToUtf32(text, 0); ImFontBaked* baked = NativeImGui.Raw.GetFontBaked(font, fontSize, -1f); - ImFontGlyph* glyph = codepoint <= ushort.MaxValue - ? NativeImGui.Raw.FindGlyph(baked, (ushort)codepoint) - : null; + ImFontGlyph* glyph = codepoint <= ushort.MaxValue ? NativeImGui.Raw.FindGlyph(baked, (ushort)codepoint) : null; if (glyph != null) return new Vector4(glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - NativeImGui.Raw.PushFont(font, fontSize); Vector2 fallbackSize = NativeImGui.CalcTextSize(text); NativeImGui.PopFont(); @@ -76,8 +144,7 @@ string text /// /// Packed ImGui text color. /// - [ScriptingApiIgnore] - public static void AddGlyphCentered( + private void AddGlyphCenteredNative( ImDrawList* drawList, ImFont* font, float fontSize, @@ -85,17 +152,10 @@ public static void AddGlyphCentered( Vector2 center, uint color ) { - Vector4 bounds = GetGlyphVisualBounds(font, fontSize, text); - Vector2 visibleCenterOffset = new( - (bounds.X + bounds.Z) * 0.5f, - (bounds.Y + bounds.W) * 0.5f); - ImGui.DrawNativeText( - drawList, - font, - fontSize, - center - visibleCenterOffset, - color, - text); + m_session.EnsureDrawing(); + Vector4 bounds = GetGlyphVisualBoundsNative(font, fontSize, text); + Vector2 visibleCenterOffset = new((bounds.X + bounds.Z) * 0.5f, (bounds.Y + bounds.W) * 0.5f); + ImGui.DrawNativeText(drawList, font, fontSize, center - visibleCenterOffset, color, text); } /// @@ -119,13 +179,11 @@ uint color /// /// Thrown when is . /// - public static bool ClickableText( + public bool ClickableText( string id, string text, string? tooltip = null - ) - => ClickableText(id, text, GetCompactClickableTextSize(), tooltip); - + ) => ClickableText(id, text, GetCompactClickableTextSize(), tooltip); /// /// Draws a text link with no button background and highlights it only through text presentation. /// @@ -144,10 +202,11 @@ public static bool ClickableText( /// /// Thrown when is . /// - public static bool HoverText( + public bool HoverText( string id, string text ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(text); Vector2 cursor = NativeImGui.GetCursorScreenPos(); @@ -155,25 +214,16 @@ string text bool pressed = NativeImGui.InvisibleButton($"##hover_text_{id}", size); bool hovered = NativeImGui.IsItemHovered(); bool active = NativeImGui.IsItemActive(); - Vector4 color = active - ? EditorPalette.accentActive - : hovered - ? EditorPalette.accentHovered - : EditorPalette.accentActive; + Vector4 color = active ? EditorPalette.accentActive : hovered ? EditorPalette.accentHovered : EditorPalette.accentActive; uint packedColor = NativeImGui.ColorConvertFloat4ToU32(color); ImGui.DrawNativeText(NativeImGui.Raw.GetWindowDrawList(), cursor, packedColor, text); if (hovered) { NativeImGui.SetMouseCursor((int)(ImGuiMouseCursor.Hand)); float underlineY = cursor.Y + size.Y; - ImGui.DrawNativeLine( - NativeImGui.Raw.GetWindowDrawList(), - new Vector2(cursor.X, underlineY), - new Vector2(cursor.X + size.X, underlineY), - packedColor, - 1f - ); + ImGui.DrawNativeLine(NativeImGui.Raw.GetWindowDrawList(), new Vector2(cursor.X, underlineY), new Vector2(cursor.X + size.X, underlineY), packedColor, 1f); } + return pressed; } @@ -199,11 +249,12 @@ string text /// /// Thrown when is . /// - public static bool ClickableIcon( + public bool ClickableIcon( string id, string icon, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(icon); Vector2 controlSize = GetCompactIconSize(); @@ -211,22 +262,14 @@ public static bool ClickableIcon( bool pressed = NativeImGui.InvisibleButton($"##clickable_icon_{id}", controlSize); bool hovered = NativeImGui.IsItemHovered(); bool active = NativeImGui.IsItemActive(); - uint color = hovered || active - ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) - : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); - AddGlyphCentered( - NativeImGui.Raw.GetWindowDrawList(), - NativeImGui.Raw.GetFont(), - NativeImGui.GetFontSize(), - icon, - cursor + controlSize * 0.5f, - color); - + uint color = hovered || active ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); + AddGlyphCenteredNative(NativeImGui.Raw.GetWindowDrawList(), NativeImGui.Raw.GetFont(), NativeImGui.GetFontSize(), icon, cursor + controlSize * 0.5f, color); if (!string.IsNullOrWhiteSpace(tooltip) && hovered && BeginMenuTooltip()) { NativeImGui.TextUnformatted(tooltip); EndMenuTooltip(); } + return pressed; } @@ -257,20 +300,18 @@ public static bool ClickableIcon( /// /// Thrown when either component of is not positive. /// - public static bool ClickableText( + public bool ClickableText( string id, string text, Vector2 controlSize, string? tooltip = null ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(text); if (controlSize.X <= 0f || controlSize.Y <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(controlSize), - controlSize, - "Clickable text size must be positive."); + throw new ArgumentOutOfRangeException(nameof(controlSize), controlSize, "Clickable text size must be positive."); } Vector2 textSize = NativeImGui.CalcTextSize(text); @@ -278,15 +319,7 @@ public static bool ClickableText( bool pressed = NativeImGui.InvisibleButton($"##clickable_text_{id}", controlSize); bool hovered = NativeImGui.IsItemHovered(); bool active = NativeImGui.IsItemActive(); - DrawClickableTextPresentation( - NativeImGui.Raw.GetWindowDrawList(), - cursor, - controlSize, - text, - textSize, - hovered, - active); - + DrawClickableTextPresentation(NativeImGui.Raw.GetWindowDrawList(), cursor, controlSize, text, textSize, hovered, active); if (!string.IsNullOrWhiteSpace(tooltip) && hovered && BeginMenuTooltip()) { NativeImGui.TextUnformatted(tooltip); @@ -311,17 +344,15 @@ public static bool ClickableText( /// /// Thrown when either component of is not positive. /// - public static void CenteredText( + public void CenteredText( string text, Vector2 areaSize ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); if (areaSize.X <= 0f || areaSize.Y <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(areaSize), - areaSize, - "Centered text area size must be positive."); + throw new ArgumentOutOfRangeException(nameof(areaSize), areaSize, "Centered text area size must be positive."); } Vector2 cursor = NativeImGui.GetCursorScreenPos(); @@ -348,53 +379,42 @@ Vector2 areaSize /// /// Thrown when an area component is not positive or a padding component is negative. /// - public static void CenteredWrappedText( + public void CenteredWrappedText( string text, Vector2 areaSize, Vector2 padding ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); if (areaSize.X <= 0f || areaSize.Y <= 0f) { - throw new ArgumentOutOfRangeException( - nameof(areaSize), - areaSize, - "Centered wrapped text area size must be positive."); + throw new ArgumentOutOfRangeException(nameof(areaSize), areaSize, "Centered wrapped text area size must be positive."); } + if (padding.X < 0f || padding.Y < 0f) { - throw new ArgumentOutOfRangeException( - nameof(padding), - padding, - "Centered wrapped text padding cannot be negative."); + throw new ArgumentOutOfRangeException(nameof(padding), padding, "Centered wrapped text padding cannot be negative."); } Vector2 origin = NativeImGui.GetCursorScreenPos(); - Vector2 effectivePadding = new( - MathF.Min(padding.X, MathF.Max(0f, (areaSize.X - 1f) * 0.5f)), - MathF.Min(padding.Y, MathF.Max(0f, (areaSize.Y - 1f) * 0.5f))); + Vector2 effectivePadding = new(MathF.Min(padding.X, MathF.Max(0f, (areaSize.X - 1f) * 0.5f)), MathF.Min(padding.Y, MathF.Max(0f, (areaSize.Y - 1f) * 0.5f))); float wrapWidth = MathF.Max(1f, areaSize.X - effectivePadding.X * 2f); Vector2 textSize = NativeImGui.CalcTextSize(text, hide_text_after_double_hash: false, wrapWidth); Vector2 textPosition = origin + new Vector2( MathF.Max(effectivePadding.X, (areaSize.X - textSize.X) * 0.5f), - MathF.Max(effectivePadding.Y, (areaSize.Y - textSize.Y) * 0.5f)); + MathF.Max(effectivePadding.Y, (areaSize.Y - textSize.Y) * 0.5f) + ); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); ImGui.PushNativeClipRectangle(drawList, origin + effectivePadding, origin + areaSize - effectivePadding, 1); try { - ImGui.DrawNativeText( - drawList, - NativeImGui.Raw.GetFont(), - NativeImGui.GetFontSize(), - textPosition, - NativeImGui.GetColorU32((int)(ImGuiCol.Text)), - text, - wrapWidth); + ImGui.DrawNativeText(drawList, NativeImGui.Raw.GetFont(), NativeImGui.GetFontSize(), textPosition, NativeImGui.GetColorU32((int)(ImGuiCol.Text)), text, wrapWidth); } finally { NativeImGui.Raw.PopClipRect(drawList); } + NativeImGui.Dummy(areaSize); } @@ -410,10 +430,11 @@ Vector2 padding /// /// Thrown when is . /// - public static void ColoredText( + public void ColoredText( Vector4 color, string text ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); NativeImGui.PushStyleColor((int)(ImGuiCol.Text), color); try @@ -436,11 +457,11 @@ string text /// /// Thrown when is . /// - public static void WrappedText(string text) + public void WrappedText(string text) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); - NativeImGui.PushTextWrapPos( - NativeImGui.GetCursorPosX() + MathF.Max(1f, NativeImGui.GetContentRegionAvail().X)); + NativeImGui.PushTextWrapPos(NativeImGui.GetCursorPosX() + MathF.Max(1f, NativeImGui.GetContentRegionAvail().X)); try { NativeImGui.TextUnformatted(text); @@ -469,17 +490,15 @@ public static void WrappedText(string text) /// /// Thrown when either component of is negative. /// - public static Vector2 GetClickableTextSize( + public Vector2 GetClickableTextSize( string text, Vector2 padding ) { + m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(text); if (padding.X < 0f || padding.Y < 0f) { - throw new ArgumentOutOfRangeException( - nameof(padding), - padding, - "Clickable text padding cannot be negative."); + throw new ArgumentOutOfRangeException(nameof(padding), padding, "Clickable text padding cannot be negative."); } Vector2 textSize = NativeImGui.CalcTextSize(text); @@ -492,10 +511,11 @@ Vector2 padding /// /// A fixed-size area that follows the editor icon slot convention. /// - public static Vector2 GetCompactClickableTextSize() + public Vector2 GetCompactClickableTextSize() { + m_session.EnsureDrawing(); float iconSlotWidth = NativeImGui.GetTextLineHeight(); - return new Vector2(iconSlotWidth + style.iconLabelSpacing, NativeImGui.GetFrameHeight()); + return new Vector2(iconSlotWidth + this.style.iconLabelSpacing, NativeImGui.GetFrameHeight()); } /// @@ -504,13 +524,14 @@ public static Vector2 GetCompactClickableTextSize() /// /// A square size based on the current ImGui font size. /// - public static Vector2 GetCompactIconSize() + public Vector2 GetCompactIconSize() { + m_session.EnsureDrawing(); float iconSlotSize = NativeImGui.GetFontSize(); return new Vector2(iconSlotSize); } - private static void DrawClickableTextPresentation( + private void DrawClickableTextPresentation( ImDrawList* drawList, Vector2 minimum, Vector2 controlSize, @@ -519,9 +540,7 @@ private static void DrawClickableTextPresentation( bool hovered, bool active ) { - uint color = hovered || active - ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) - : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); + uint color = hovered || active ? NativeImGui.ColorConvertFloat4ToU32(EditorPalette.compactControlHovered) : NativeImGui.GetColorU32((int)(ImGuiCol.Text)); ImGui.DrawNativeText(drawList, minimum + (controlSize - textSize) * 0.5f, color, text); } @@ -538,14 +557,13 @@ bool active /// /// Whether to underline and emphasize the drawn icon and text. /// - public static void IconText( + public void IconText( string icon, string text, bool highlight ) { - ImGuiFontScope fontScope = highlight - ? ImGuiFont.PushStyle(ImGuiFontStyle.Bold | ImGuiFontStyle.Italic) - : default; + m_session.EnsureDrawing(); + ImGuiFontScope fontScope = highlight ? ImGuiFont.PushStyle(ImGuiFontStyle.Bold | ImGuiFontStyle.Italic) : default; try { Vector2 cursor = NativeImGui.GetCursorScreenPos(); @@ -553,38 +571,21 @@ bool highlight ImFont* font = NativeImGui.Raw.GetFont(); float fontSize = NativeImGui.GetFontSize(); float lineHeight = NativeImGui.GetTextLineHeight(); - Vector4 glyphBounds = GetGlyphVisualBounds(font, fontSize, icon); + Vector4 glyphBounds = GetGlyphVisualBoundsNative(font, fontSize, icon); float iconSlotWidth = MathF.Max(lineHeight, glyphBounds.Z - glyphBounds.X); Vector2 textSize = NativeImGui.CalcTextSize(text); Vector2 textPos = new(cursor.X + iconSlotWidth + style->ItemInnerSpacing.X, cursor.Y); - uint color = NativeImGui.GetColorU32((int)(ImGuiCol.Text)); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); - AddGlyphCentered( - drawList, - font, - fontSize, - icon, - new Vector2(cursor.X + iconSlotWidth * 0.5f, cursor.Y + lineHeight * 0.5f), - color); + AddGlyphCenteredNative(drawList, font, fontSize, icon, new Vector2(cursor.X + iconSlotWidth * 0.5f, cursor.Y + lineHeight * 0.5f), color); ImGui.DrawNativeText(drawList, textPos, color, text); - if (highlight) { - float lineY = cursor.Y + NativeImGui.GetTextLineHeight() - - ImGuiWidget.style.textDecorationOffset; - ImGui.DrawNativeLine( - drawList, - new Vector2(cursor.X, lineY), - new Vector2(textPos.X + textSize.X, lineY), - color, - ImGuiWidget.style.borderSize - ); + float lineY = cursor.Y + NativeImGui.GetTextLineHeight() - this.style.textDecorationOffset; + ImGui.DrawNativeLine(drawList, new Vector2(cursor.X, lineY), new Vector2(textPos.X + textSize.X, lineY), color, this.style.borderSize); } - NativeImGui.Dummy(new Vector2( - iconSlotWidth + style->ItemInnerSpacing.X + textSize.X, - lineHeight)); + NativeImGui.Dummy(new Vector2(iconSlotWidth + style->ItemInnerSpacing.X + textSize.X, lineHeight)); } finally { @@ -592,7 +593,7 @@ bool highlight } } - private static void IconTextAt( + private void IconTextAt( Vector2 screenPos, string icon, string text, diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs index 32221b71d..acedaa69b 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs @@ -1,32 +1,24 @@ using System; using System.Collections.Generic; using System.Numerics; - using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; namespace Inno.Editor.ImGui.ImGuiWidget; - /// /// Provides reusable editor controls and rendering helpers built on the native ImGui API. /// -public static unsafe partial class ImGuiWidget +public sealed unsafe partial class ImGuiWidget { - private static readonly Dictionary s_treeStatesByWindow = []; - private static bool s_hasNextTreeNodeOpen; - private static bool s_nextTreeNodeOpen; - - private static List s_treeNodeStack => GetTreeWindowState().treeNodeStack; - private static List s_lastNodeIdsByDepth => GetTreeWindowState().lastNodeIdsByDepth; - private static List s_lineSegments => GetTreeWindowState().lineSegments; - private static Dictionary s_hasNextSiblingById => GetTreeWindowState().hasNextSiblingById; - private static Dictionary s_openStatesById => GetTreeWindowState().openStatesById; - - private static int s_lastFrame - { - get => GetTreeWindowState().lastFrame; - set => GetTreeWindowState().lastFrame = value; - } + private readonly Dictionary<(string Owner, uint Window), TreeWidgetWindowState> m_treeStatesByWindow = []; + private bool m_hasNextTreeNodeOpen; + private bool m_nextTreeNodeOpen; + private List m_treeNodeStack => GetTreeWindowState().treeNodeStack; + private List m_lastNodeIdsByDepth => GetTreeWindowState().lastNodeIdsByDepth; + private List m_lineSegments => GetTreeWindowState().lineSegments; + private Dictionary m_hasNextSiblingById => GetTreeWindowState().hasNextSiblingById; + private Dictionary m_openStatesById => GetTreeWindowState().openStatesById; + private int m_lastFrame { get => GetTreeWindowState().lastFrame; set => GetTreeWindowState().lastFrame = value; } /// /// Draws a full-width interactive tree row whose non-leaf content toggles expansion when @@ -44,14 +36,14 @@ private static int s_lastFrame /// /// Interaction and row geometry for the submitted item. /// - public static TreeNodeResult TreeNode( + public TreeNodeResult TreeNode( string id, Action onDraw, in TreeNodeOptions options ) { + m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(onDraw); - bool isLeaf = options.isLeaf; BeginTreeFrameIfNeeded(); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); @@ -67,23 +59,22 @@ in TreeNodeOptions options } } - private static TreeNodeResult DrawTreeNode( + private TreeNodeResult DrawTreeNode( string id, Action onDraw, in TreeNodeOptions options, bool isLeaf, ImDrawList* drawList ) { - bool open = !isLeaf && s_openStatesById.TryGetValue(id, out bool storedOpen) && storedOpen; - if (!isLeaf && s_hasNextTreeNodeOpen) + bool open = !isLeaf && m_openStatesById.TryGetValue(id, out bool storedOpen) && storedOpen; + if (!isLeaf && m_hasNextTreeNodeOpen) { - open = s_nextTreeNodeOpen; - s_openStatesById[id] = open; + open = m_nextTreeNodeOpen; + m_openStatesById[id] = open; } - s_hasNextTreeNodeOpen = false; + m_hasNextTreeNodeOpen = false; NativeImGui.SetNextItemOpen(open, (int)(ImGuiCond.Always)); - Vector2 nodeCursor = NativeImGui.GetCursorScreenPos(); // Each disclosure, content row and overlay owns its own hit region. ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.None; @@ -91,7 +82,6 @@ private static TreeNodeResult DrawTreeNode( flags |= ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen; else flags |= ImGuiTreeNodeFlags.OpenOnArrow; - int depth = Math.Max(0, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); PruneTreeNodeStack(depth); bool hasNextSibling = TrackSiblingState(id, depth); @@ -107,57 +97,29 @@ private static TreeNodeResult DrawTreeNode( { NativeImGui.PopStyleColor(3); } + float nativeRowMaxY = NativeImGui.GetItemRectMax().Y; try { if (!isLeaf && NativeImGui.IsItemToggledOpen()) - s_openStatesById[id] = isOpen; - - TreeWidgetHighlightRect contentRect = DrawTreeNodeContentContainer( - id, - nodeCursor, - nativeRowMaxY, - onDraw, - options.drawViewportOverlay, - out Vector2 interactionMin); + m_openStatesById[id] = isOpen; + TreeWidgetHighlightRect contentRect = DrawTreeNodeContentContainer(id, nodeCursor, nativeRowMaxY, onDraw, options.drawViewportOverlay, out Vector2 interactionMin); bool hovered = NativeImGui.IsItemHovered((int)(ImGuiHoveredFlags.AllowWhenOverlappedByItem)); bool clicked = hovered && NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Left)); bool doubleClicked = hovered && NativeImGui.IsMouseDoubleClicked((int)(ImGuiMouseButton.Left)); if (!isLeaf && doubleClicked) - s_openStatesById[id] = !isOpen; - bool showHoverHighlight = hovered && - !options.suppressHoverHighlight && - NativeImGui.Raw.IsDragDropActive() == 0; + m_openStatesById[id] = !isOpen; + bool showHoverHighlight = hovered && !options.suppressHoverHighlight && NativeImGui.Raw.IsDragDropActive() == 0; if (options.selected || showHoverHighlight) - DrawTreeRowBackground( - drawList, - contentRect, - NativeImGui.GetColorU32((int)(options.selected ? ImGuiCol.Header : ImGuiCol.HeaderHovered))); + DrawTreeRowBackground(drawList, contentRect, NativeImGui.GetColorU32((int)(options.selected ? ImGuiCol.Header : ImGuiCol.HeaderHovered))); else if (options.showBackground) - DrawTreeRowBackground( - drawList, - contentRect, - NativeImGui.ColorConvertFloat4ToU32(options.backgroundColor)); - + DrawTreeRowBackground(drawList, contentRect, NativeImGui.ColorConvertFloat4ToU32(options.backgroundColor)); if (isOpen && !isLeaf) { - s_treeNodeStack.Add(new TreeWidgetNodeState - { - id = id, - cursor = nodeCursor, - rowMaxY = contentRect.max.Y, - hasNextSibling = hasNextSibling - }); + m_treeNodeStack.Add(new TreeWidgetNodeState { id = id, cursor = nodeCursor, rowMaxY = contentRect.max.Y, hasNextSibling = hasNextSibling }); } - return new TreeNodeResult( - isOpen && !isLeaf, - clicked, - doubleClicked, - hovered, - contentRect.min, - contentRect.max, - interactionMin); + return new TreeNodeResult(isOpen && !isLeaf, clicked, doubleClicked, hovered, contentRect.min, contentRect.max, interactionMin); } catch { @@ -173,25 +135,26 @@ private static TreeNodeResult DrawTreeNode( /// /// Whether the next row should be expanded. /// - public static void SetNextTreeNodeOpen(bool open) + public void SetNextTreeNodeOpen(bool open) { - s_hasNextTreeNodeOpen = true; - s_nextTreeNodeOpen = open; + m_session.EnsureDrawing(); + m_hasNextTreeNodeOpen = true; + m_nextTreeNodeOpen = open; } - private static TreeWidgetWindowState GetTreeWindowState() + private TreeWidgetWindowState GetTreeWindowState() { - int windowKey = ((nint)NativeImGui.Raw.GetWindowDrawList()).GetHashCode(); - if (!s_treeStatesByWindow.TryGetValue(windowKey, out TreeWidgetWindowState? state)) + var windowKey = (m_session.owner, NativeImGui.Raw.GetCurrentWindow()->ID); + if (!m_treeStatesByWindow.TryGetValue(windowKey, out TreeWidgetWindowState? state)) { state = new TreeWidgetWindowState(); - s_treeStatesByWindow[windowKey] = state; + m_treeStatesByWindow[windowKey] = state; } return state; } - private static TreeWidgetHighlightRect DrawTreeNodeContentContainer( + private TreeWidgetHighlightRect DrawTreeNodeContentContainer( string id, Vector2 nodeCursor, float nativeRowMaxY, @@ -204,10 +167,7 @@ out Vector2 interactionMin float contentX = nodeCursor.X + NativeImGui.GetTreeNodeToLabelSpacing(); float contentRightX = window->WorkRect.Max.X; float contentBottomY = window->InnerRect.Max.Y; - float contentOffsetX = contentX - windowPos.X + window->Scroll.X - - window->DC.GroupOffset.X - - window->DC.ColumnsOffset.X; - + float contentOffsetX = contentX - windowPos.X + window->Scroll.X - window->DC.GroupOffset.X - window->DC.ColumnsOffset.X; NativeImGui.SameLine(contentOffsetX, 0f); NativeImGui.BeginGroup(); NativeImGui.PushClipRect(new Vector2(contentX, nodeCursor.Y), new Vector2(contentRightX, contentBottomY), true); @@ -228,7 +188,6 @@ out Vector2 interactionMin min = new Vector2(windowPos.X, MathF.Min(nodeCursor.Y, contentMin.Y)), max = new Vector2(contentRightX, MathF.Max(nativeRowMaxY, contentMax.Y)) }; - Vector2 hitMin = new(contentX, rect.min.Y); Vector2 hitMax = rect.max; interactionMin = hitMin; @@ -242,13 +201,12 @@ out Vector2 interactionMin return rect; } - private static void DrawTreeNodeViewportOverlay( + private void DrawTreeNodeViewportOverlay( ImGuiWindow* window, Action? drawViewportOverlay ) { if (drawViewportOverlay is null) return; - float contentBoundaryX = window->DC.CursorMaxPos.X; float idealBoundaryX = window->DC.IdealMaxPos.X; try @@ -262,81 +220,70 @@ private static void DrawTreeNodeViewportOverlay( } } - private static void PushTransparentTreeNodeHeaderColors() + private void PushTransparentTreeNodeHeaderColors() { NativeImGui.PushStyleColor((int)(ImGuiCol.Header), EditorPalette.transparent); NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderHovered), EditorPalette.transparent); NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderActive), EditorPalette.transparent); } - private static void BeginTreeFrameIfNeeded() + private void BeginTreeFrameIfNeeded() { int frame = NativeImGui.GetFrameCount(); - if (frame == s_lastFrame) + if (frame == m_lastFrame) return; - TreeWidgetWindowState state = GetTreeWindowState(); state.treeNodeStack.Clear(); state.lastNodeIdsByDepth.Clear(); state.lineSegments.Clear(); - s_lastFrame = frame; + m_lastFrame = frame; } - private static void PruneTreeNodeStack(int nativeDepth) + private void PruneTreeNodeStack(int nativeDepth) { - while (s_treeNodeStack.Count > nativeDepth) - s_treeNodeStack.RemoveAt(s_treeNodeStack.Count - 1); + while (m_treeNodeStack.Count > nativeDepth) + m_treeNodeStack.RemoveAt(m_treeNodeStack.Count - 1); } - private static bool TrackSiblingState( + private bool TrackSiblingState( string id, int depth ) { - bool hasNextSibling = s_hasNextSiblingById.TryGetValue(id, out bool cachedHasNextSibling) && cachedHasNextSibling; - - while (s_lastNodeIdsByDepth.Count <= depth) - s_lastNodeIdsByDepth.Add(string.Empty); - - string previousId = s_lastNodeIdsByDepth[depth]; + bool hasNextSibling = m_hasNextSiblingById.TryGetValue(id, out bool cachedHasNextSibling) && cachedHasNextSibling; + while (m_lastNodeIdsByDepth.Count <= depth) + m_lastNodeIdsByDepth.Add(string.Empty); + string previousId = m_lastNodeIdsByDepth[depth]; if (!string.IsNullOrEmpty(previousId) && !string.Equals(previousId, id, StringComparison.Ordinal)) - s_hasNextSiblingById[previousId] = true; - - s_hasNextSiblingById[id] = false; - s_lastNodeIdsByDepth[depth] = id; - - for (int i = depth + 1; i < s_lastNodeIdsByDepth.Count; i++) - s_lastNodeIdsByDepth[i] = string.Empty; - + m_hasNextSiblingById[previousId] = true; + m_hasNextSiblingById[id] = false; + m_lastNodeIdsByDepth[depth] = id; + for (int i = depth + 1; i < m_lastNodeIdsByDepth.Count; i++) + m_lastNodeIdsByDepth[i] = string.Empty; return hasNextSibling; } - private static void DrawTreeGuideLines( + private void DrawTreeGuideLines( Vector2 nodeCursor, bool hasNextSibling, bool hasDisclosureArrow ) { - if (s_treeNodeStack.Count == 0) + if (m_treeNodeStack.Count == 0) return; - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); float textLineHeight = NativeImGui.GetTextLineHeight(); - float lineOverlap = ImGuiWidget.style.treeGuideLineOverlap; + float lineOverlap = this.style.treeGuideLineOverlap; float rowMaxY = nodeCursor.Y + textLineHeight + style->ItemSpacing.Y + lineOverlap; float rowCenterY = nodeCursor.Y + textLineHeight * 0.5f; float nodeToLabel = NativeImGui.GetTreeNodeToLabelSpacing(); - float guideOffset = nodeToLabel * 0.5f - ImGuiWidget.style.treeGuideLeftOffset; + float guideOffset = nodeToLabel * 0.5f - this.style.treeGuideLeftOffset; float labelStartX = nodeCursor.X + nodeToLabel; float fileConnectorPadding = MathF.Max(1f, style->ItemInnerSpacing.X); - float folderConnectorPadding = fileConnectorPadding * 2f + - ImGuiWidget.style.treeFolderConnectorPadding; - float targetX = hasDisclosureArrow - ? nodeCursor.X + guideOffset - folderConnectorPadding - : labelStartX - fileConnectorPadding; - - for (int i = 0; i < s_treeNodeStack.Count - 1; i++) + float folderConnectorPadding = fileConnectorPadding * 2f + this.style.treeFolderConnectorPadding; + float targetX = hasDisclosureArrow ? nodeCursor.X + guideOffset - folderConnectorPadding : labelStartX - fileConnectorPadding; + for (int i = 0; i < m_treeNodeStack.Count - 1; i++) { - TreeWidgetNodeState ancestorState = s_treeNodeStack[i]; - TreeWidgetNodeState pathChildState = s_treeNodeStack[i + 1]; + TreeWidgetNodeState ancestorState = m_treeNodeStack[i]; + TreeWidgetNodeState pathChildState = m_treeNodeStack[i + 1]; if (pathChildState.hasNextSibling) { float ancestorX = ancestorState.cursor.X + guideOffset; @@ -345,7 +292,7 @@ bool hasDisclosureArrow } } - TreeWidgetNodeState parentState = s_treeNodeStack[^1]; + TreeWidgetNodeState parentState = m_treeNodeStack[^1]; float branchX = parentState.cursor.X + guideOffset; float branchStartY = MathF.Max(parentState.rowMaxY, nodeCursor.Y); AddTreeLine(new Vector2(branchX, branchStartY), new Vector2(branchX, hasNextSibling ? rowMaxY : rowCenterY)); @@ -353,7 +300,7 @@ bool hasDisclosureArrow AddTreeLine(new Vector2(branchX, rowCenterY), new Vector2(targetX, rowCenterY)); } - private static void AddTreeLine( + private void AddTreeLine( Vector2 from, Vector2 to ) { @@ -362,22 +309,16 @@ Vector2 to from = SnapTreeLinePoint(from), to = SnapTreeLinePoint(to) }; - s_lineSegments.Add(line); - ImGui.DrawNativeLine( - NativeImGui.Raw.GetWindowDrawList(), - line.from, - line.to, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.treeGuide), - style.borderSize - ); + m_lineSegments.Add(line); + ImGui.DrawNativeLine(NativeImGui.Raw.GetWindowDrawList(), line.from, line.to, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.treeGuide), this.style.borderSize); } - private static Vector2 SnapTreeLinePoint(Vector2 point) + private Vector2 SnapTreeLinePoint(Vector2 point) { return new Vector2(MathF.Floor(point.X) + 0.5f, MathF.Floor(point.Y) + 0.5f); } - private static void DrawTreeRowBackground( + private void DrawTreeRowBackground( ImDrawList* drawList, TreeWidgetHighlightRect rect, uint color @@ -386,7 +327,6 @@ uint color ImGui.DrawNativeFilledRectangle(drawList, rect.min, rect.max, color, 0, 0); NativeImGui.Raw.ChannelsSetCurrent(drawList, 1); } - } /// @@ -414,32 +354,26 @@ public readonly struct TreeNodeOptions /// Gets whether the row is selected. /// public bool selected { get; init; } - /// /// Gets whether the row has no expandable children. /// public bool isLeaf { get; init; } - /// /// Gets whether a custom background is drawn behind an unselected row. /// public bool showBackground { get; init; } - /// /// Gets the custom background color used when is enabled. /// public Vector4 backgroundColor { get; init; } - /// /// Gets whether the row keeps its configured background while hovered. /// public bool suppressHoverHighlight { get; init; } - /// /// Gets whether ancestor and branch guide lines are omitted for this row. /// public bool hideGuideLines { get; init; } - /// /// Gets the callback that draws controls fixed to the current viewport without extending the /// tree's horizontal content boundary. @@ -474,33 +408,27 @@ Vector2 contentMin /// Gets whether child content should be rendered. /// public bool isOpen { get; } - /// /// Gets whether the content row was clicked. /// public bool isClicked { get; } - /// /// Gets whether the content row was double-clicked. Double-clicking a non-leaf content row /// also toggles its retained expansion state for the next frame. /// public bool isDoubleClicked { get; } - /// /// Gets whether the full row is hovered. /// public bool isHovered { get; } - /// /// Gets the row minimum screen coordinate. /// public Vector2 min { get; } - /// /// Gets the row maximum screen coordinate. /// public Vector2 max { get; } - /// /// Gets the minimum screen coordinate of the row's interactive content, excluding tree indentation. /// diff --git a/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Inno.Editor.Shaders.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Inno.Editor.Shaders.ImGui.csproj new file mode 100644 index 000000000..aa60e9a6f --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Inno.Editor.Shaders.ImGui.csproj @@ -0,0 +1,40 @@ + + + net10.0 + 14.0 + disable + enable + true + $(WarningsAsErrors);CS1572;CS1573;CS1591 + Editor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialInspectionDrawer.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialInspectionDrawer.cs similarity index 62% rename from src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialInspectionDrawer.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialInspectionDrawer.cs index 9cb69387f..839cca60a 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialInspectionDrawer.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialInspectionDrawer.cs @@ -1,21 +1,23 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Shaders; using System; using System.Linq; using Inno.Assets.Pipeline; using Inno.Editor.Inspection; using Inno.Rendering; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; -namespace Inno.Editor.Shaders; - +namespace Inno.Editor.Shaders.ImGui; [InspectionDrawer(typeof(MaterialAsset))] internal sealed class MaterialInspectionDrawer(IInspectionIconProvider icons) : InspectionDrawer { /// /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + public override string icon => Inno.Editor.ImGui.ImGuiIcon.File; + /// /// Retrieves the current icon from authoritative state. /// @@ -28,12 +30,15 @@ internal sealed class MaterialInspectionDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - MaterialAsset target -) - => context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetFileSystemEntry(target.assetPath, out AssetFileEntry entry) ? icons.GetIcon(entry) : icon; + protected override string GetIcon( + InspectionDrawContext context, + MaterialAsset target + ) => context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetFileSystemEntry( + target.assetPath, + out AssetFileEntry entry + ) ? icons.GetIcon(entry) : icon; /// /// Binds a caller-visible label to the current inspection target. /// @@ -46,11 +51,10 @@ MaterialAsset target /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - MaterialAsset target -) - => (target.name, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + MaterialAsset target + ) => (target.name, null); /// /// Renders the header presentation for the current editor frame. /// @@ -60,13 +64,14 @@ MaterialAsset target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - MaterialAsset target -) { + protected override void DrawHeader( + InspectionDrawContext context, + MaterialAsset target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) MaterialInspector.DrawHeader(context, documents, documents.Open(target)); } + /// /// Renders the value presentation for the current editor frame. /// @@ -76,10 +81,10 @@ MaterialAsset target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - MaterialAsset target -) { + protected override void Draw( + InspectionDrawContext context, + MaterialAsset target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) MaterialInspector.Draw(context, documents, documents.Open(target)); } @@ -91,7 +96,8 @@ internal sealed class MaterialSourceDrawer(IInspectionIconProvider /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + public override string icon => Inno.Editor.ImGui.ImGuiIcon.File; + /// /// Retrieves the current icon from authoritative state. /// @@ -104,10 +110,10 @@ internal sealed class MaterialSourceDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - AssetFileEntry target -) => icons.GetIcon(target); + protected override string GetIcon( + InspectionDrawContext context, + AssetFileEntry target + ) => icons.GetIcon(target); /// /// Checks whether this drawer supports the selected Inspector target. /// @@ -117,8 +123,7 @@ AssetFileEntry target /// /// when the documented condition is satisfied; otherwise, . /// -protected override bool CanInspect(AssetFileEntry target) - => !target.isDirectory && target.extension.Equals(".imaterial", StringComparison.OrdinalIgnoreCase); + protected override bool CanInspect(AssetFileEntry target) => !target.isDirectory && target.extension.Equals(".imaterial", StringComparison.OrdinalIgnoreCase); /// /// Binds a caller-visible label to the current inspection target. /// @@ -131,11 +136,10 @@ protected override bool CanInspect(AssetFileEntry target) /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - AssetFileEntry target -) - => (target.nameWithoutExtension, null); + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + AssetFileEntry target + ) => (target.nameWithoutExtension, null); /// /// Renders the header presentation for the current editor frame. /// @@ -145,13 +149,14 @@ AssetFileEntry target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - AssetFileEntry target -) { + protected override void DrawHeader( + InspectionDrawContext context, + AssetFileEntry target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) MaterialInspector.DrawHeader(context, documents, documents.OpenDraft(target.assetPath)); } + /// /// Renders the value presentation for the current editor frame. /// @@ -161,10 +166,10 @@ AssetFileEntry target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - AssetFileEntry target -) { + protected override void Draw( + InspectionDrawContext context, + AssetFileEntry target + ) { if (context.interactions.TryGetModule(out var documents) && documents is not null) MaterialInspector.Draw(context, documents, documents.OpenDraft(target.assetPath)); } @@ -177,12 +182,12 @@ internal static void DrawHeader( MaterialDocuments documents, Inno.Editor.Assets.AssetDraftDocuments.Draft draft ) { - EditorWidget.Disabled(draft.readOnly, () => + EditorUI.current.widgets.Disabled(draft.readOnly, () => { - if (NativeImGui.Button("Save")) + if (Ui.Button("Save")) _ = context.interactions.documents.Save(draft.documentId); - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.Button("Revert")) + Ui.SameLine(0f, -1f); + if (Ui.Button("Revert")) _ = context.interactions.documents.Revert(draft.documentId); }); } @@ -192,78 +197,86 @@ internal static void Draw( MaterialDocuments documents, Inno.Editor.Assets.AssetDraftDocuments.Draft draft ) { - documents.TouchInspection(draft); MaterialAsset material = documents.Read(draft.id); var edits = new DraftEdits(documents, draft, material); - NativeImGui.PushID(draft.id.ToString("N")); + Ui.PushId(draft.id.ToString("N")); try { - if (EditorWidget.SectionHeader("Preview", "Shows an isolated preview of the current Material draft.")) + if (EditorUI.current.widgets.SectionHeader("Preview", "Shows an isolated preview of the current Material draft.")) { if (material.shader is null || material.shader.isMissing) - EditorWidget.Hint("Choose an available Shader to preview this Material."); + EditorUI.current.widgets.Hint("Choose an available Shader to preview this Material."); else if (context.interactions.TryGetModule(out var shaderPreviews) && shaderPreviews is not null) - shaderPreviews.DrawMaterial(draft.id, material, MathF.Max(1f, MathF.Min(256f, NativeImGui.GetContentRegionAvail().X))); + shaderPreviews.DrawMaterial(draft.id, material, MathF.Max(1f, MathF.Min(256f, Ui.GetContentRegionAvailable().X))); else - EditorWidget.Hint("Material preview is unavailable."); + EditorUI.current.widgets.Hint("Material preview is unavailable."); } - bool materialOpen = EditorWidget.SectionHeader("Material", "Edits stay in this draft. Save publishes through normal asset import; Revert reloads the source."); + bool materialOpen = EditorUI.current.widgets.SectionHeader( + "Material", + "Edits stay in this draft. Save publishes through normal asset import; Revert reloads the source." + ); if (materialOpen) { - EditorWidget.Hint(draft.isDirty ? "Unsaved changes · Scene/Game unchanged" : "Saved · compilation and import are separate"); + EditorUI.current.widgets.Hint(draft.isDirty ? "Unsaved changes · Scene/Game unchanged" : "Saved · compilation and import are separate"); if (draft.error.Length != 0) - EditorWidget.Hint(draft.error); + EditorUI.current.widgets.Hint(draft.error); if (draft.readOnly) - EditorWidget.Hint("Installed material · copy to the project to edit"); - Draw("shader", "Shader", typeof(ShaderAsset), () => material.shader, value => material.shader = (ShaderAsset?)value); + EditorUI.current.widgets.Hint("Installed material · copy to the project to edit"); + Draw("shader", "Shader", typeof(ShaderAsset), () => material.shader, value => material.shader = (ShaderAsset? )value); } + ShaderDefinition? definition = material.shader?.definition; if (material.shader is null || material.shader.isMissing || definition is null) { if (materialOpen) - EditorWidget.Hint("Choose an available Shader. Existing overrides are retained until its interface can be resolved."); + EditorUI.current.widgets.Hint("Choose an available Shader. Existing overrides are retained until its interface can be resolved."); return; } - if (materialOpen && NativeImGui.SmallButton("Open Shader Editor")) + + if (materialOpen && Ui.SmallButton("Open Shader Editor")) { context.interactions.SetSelection(material.shader); context.interactions.OpenPanel("rendering.shader-editor"); } + if (materialOpen) { - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.SmallButton("Locate Shader") - && documents.assets.TryGetFileSystemEntry(material.shader.assetPath, out AssetFileEntry shaderEntry)) + Ui.SameLine(0f, -1f); + if (Ui.SmallButton("Locate Shader") && documents.assets.TryGetFileSystemEntry(material.shader.assetPath, out AssetFileEntry shaderEntry)) context.interactions.SetSelection(shaderEntry); } if (materialOpen && (definition.techniques.Length > 1 || material.techniqueId.isValid)) { - if (EditorWidget.BeginBoundedCombo("##technique", material.techniqueId.isValid ? material.techniqueId.value : "Automatic")) + if (EditorUI.current.widgets.BeginBoundedCombo("##technique", material.techniqueId.isValid ? material.techniqueId.value : "Automatic")) { try { - if (NativeImGui.Selectable("Automatic", !material.techniqueId.isValid) && !draft.readOnly) + if (Ui.Selectable("Automatic", !material.techniqueId.isValid) && !draft.readOnly) { material.techniqueId = default; - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } + foreach (ShaderTechniqueDefinition technique in definition.techniques) - if (NativeImGui.Selectable(technique.id.value, material.techniqueId == technique.id) && !draft.readOnly) + if (Ui.Selectable(technique.id.value, material.techniqueId == technique.id) && !draft.readOnly) { material.techniqueId = technique.id; - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } } - bool parametersOpen = EditorWidget.SectionHeader("Parameters", "Inherited values follow Shader defaults. Changing a field creates a Material override; Reset restores inheritance."); + bool parametersOpen = EditorUI.current.widgets.SectionHeader( + "Parameters", + "Inherited values follow Shader defaults. Changing a field creates a Material override; Reset restores inheritance." + ); bool groupOpen = parametersOpen; string currentGroup = ""; int visibleParameters = 0; @@ -271,15 +284,16 @@ Inno.Editor.Assets.AssetDraftDocuments.Draft draft { ShaderParameterPresentation presentation = documents.Presentation(material.shader, property.id, out string presentationError); if (presentationError.Length != 0) - EditorWidget.Hint("Parameter presentation unavailable: " + presentationError); + EditorUI.current.widgets.Hint("Parameter presentation unavailable: " + presentationError); if (!presentation.visible) continue; visibleParameters++; if (presentation.group != currentGroup) { currentGroup = presentation.group; - groupOpen = EditorWidget.SectionHeader(currentGroup.Length == 0 ? "Parameters" : currentGroup); + groupOpen = EditorUI.current.widgets.SectionHeader(currentGroup.Length == 0 ? "Parameters" : currentGroup); } + if (!groupOpen) continue; bool overridden = material.TryGet(property.id, out MaterialValue value); @@ -287,122 +301,138 @@ Inno.Editor.Assets.AssetDraftDocuments.Draft draft continue; if (!overridden) value = property.defaultValue; - NativeImGui.PushID(property.id.value); + Ui.PushId(property.id.value); try { - ShaderPropertyInspector.Draw(context, draft, "material." + property.id.value, property, value, - next => material.Set(property.id, next), edits, draft.readOnly, presentation); - EditorWidget.Hint(overridden ? "Material override" : "Inherited from Shader"); - if (overridden && !draft.readOnly && NativeImGui.SmallButton("Reset override")) + ShaderPropertyInspector.Draw( + context, + draft, + "material." + property.id.value, + property, + value, + next => material.Set(property.id, next), + edits, + draft.readOnly, + presentation + ); + EditorUI.current.widgets.Hint(overridden ? "Material override" : "Inherited from Shader"); + if (overridden && !draft.readOnly && Ui.SmallButton("Reset override")) { material.ReplaceProperties(material.properties.Where(entry => entry.id != property.id).ToArray()); - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } + if (value.kind == MaterialValueKind.Texture) { if (value.texture is { isMissing: false } texture && documents.previews.TryGetTexture(texture, out var preview)) { - float width = MathF.Max(1f, MathF.Min(128f, NativeImGui.GetContentRegionAvail().X)); + float width = MathF.Max(1f, MathF.Min(128f, Ui.GetContentRegionAvailable().X)); float height = width * preview.pixelHeight / preview.pixelWidth; if (height > 128f) { width *= 128f / height; height = 128f; } + documents.previews.Draw(preview, new(width, height)); } } } finally { - NativeImGui.PopID(); + Ui.PopId(); } } if (parametersOpen && visibleParameters == 0 && definition.keywords.Length == 0) - EditorWidget.Hint("No editable parameters."); - - if (parametersOpen && material.properties.Count != 0 && !draft.readOnly && NativeImGui.Button("Reset all overrides")) + EditorUI.current.widgets.Hint("No editable parameters."); + if (parametersOpen && material.properties.Count != 0 && !draft.readOnly && Ui.Button("Reset all overrides")) { material.ReplaceProperties([]); - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } foreach (ShaderKeywordDefinition keyword in parametersOpen ? definition.keywords : []) { string selected = keyword.options.FirstOrDefault(option => material.keywords.Contains(option)) ?? "None"; - if (!EditorWidget.BeginBoundedCombo(keyword.id, selected)) + if (!EditorUI.current.widgets.BeginBoundedCombo(keyword.id, selected)) continue; try { - if (NativeImGui.Selectable("None", selected == "None")) + if (Ui.Selectable("None", selected == "None")) SetOption(null); foreach (string option in keyword.options) - if (NativeImGui.Selectable(option, selected == option)) + if (Ui.Selectable(option, selected == option)) SetOption(option); } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } + void SetOption(string? next) { if (draft.readOnly) return; foreach (string option in keyword.options) material.SetKeyword(option, option == next); - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } } - MaterialPropertyEntry[] orphaned = material.properties.Where(entry => !definition.properties.Any(property => property.id == entry.id - && IsEditable(property) && ShaderPropertyInspector.Compatible(property.type, entry.value.kind))).ToArray(); + MaterialPropertyEntry[] orphaned = material.properties.Where(entry => !definition.properties.Any(property => property.id == entry.id && IsEditable(property) && ShaderPropertyInspector.Compatible(property.type, entry.value.kind))).ToArray(); if (orphaned.Length != 0) { - if (EditorWidget.SectionHeader("Unresolved Overrides", "These values were retained after an interface change. Remove them explicitly or restore a compatible Shader before publishing.")) + if (EditorUI.current.widgets.SectionHeader( + "Unresolved Overrides", + "These values were retained after an interface change. Remove them explicitly or restore a compatible Shader before publishing." + )) { foreach (MaterialPropertyEntry entry in orphaned) { - NativeImGui.TextUnformatted(entry.id.value + " · " + entry.value.kind); - if (!draft.readOnly && NativeImGui.SmallButton("Remove##" + entry.id.value)) + Ui.Text(entry.id.value + " · " + entry.value.kind); + if (!draft.readOnly && Ui.SmallButton("Remove##" + entry.id.value)) { material.ReplaceProperties(material.properties.Where(value => value.id != entry.id).ToArray()); - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } } } } + ShaderPropertyDefinition[] passBindings = definition.properties.Where(property => !IsEditable(property)).ToArray(); string[] unknownKeywords = material.keywords.Where(value => !definition.keywords.Any(keyword => keyword.options.Contains(value))).ToArray(); if (unknownKeywords.Length != 0) { - if (EditorWidget.SectionHeader("Unresolved Keywords", "These overrides no longer belong to the Shader interface. They remain until explicitly removed.")) + if (EditorUI.current.widgets.SectionHeader( + "Unresolved Keywords", + "These overrides no longer belong to the Shader interface. They remain until explicitly removed." + )) { foreach (string keyword in unknownKeywords) { - NativeImGui.TextUnformatted(keyword); - if (!draft.readOnly && NativeImGui.SmallButton("Remove##keyword." + keyword)) + Ui.Text(keyword); + if (!draft.readOnly && Ui.SmallButton("Remove##keyword." + keyword)) { material.SetKeyword(keyword, false); - documents.Edit(draft, material, true); + documents.Replace(draft.id, material); } } } } + if (passBindings.Length != 0) { - if (EditorWidget.SectionHeader("Pipeline Bindings", "These resources are supplied by the Render Pass, not by this Material.")) + if (EditorUI.current.widgets.SectionHeader("Pipeline Bindings", "These resources are supplied by the Render Pass, not by this Material.")) { foreach (ShaderPropertyDefinition property in passBindings) - EditorWidget.Hint(property.displayName + " · " + property.type + " · " + property.bindingOwner); + EditorUI.current.widgets.Hint(property.displayName + " · " + property.type + " · " + property.bindingOwner); } } - if (!NativeImGui.IsAnyItemActive()) - documents.Commit(draft); } finally { - NativeImGui.PopID(); + Ui.PopId(); } void Draw( @@ -411,45 +441,28 @@ void Draw( Type type, Func getter, Action setter - ) - => context.properties.DrawValue(context.editorContext, draft, "material." + path, label, type, getter, setter, edits, draft.readOnly); + ) => context.properties.DrawValue(context.editorContext, draft, "material." + path, label, type, getter, setter, edits, draft.readOnly); } private static bool IsEditable(ShaderPropertyDefinition property) => property.bindingOwner == ShaderPropertyBindingOwner.Material && property.bindingKind is ShaderPropertyBindingKind.Uniform or ShaderPropertyBindingKind.SampledTexture; - private sealed class DraftEdits( MaterialDocuments documents, Inno.Editor.Assets.AssetDraftDocuments.Draft draft, MaterialAsset material ) : IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) { + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) { mutation(); - documents.Replace(draft.id, material, !NativeImGui.IsAnyItemActive()); + documents.Replace(draft.id, material, mergeKey); return true; } } diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialSelectionDrawer.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialSelectionDrawer.cs similarity index 62% rename from src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialSelectionDrawer.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialSelectionDrawer.cs index 1e74a0292..fc5373a7d 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Materials/MaterialSelectionDrawer.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/MaterialSelectionDrawer.cs @@ -1,3 +1,8 @@ +using Inno.Editor.ImGui; +using Inno.Editor.Interactions; +using Inno.Core.Identity; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Shaders; using System; using System.Collections.Generic; using System.Linq; @@ -5,19 +10,21 @@ using Inno.Assets.Pipeline; using Inno.Editor.Inspection; using Inno.Rendering; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using ImGuiApi = Inno.Native.ImGui.ImGui; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; -namespace Inno.Editor.Shaders; - -[InspectionDrawer(typeof(AssetInspectionSelection))] -internal sealed class MaterialSelectionDrawer(IInspectionIconProvider icons) : InspectionDrawer +namespace Inno.Editor.Shaders.ImGui; +[InspectionDrawer(typeof(EditorSelectionSnapshot), priority: 100, conditional: true)] +internal sealed class MaterialSelectionDrawer( + IInspectionIconProvider icons, + EditorInteractions interactions +) : InspectionDrawer { /// /// Gets the icon glyph used to represent this item in the editor. /// -public override string icon => Inno.Adapter.Presentation.ImGui.ImGuiIcon.File; + public override string icon => Inno.Editor.ImGui.ImGuiIcon.File; + /// /// Retrieves the current icon from authoritative state. /// @@ -30,16 +37,25 @@ internal sealed class MaterialSelectionDrawer(IInspectionIconProvider /// The validated text representation owned by the caller. /// -protected override string GetIcon( - InspectionDrawContext context, - AssetInspectionSelection target -) { - Guid id = target.assetIds.FirstOrDefault(); + protected override string GetIcon( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) { + Guid id = GetAssetIds(target).FirstOrDefault(); return id != Guid.Empty - && context.interactions.TryGetModule(out var documents) && documents is not null - && documents.assets.TryGetInfo(id, out AssetInfo? info) && info is not null - && documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry) ? icons.GetIcon(entry) : icon; + && context.interactions.TryGetModule(out var documents) + && documents is not null + && documents.assets.TryGetInfo( + id, + out AssetInfo? info + ) + && info is not null + && documents.assets.TryGetFileSystemEntry( + info.assetPath, + out AssetFileEntry entry + ) ? icons.GetIcon(entry) : icon; } + /// /// Binds a caller-visible label to the current inspection target. /// @@ -52,12 +68,10 @@ AssetInspectionSelection target /// /// The validated (string name, actionstring? setter) that represents the completed operation. /// -protected override (string name, Action? setter) BindName( - InspectionDrawContext context, - AssetInspectionSelection target -) - => (target.assetIds.Count + " Assets", null); - + protected override (string name, Action? setter) BindName( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) => (GetAssetIds(target).Count + " Assets", null); /// /// Renders the header presentation for the current editor frame. /// @@ -67,10 +81,10 @@ AssetInspectionSelection target /// /// The existing target that receives the validated result. /// -protected override void DrawHeader( - InspectionDrawContext context, - AssetInspectionSelection target -) { + protected override void DrawHeader( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) { if (!TryOpenDrafts(context, target, out MaterialDocuments? documents, out var drafts)) return; bool readOnly = drafts.Any(value => value.readOnly); @@ -79,14 +93,15 @@ AssetInspectionSelection target { if (ImGuiApi.Button("Save Selected")) { - documents!.CommitMany(target.assetIds); + EditorUiSession.current.widgets.CommitEdits(); foreach (var draft in drafts) _ = context.interactions.documents.Save(draft.documentId); } + ImGuiApi.SameLine(0f, -1f); if (ImGuiApi.Button("Revert Selected")) { - documents!.CommitMany(target.assetIds); + EditorUiSession.current.widgets.CommitEdits(); using var transaction = context.interactions.history.BeginTransaction("Revert Materials"); foreach (var draft in drafts) _ = context.interactions.documents.Revert(draft.documentId); @@ -108,55 +123,58 @@ AssetInspectionSelection target /// /// The existing target that receives the validated result. /// -protected override void Draw( - InspectionDrawContext context, - AssetInspectionSelection target -) { + protected override void Draw( + InspectionDrawContext context, + EditorSelectionSnapshot target + ) { if (!TryOpenDrafts(context, target, out MaterialDocuments? documents, out var drafts)) { - Widget.Hint("Select Materials with compatible Shader interfaces to edit their common parameters."); + EditorUI.current.widgets.Hint("Select Materials with compatible Shader interfaces to edit their common parameters."); return; } + MaterialDocuments owner = documents!; var materials = drafts.ToDictionary(value => value.id, value => owner.Read(value.id)); bool readOnly = drafts.Any(value => value.readOnly); - ImGuiApi.PushID("materials." + string.Join(".", target.assetIds)); + ImGuiApi.PushId("materials." + string.Join(".", GetAssetIds(target))); ImGuiApi.BeginDisabled(readOnly); try { - Widget.Hint("Save runs per document. Unsaved edits do not change Scene/Game."); + EditorUI.current.widgets.Hint("Save runs per document. Unsaved edits do not change Scene/Game."); if (readOnly) - Widget.Hint("Selection includes installed read-only content. Copy it to the project before editing."); + EditorUI.current.widgets.Hint("Selection includes installed read-only content. Copy it to the project before editing."); foreach (var draft in drafts.Where(value => value.error.Length != 0)) - Widget.Hint(draft.path.localPath + ": " + draft.error); + EditorUI.current.widgets.Hint(draft.path.localPath + ": " + draft.error); var edits = new SelectionEdits(owner, materials); MaterialAsset first = materials.Values.First(); - bool selectionOpen = Widget.SectionHeader("Material Selection", "Changes are recorded together. Changing Shader retains each Material's independent overrides."); + bool selectionOpen = EditorUI.current.widgets.SectionHeader( + "Material Selection", + "Changes are recorded together. Changing Shader retains each Material's independent overrides." + ); if (selectionOpen) { if (materials.Values.Any(value => value.shader?.identity.persistentId != first.shader?.identity.persistentId)) - Widget.Hint("Shader · Mixed values"); - context.properties.DrawValue(context.editorContext, target, "materials.shader", "Shader", typeof(ShaderAsset), - () => first.shader, value => - { - foreach (MaterialAsset material in materials.Values) - material.shader = (ShaderAsset?)value; - }, edits, readOnly); + EditorUI.current.widgets.Hint("Shader · Mixed values"); + context.properties.DrawValue(context.editorContext, target, "materials.shader", "Shader", typeof(ShaderAsset), () => first.shader, value => + { + foreach (MaterialAsset material in materials.Values) + material.shader = (ShaderAsset? )value; + }, edits, readOnly); } + if (first.shader is null || first.shader.isMissing || first.shader.definition is not { } firstDefinition) { if (selectionOpen) - Widget.Hint("A selected Shader is unavailable."); + EditorUI.current.widgets.Hint("A selected Shader is unavailable."); return; } - ShaderTechniqueId[] commonTechniques = firstDefinition.techniques.Select(value => value.id) - .Where(id => materials.Values.All(material => material.shader is { isMissing: false, definition: { } definition } - && definition.techniques.Any(technique => technique.id == id))).ToArray(); + + ShaderTechniqueId[] commonTechniques = firstDefinition.techniques.Select(value => value.id).Where(id => materials.Values.All(material => material.shader is { isMissing: false, definition: { } definition } && definition.techniques.Any(technique => technique.id == id))).ToArray(); bool mixedTechnique = materials.Values.Any(value => value.techniqueId != first.techniqueId); if (selectionOpen && (commonTechniques.Length > 1 || materials.Values.Any(value => value.techniqueId.isValid))) { string current = mixedTechnique ? "Mixed Techniques" : first.techniqueId.isValid ? first.techniqueId.value : "Automatic"; - if (Widget.BeginBoundedCombo("##materials.technique", current)) + if (EditorUI.current.widgets.BeginBoundedCombo("##materials.technique", current)) { try { @@ -168,20 +186,19 @@ AssetInspectionSelection target } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } - } - } + } + } foreach (ShaderKeywordDefinition keyword in selectionOpen ? firstDefinition.keywords : []) { - if (!materials.Values.All(material => material.shader is { isMissing: false, definition: { } definition } - && definition.keywords.Any(value => value.id == keyword.id && value.options.SequenceEqual(keyword.options)))) + if (!materials.Values.All(material => material.shader is { isMissing: false, definition: { } definition } && definition.keywords.Any(value => value.id == keyword.id && value.options.SequenceEqual(keyword.options)))) continue; string Option(MaterialAsset material) => keyword.options.FirstOrDefault(material.keywords.Contains) ?? "None"; string selected = Option(first); bool mixed = materials.Values.Any(material => Option(material) != selected); - if (!Widget.BeginBoundedCombo(keyword.id, mixed ? "Mixed values" : selected)) + if (!EditorUI.current.widgets.BeginBoundedCombo(keyword.id, mixed ? "Mixed values" : selected)) continue; try { @@ -193,8 +210,9 @@ AssetInspectionSelection target } finally { - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } + void SetOption(string? next) { foreach (MaterialAsset material in materials.Values) @@ -203,37 +221,45 @@ void SetOption(string? next) owner.ReplaceMany(materials); } } - bool parametersOpen = Widget.SectionHeader("Common Parameters", "Only compatible Material-owned inputs appear here. Unrelated overrides are retained per Material."); + + bool parametersOpen = EditorUI.current.widgets.SectionHeader( + "Common Parameters", + "Only compatible Material-owned inputs appear here. Unrelated overrides are retained per Material." + ); bool groupOpen = parametersOpen; string currentGroup = ""; foreach (ShaderPropertyDefinition property in firstDefinition.properties) { if (property.bindingOwner != ShaderPropertyBindingOwner.Material || property.bindingKind is not (ShaderPropertyBindingKind.Uniform or ShaderPropertyBindingKind.SampledTexture)) - continue; + continue; var values = new List(); ShaderParameterPresentation presentation = owner.Presentation(first.shader, property.id, out string presentationError); if (presentationError.Length != 0) - Widget.Hint("Parameter presentation unavailable: " + presentationError); + EditorUI.current.widgets.Hint("Parameter presentation unavailable: " + presentationError); if (!presentation.visible) continue; bool compatible = true; foreach (MaterialAsset material in materials.Values) { ShaderPropertyDefinition[] declarations = material.shader is { isMissing: false } shader ? shader.definition?.properties ?? [] : []; - int index = Array.FindIndex(declarations, value => value.id == property.id && value.type == property.type - && value.bindingKind == property.bindingKind && value.bindingOwner == property.bindingOwner); + int index = Array.FindIndex( + declarations, + value => value.id == property.id && value.type == property.type && value.bindingKind == property.bindingKind && value.bindingOwner == property.bindingOwner + ); if (index < 0) { compatible = false; break; } + ShaderParameterPresentation other = owner.Presentation(material.shader!, property.id, out _); if (!other.visible) { compatible = false; break; } + if (other.hasRange) { if (presentation.hasRange) @@ -253,42 +279,46 @@ void SetOption(string? next) presentation.maximum = other.maximum; } } + MaterialValue value = material.TryGet(property.id, out var overridden) ? overridden : declarations[index].defaultValue; if (!ShaderPropertyInspector.Compatible(property.type, value.kind)) { compatible = false; break; } + values.Add(value); } + if (!compatible) continue; if (presentation.group != currentGroup) { currentGroup = presentation.group; - groupOpen = Widget.SectionHeader(currentGroup.Length == 0 ? "Common Parameters" : currentGroup); + groupOpen = EditorUI.current.widgets.SectionHeader(currentGroup.Length == 0 ? "Common Parameters" : currentGroup); } + if (!groupOpen) continue; bool mixed = values.Skip(1).Any(value => !value.Equals(values[0])); - ImGuiApi.PushID(property.id.value); + ImGuiApi.PushId(property.id.value); try { if (mixed) - Widget.Hint(property.displayName + " · Mixed values (editing applies to all selected Materials)"); - ShaderPropertyInspector.Draw(context, target, "materials." + property.id.value, property, values[0], - value => - { - int selectedIndex = 0; - foreach (MaterialAsset material in materials.Values) - material.Set(property.id, ShaderPropertyInspector.ApplyEdit(property.type, values[0], value, values[selectedIndex++])); - }, edits, readOnly, presentation); + EditorUI.current.widgets.Hint(property.displayName + " · Mixed values (editing applies to all selected Materials)"); + ShaderPropertyInspector.Draw(context, target, "materials." + property.id.value, property, values[0], value => + { + int selectedIndex = 0; + foreach (MaterialAsset material in materials.Values) + material.Set(property.id, ShaderPropertyInspector.ApplyEdit(property.type, values[0], value, values[selectedIndex++])); + }, edits, readOnly, presentation); if (mixed && ImGuiApi.SmallButton("Use First Value for Selection")) { foreach (MaterialAsset material in materials.Values) material.Set(property.id, values[0]); owner.ReplaceMany(materials); } + if (ImGuiApi.SmallButton("Reset Selected Overrides")) { foreach (MaterialAsset material in materials.Values) @@ -298,17 +328,16 @@ void SetOption(string? next) } finally { - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } + if (parametersOpen && materials.Values.Any(value => value.properties.Count != 0) && ImGuiApi.Button("Reset All Selected Overrides")) { foreach (MaterialAsset material in materials.Values) material.ReplaceProperties([]); owner.ReplaceMany(materials); } - if (!ImGuiApi.IsAnyItemActive()) - owner.CommitMany(target.assetIds); void SetTechnique(ShaderTechniqueId technique) { @@ -320,29 +349,70 @@ void SetTechnique(ShaderTechniqueId technique) finally { ImGuiApi.EndDisabled(); - ImGuiApi.PopID(); + ImGuiApi.PopId(); } } - private static bool TryOpenDrafts( + /// + protected override bool CanInspect(EditorSelectionSnapshot target) => target.identities.Count > 1 && GetAssetIds(target).Count == target.identities.Count; + private IReadOnlyList GetAssetIds(EditorSelectionSnapshot target) + { + if (!interactions.TryGetModule(out var materials) || materials is null) + return Array.Empty(); + var ids = new List(target.identities.Count); + var entries = new List(target.identities.Count); + target.CopyTargets(interactions, entries); + if (entries.Count != target.identities.Count) + return Array.Empty(); + entries.Reverse(); + foreach (AssetFileEntry entry in entries) + { + if (entry.isDirectory + || !entry.extension.Equals( + ".imaterial", + StringComparison.OrdinalIgnoreCase + ) + || !materials.assets.TryGetInfo( + entry.assetPath, + out AssetInfo? info + ) + || info is null) + return Array.Empty(); + ids.Add(info.persistentId); + } + + return ids; + } + + private bool TryOpenDrafts( InspectionDrawContext context, - AssetInspectionSelection target, + EditorSelectionSnapshot target, out MaterialDocuments? documents, out List.Draft> drafts ) { drafts = []; if (!context.interactions.TryGetModule(out documents) || documents is null) return false; - foreach (Guid id in target.assetIds) + foreach (Guid id in GetAssetIds(target)) { - if (!documents.assets.TryGetInfo(id, out AssetInfo? info) || info is null - || !documents.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry) - || !entry.extension.Equals(".imaterial", StringComparison.OrdinalIgnoreCase)) + if (!documents.assets.TryGetInfo( + id, + out AssetInfo? info + ) + || info is null + || !documents.assets.TryGetFileSystemEntry( + info.assetPath, + out AssetFileEntry entry + ) + || !entry.extension.Equals( + ".imaterial", + StringComparison.OrdinalIgnoreCase + )) return false; var draft = documents.OpenDraft(info.assetPath); - documents.TouchInspection(draft); drafts.Add(draft); } + return drafts.Count != 0; } @@ -351,32 +421,17 @@ private sealed class SelectionEdits( IReadOnlyDictionary materials ) : IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) { + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) { mutation(); - documents.ReplaceMany(materials, !ImGuiApi.IsAnyItemActive()); + documents.ReplaceMany(materials, mergeKey); return true; } } diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs similarity index 70% rename from src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs index 48a49638c..4b63d7825 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Materials/ShaderPropertyInspector.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Materials/ShaderPropertyInspector.cs @@ -1,11 +1,12 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Shaders; using System; using Inno.Core.Mathematics; using Inno.Editor.Inspection; using Inno.Rendering; using Inno.Rendering.Assets; -namespace Inno.Editor.Shaders; - +namespace Inno.Editor.Shaders.ImGui; /// /// Uses the shared Inspector drawers for both Shader defaults and Material overrides. /// @@ -52,17 +53,31 @@ public static void Draw( bool readOnly, ShaderParameterPresentation? presentation = null ) { - context.properties.DrawValue(context.editorContext, owner, path, property.displayName, ValueType(property.type), - () => Unbox(property.type, value), next => setter(Box(property.type, next, value)), edits, readOnly, - hdrColor: property.type == ShaderPropertyType.Color, tooltip: presentation?.description, + context.properties.DrawValue( + context.editorContext, + owner, + path, + property.displayName, + ValueType(property.type), + () => Unbox(property.type, value), + next => setter(Box(property.type, next, value)), + edits, + readOnly, + hdrColor: property.type == ShaderPropertyType.Color, + tooltip: presentation?.description, minimum: property.type == ShaderPropertyType.Float && presentation?.hasRange == true ? presentation.minimum : null, - maximum: property.type == ShaderPropertyType.Float && presentation?.hasRange == true ? presentation.maximum : null); + maximum: property.type == ShaderPropertyType.Float && presentation?.hasRange == true ? presentation.maximum : null + ); if (property.type == ShaderPropertyType.Color) - Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Hint("Linear RGBA · HDR values are preserved"); + EditorUI.current.widgets.Hint("Linear RGBA · HDR values are preserved"); if (property.bindingKind == ShaderPropertyBindingKind.SampledTexture) - context.properties.DrawValue(context.editorContext, owner, path + ".sampler", "Sampler", typeof(RenderSamplerState), - () => value.sampler, next => { var candidate = value; candidate.kind = MaterialValueKind.Texture; - candidate.sampler = (RenderSamplerState)next!; setter(candidate); }, edits, readOnly); + context.properties.DrawValue(context.editorContext, owner, path + ".sampler", "Sampler", typeof(RenderSamplerState), () => value.sampler, next => + { + var candidate = value; + candidate.kind = MaterialValueKind.Texture; + candidate.sampler = (RenderSamplerState)next!; + setter(candidate); + }, edits, readOnly); } /// @@ -89,7 +104,6 @@ MaterialValueKind kind ShaderPropertyType.Texture2D or ShaderPropertyType.Texture2DArray or ShaderPropertyType.Texture3D or ShaderPropertyType.TextureCube => kind == MaterialValueKind.Texture, _ => false }; - /// /// Applies the edited components of a representative value without replacing unrelated mixed components. /// @@ -126,51 +140,68 @@ MaterialValue target before.sampler.filter == edited.sampler.filter ? target.sampler.filter : edited.sampler.filter, before.sampler.addressU == edited.sampler.addressU ? target.sampler.addressU : edited.sampler.addressU, before.sampler.addressV == edited.sampler.addressV ? target.sampler.addressV : edited.sampler.addressV, - before.sampler.addressW == edited.sampler.addressW ? target.sampler.addressW : edited.sampler.addressW); + before.sampler.addressW == edited.sampler.addressW ? target.sampler.addressW : edited.sampler.addressW + ); } else if (edited.kind == MaterialValueKind.Matrix) { Matrix b = before.matrix, e = edited.matrix, t = target.matrix; target.matrix = new( - Merge(b.m11,e.m11,t.m11), Merge(b.m12,e.m12,t.m12), Merge(b.m13,e.m13,t.m13), Merge(b.m14,e.m14,t.m14), - Merge(b.m21,e.m21,t.m21), Merge(b.m22,e.m22,t.m22), Merge(b.m23,e.m23,t.m23), Merge(b.m24,e.m24,t.m24), - Merge(b.m31,e.m31,t.m31), Merge(b.m32,e.m32,t.m32), Merge(b.m33,e.m33,t.m33), Merge(b.m34,e.m34,t.m34), - Merge(b.m41,e.m41,t.m41), Merge(b.m42,e.m42,t.m42), Merge(b.m43,e.m43,t.m43), Merge(b.m44,e.m44,t.m44)); + Merge(b.m11, e.m11, t.m11), + Merge(b.m12, e.m12, t.m12), + Merge(b.m13, e.m13, t.m13), + Merge(b.m14, e.m14, t.m14), + Merge(b.m21, e.m21, t.m21), + Merge(b.m22, e.m22, t.m22), + Merge(b.m23, e.m23, t.m23), + Merge(b.m24, e.m24, t.m24), + Merge(b.m31, e.m31, t.m31), + Merge(b.m32, e.m32, t.m32), + Merge(b.m33, e.m33, t.m33), + Merge(b.m34, e.m34, t.m34), + Merge(b.m41, e.m41, t.m41), + Merge(b.m42, e.m42, t.m42), + Merge(b.m43, e.m43, t.m43), + Merge(b.m44, e.m44, t.m44) + ); } else { Vector4 b = before.vector, e = edited.vector, t = target.vector; - target.vector = new(Merge(b.x,e.x,t.x), Merge(b.y,e.y,t.y), Merge(b.z,e.z,t.z), Merge(b.w,e.w,t.w)); + target.vector = new(Merge(b.x, e.x, t.x), Merge(b.y, e.y, t.y), Merge(b.z, e.z, t.z), Merge(b.w, e.w, t.w)); } + return target; static float Merge( float previous, float next, float current - ) - => BitConverter.SingleToUInt32Bits(previous) == BitConverter.SingleToUInt32Bits(next) ? current : next; + ) => BitConverter.SingleToUInt32Bits(previous) == BitConverter.SingleToUInt32Bits(next) ? current : next; } private static Type ValueType(ShaderPropertyType type) => type switch { - ShaderPropertyType.Float => typeof(float), ShaderPropertyType.Vector2 => typeof(Vector2), - ShaderPropertyType.Vector3 => typeof(Vector3), ShaderPropertyType.Vector4 => typeof(Vector4), - ShaderPropertyType.Color => typeof(Color), ShaderPropertyType.Matrix4x4 => typeof(Matrix), + ShaderPropertyType.Float => typeof(float), + ShaderPropertyType.Vector2 => typeof(Vector2), + ShaderPropertyType.Vector3 => typeof(Vector3), + ShaderPropertyType.Vector4 => typeof(Vector4), + ShaderPropertyType.Color => typeof(Color), + ShaderPropertyType.Matrix4x4 => typeof(Matrix), ShaderPropertyType.Texture2D or ShaderPropertyType.Texture2DArray or ShaderPropertyType.Texture3D or ShaderPropertyType.TextureCube => typeof(TextureAsset), - _ => throw new InvalidOperationException("No material editor exists for " + type) - }; - + _ => throw new InvalidOperationException("No material editor exists for " + type)}; private static object? Unbox( ShaderPropertyType type, MaterialValue value ) => type switch { - ShaderPropertyType.Float => value.vector.x, ShaderPropertyType.Vector2 => new Vector2(value.vector.x, value.vector.y), - ShaderPropertyType.Vector3 => new Vector3(value.vector.x, value.vector.y, value.vector.z), ShaderPropertyType.Vector4 => value.vector, + ShaderPropertyType.Float => value.vector.x, + ShaderPropertyType.Vector2 => new Vector2(value.vector.x, value.vector.y), + ShaderPropertyType.Vector3 => new Vector3(value.vector.x, value.vector.y, value.vector.z), + ShaderPropertyType.Vector4 => value.vector, ShaderPropertyType.Color => new Color(value.vector.x, value.vector.y, value.vector.z, value.vector.w), - ShaderPropertyType.Matrix4x4 => value.matrix, _ => value.texture + ShaderPropertyType.Matrix4x4 => value.matrix, + _ => value.texture }; - private static MaterialValue Box( ShaderPropertyType type, object? value, @@ -180,8 +211,14 @@ MaterialValue previous ShaderPropertyType.Float => MaterialValue.FromFloat((float)value!), ShaderPropertyType.Vector2 => MaterialValue.FromVector(new Vector4(((Vector2)value!).x, ((Vector2)value!).y, 0, 0)), ShaderPropertyType.Vector3 => MaterialValue.FromVector(new Vector4(((Vector3)value!).x, ((Vector3)value!).y, ((Vector3)value!).z, 0)), - ShaderPropertyType.Vector4 => MaterialValue.FromVector((Vector4)value!), ShaderPropertyType.Color => MaterialValue.FromColor((Color)value!), + ShaderPropertyType.Vector4 => MaterialValue.FromVector((Vector4)value!), + ShaderPropertyType.Color => MaterialValue.FromColor((Color)value!), ShaderPropertyType.Matrix4x4 => MaterialValue.FromMatrix((Matrix)value!), - _ => new MaterialValue { kind = MaterialValueKind.Texture, texture = (TextureAsset?)value, sampler = previous.sampler } + _ => new MaterialValue + { + kind = MaterialValueKind.Texture, + texture = (TextureAsset? )value, + sampler = previous.sampler + } }; } diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs similarity index 82% rename from src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs index ed8108230..ac92c4ee0 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawer.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawer.cs @@ -1,12 +1,11 @@ +using Inno.Editor.Shaders; using System; using Inno.Core.Graphs; using Inno.Core.Serialization; using Inno.Editor.Rendering; using Inno.Editor.Inspection; -using ImGuiApi = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Shaders; +namespace Inno.Editor.Shaders.ImGui; /// /// Registers Editor-only node controls independently from a node's shader compiler. /// @@ -45,6 +44,7 @@ public ShaderNodeDrawerAttribute( this.createOrder = createOrder; this.separatorBefore = separatorBefore; } + /// /// Gets the stable node identity handled by this drawer. /// @@ -103,17 +103,14 @@ bool separatorBefore /// Gets the optional artist-facing node name. /// public string displayName { get; } - /// /// Gets the optional slash-delimited creation-menu path. /// public string createPath { get; } - /// /// Gets the stable ordering value within the creation menu. /// public int createOrder { get; } - /// /// Gets whether the contributed group starts a visual section. /// @@ -142,7 +139,7 @@ public sealed class ShaderNodeDrawContext private readonly GraphNodeRecord m_node; private readonly SerializationRegistry m_serialization; private readonly SerializationContext m_context; - private readonly Action m_write; + private readonly Action m_write; private readonly InspectionDrawContext m_inspection; private readonly bool m_readOnly; /// @@ -173,7 +170,7 @@ public ShaderNodeDrawContext( GraphNodeRecord node, SerializationRegistry serialization, SerializationContext context, - Action write, + Action write, IEditorPreviewService previews, InspectionDrawContext inspection, bool readOnly @@ -191,7 +188,6 @@ bool readOnly /// Gets the shared generation-scoped preview service for optional inline texture previews; valid only during this draw. /// public IEditorPreviewService previews { get; } - /// /// Gets the stable node identity for Editor widget IDs. /// @@ -215,9 +211,7 @@ bool readOnly public T Read( string key, T defaultValue - ) - => Inno.Rendering.Shaders.ShaderGraphDocument.Read(m_node, key, defaultValue, m_serialization, m_context); - + ) => Inno.Rendering.Shaders.ShaderGraphDocument.Read(m_node, key, defaultValue, m_serialization, m_context); /// /// Records an unsaved draft edit through shared history; only an explicit document save applies it to the asset. /// @@ -230,17 +224,17 @@ T defaultValue /// /// New detached value. /// - /// - /// Whether this sample belongs to the active text or numeric gesture. + /// + /// The current shared gesture key, or null for an independent edit. /// public void Write( string key, T value, - bool continuous = false + string? mergeKey = null ) { if (m_readOnly) throw new InvalidOperationException("Copy this installed Shader into the project before editing."); - m_write(key, Inno.Rendering.Shaders.ShaderGraphDocument.Encode(value, m_serialization, m_context), continuous); + m_write(key, Inno.Rendering.Shaders.ShaderGraphDocument.Encode(value, m_serialization, m_context), mergeKey); } /// @@ -264,41 +258,28 @@ public void DrawProperty( T defaultValue ) { T value = Read(key, defaultValue); - m_inspection.properties.DrawValue(m_inspection.editorContext, m_node, "shader.node." + key, label, typeof(T), - () => value, edited => value = (T)edited!, new Edits(mutation => - { - mutation(); - Write(key, value, ImGuiApi.IsAnyItemActive()); - }), m_readOnly); + m_inspection.properties.DrawValue(m_inspection.editorContext, m_node, "shader.node." + key, label, typeof(T), () => value, edited => value = (T)edited!, new Edits(( + mutation, + mergeKey + ) => + { + mutation(); + Write(key, value, mergeKey); + }), m_readOnly); } - private sealed class Edits(Action apply) : IInspectionPropertyEditService + private sealed class Edits(Action apply) : IInspectionPropertyEditService { - /// - /// Applies one serialized property edit and records its reversible history payload. - /// - /// - /// The object that owns the resulting lifetime and state. - /// - /// - /// The property name text validated by the change property operation. - /// - /// - /// The callback invoked by change property within the operation's owned lifetime. - /// - /// - /// The history name text validated by the change property operation. - /// - /// - /// when the documented condition is satisfied; otherwise, . - /// -public bool ChangeProperty( - object owner, - string propertyName, - Action mutation, - string historyName -) { - apply(mutation); + /// + public bool ChangeProperty( + object owner, + string propertyName, + Action mutation, + string historyName, + string? mergeKey, + string? componentName + ) { + apply(mutation, mergeKey); return true; } } diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs similarity index 93% rename from src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs index 7f7e36387..6ffb0b791 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Nodes/ShaderNodeDrawerRegistry.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Nodes/ShaderNodeDrawerRegistry.cs @@ -1,11 +1,11 @@ +using Inno.Editor.Shaders; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Inno.Extensibility.Types; -namespace Inno.Editor.Shaders; - +namespace Inno.Editor.Shaders.ImGui; /// /// Shares generation-scoped node presentation between graph and Inspector hosts. /// @@ -13,7 +13,6 @@ public sealed class ShaderNodeDrawerRegistry : IDisposable { private readonly TypeCatalog m_types; private readonly Registry m_registry; - /// /// Creates a presentation registry owned by the current editor feature lifetime. /// @@ -88,7 +87,6 @@ out ShaderNodePresentation presentation /// Retires the current presentation providers through the shared lifecycle. /// public void Dispose() => m_registry.Dispose(); - private sealed class Registry(TypeCatalog types) : TypeRegistry(types) { internal Snapshot snapshot => current; @@ -102,7 +100,7 @@ private sealed class Registry(TypeCatalog types) : TypeRegistry(types) /// /// The validated snapshot that represents the completed operation. /// -protected override Snapshot Build(TypeCacheSnapshot snapshot) + protected override Snapshot Build(TypeCacheSnapshot snapshot) { var result = new Dictionary(StringComparer.Ordinal); var names = new Dictionary(StringComparer.Ordinal); @@ -119,6 +117,7 @@ protected override Snapshot Build(TypeCacheSnapshot snapshot) names.Add(id, attribute.displayName); presentations.Add(id, new(attribute.displayName, Normalize(attribute.createPath), attribute.createOrder, attribute.separatorBefore)); } + return new(result, names, presentations); } catch (Exception failure) @@ -131,6 +130,7 @@ protected override Snapshot Build(TypeCacheSnapshot snapshot) { throw new AggregateException(failure, retirement); } + throw; } } @@ -141,12 +141,10 @@ protected override Snapshot Build(TypeCacheSnapshot snapshot) /// /// The immutable state snapshot consumed by this operation. /// -protected override void DisposeSnapshot(Snapshot snapshot) => DisposeExtensions(snapshot.drawers.Values); + protected override void DisposeSnapshot(Snapshot snapshot) => DisposeExtensions(snapshot.drawers.Values); } - private static string Normalize(string value) - => string.Join('/', value.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); - + private static string Normalize(string value) => string.Join('/', value.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); private sealed record Snapshot( IReadOnlyDictionary drawers, IReadOnlyDictionary names, diff --git a/src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs similarity index 82% rename from src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs rename to src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs index 2908242ee..286b829e2 100644 --- a/src/composition/editor/features/Inno.Editor.Shaders/Previews/ShaderPreviews.cs +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Previews/ShaderPreviews.cs @@ -1,3 +1,5 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Shaders; using System; using System.Collections.Generic; using System.Linq; @@ -8,12 +10,10 @@ using Inno.Editor.Rendering; using Inno.Extensibility.Types; using Inno.Rendering; -using ImGuiApi = Inno.Native.ImGui.ImGui; -using Widget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; -namespace Inno.Editor.Shaders; - +namespace Inno.Editor.Shaders.ImGui; /// /// Hosts isolated Shader and Material images using domain-contributed preview pipelines. /// @@ -26,7 +26,6 @@ public sealed class ShaderPreviews : EditorModule private readonly EditorShaderCompilation m_compilation; private readonly Dictionary m_states = []; private ulong m_frame; - internal ShaderPreviews( TypeCatalog types, IEditorPreviewService previews, @@ -57,16 +56,17 @@ float logicalSize ) { if (material.shader is null || material.shader.isMissing) { - Widget.Hint("Preview requires an available Shader."); + EditorUI.current.widgets.Hint("Preview requires an available Shader."); return; } + try { Draw(ownerId, material, m_compilation.RequestArtifact(material.shader, RenderShaderVariant.FromMaterial(material)), logicalSize); } catch (Exception error) when (Recoverable(error)) { - Widget.Hint("Preview: " + error.Message); + EditorUI.current.widgets.Hint("Preview: " + error.Message); } } @@ -93,14 +93,16 @@ float logicalSize ) { if (ownerId == Guid.Empty) throw new ArgumentException("A preview owner identity is required.", nameof(ownerId)); - ArgumentNullException.ThrowIfNull(material); ArgumentNullException.ThrowIfNull(compilation); + ArgumentNullException.ThrowIfNull(material); + ArgumentNullException.ThrowIfNull(compilation); if (!(logicalSize > 0f) || !float.IsFinite(logicalSize)) throw new ArgumentOutOfRangeException(nameof(logicalSize)); if (compilation.artifact is not { } artifact) { - Widget.Hint("Preview: " + compilation.state); + EditorUI.current.widgets.Hint("Preview: " + compilation.state); return; } + if (!m_states.TryGetValue(ownerId, out State? state)) m_states.Add(ownerId, state = new("shader-preview/" + ownerId.ToString("N"))); state.frame = m_frame; @@ -108,18 +110,19 @@ float logicalSize { using IDisposable operation = m_types.AcquireOperation("Build isolated Shader preview"); ShaderDefinition definition = m_compilation.ReadDefinition(artifact); - string[] contracts = definition.techniques.Where(value => !material.techniqueId.isValid || value.id == material.techniqueId) - .Select(value => value.contract.value).Distinct(StringComparer.Ordinal).Where(m_registry.providers.ContainsKey).ToArray(); + string[] contracts = definition.techniques.Where(value => !material.techniqueId.isValid || value.id == material.techniqueId).Select(value => value.contract.value).Distinct(StringComparer.Ordinal).Where(m_registry.providers.ContainsKey).ToArray(); if (contracts.Length == 0) { - Widget.Hint("No preview provider for this Shader contract."); + EditorUI.current.widgets.Hint("No preview provider for this Shader contract."); return; } + if (contracts.Length != 1) { - Widget.Hint("Select a Technique to choose an unambiguous preview contract."); + EditorUI.current.widgets.Hint("Select a Technique to choose an unambiguous preview contract."); return; } + int pixels = Math.Clamp((int)MathF.Ceiling(logicalSize * ImGuiApi.GetWindowDpiScale()), 1, 2048); var context = new ShaderPreviewContext(new(state.viewportId), material, artifact, definition, state, pixels, pixels); EditorViewportLayer layer = m_registry.providers[contracts[0]].CreateLayer(context); @@ -127,13 +130,13 @@ float logicalSize m_previews.Draw(handle, new(logicalSize, logicalSize)); else ImGuiApi.Dummy(new(logicalSize, logicalSize)); - Widget.Hint(compilation.usingLastGood ? "Draft preview · last-good candidate · Scene/Game unchanged" : "Isolated preview · Scene/Game unchanged"); + EditorUI.current.widgets.Hint(compilation.usingLastGood ? "Draft preview · last-good candidate · Scene/Game unchanged" : "Isolated preview · Scene/Game unchanged"); foreach (Diagnostic error in state.errors.Values) - Widget.WrappedText(error.message); + EditorUI.current.widgets.WrappedText(error.message); } catch (Exception error) when (Recoverable(error)) { - Widget.Hint("Preview: " + error.Message); + EditorUI.current.widgets.Hint("Preview: " + error.Message); } } @@ -161,6 +164,7 @@ protected override void OnUpdate(EditorContext context) foreach (Guid id in m_states.Where(pair => m_frame - pair.Value.frame > 2).Select(pair => pair.Key).ToArray()) Release(id); } + /// /// Stops this feature before its owning runtime releases the active generation. /// @@ -176,7 +180,6 @@ protected override void OnStop(EditorContext context) private static bool Recoverable(Exception error) => RetirementPendingException.Find(error) is null && error is InvalidOperationException or ArgumentException or FormatException; - private sealed class State(string viewportId) : IDiagnosticReporter { internal readonly string viewportId = viewportId; @@ -188,7 +191,7 @@ private sealed class State(string viewportId) : IDiagnosticReporter /// /// The diagnostic consumed by publish; ownership remains with the caller unless explicitly stated otherwise. /// -public void Publish(Diagnostic diagnostic) => errors[(diagnostic.code, diagnostic.semanticId, diagnostic.objectId)] = diagnostic; + public void Publish(Diagnostic diagnostic) => errors[(diagnostic.code, diagnostic.semanticId, diagnostic.objectId)] = diagnostic; /// /// Resolves the requested stable identity to its current-generation value. /// @@ -201,28 +204,29 @@ private sealed class State(string viewportId) : IDiagnosticReporter /// /// The object id consumed by resolve; ownership remains with the caller unless explicitly stated otherwise. /// -public void Resolve( - string code, - string? semanticId = null, - Guid? objectId = null -) => errors.Remove((code, semanticId, objectId)); + public void Resolve( + string code, + string? semanticId = null, + Guid? objectId = null + ) => errors.Remove((code, semanticId, objectId)); /// /// Records errors from the current diagnostic report. /// /// /// The diagnostics consumed by replace; ownership remains with the caller unless explicitly stated otherwise. /// -public void Replace(IEnumerable diagnostics) -{ - errors.Clear(); - foreach (Diagnostic value in diagnostics) - Publish(value); -} + public void Replace(IEnumerable diagnostics) + { + errors.Clear(); + foreach (Diagnostic value in diagnostics) + Publish(value); + } } private sealed class Registry(TypeCatalog types) : TypeRegistry>(types) { internal IReadOnlyDictionary providers => current; + /// /// Builds a validated result from the current immutable input snapshot. /// @@ -232,7 +236,7 @@ private sealed class Registry(TypeCatalog types) : TypeRegistry /// An immutable snapshot of the values selected by the operation. /// -protected override IReadOnlyDictionary Build(TypeCacheSnapshot snapshot) + protected override IReadOnlyDictionary Build(TypeCacheSnapshot snapshot) { var result = new Dictionary(StringComparer.Ordinal); try @@ -244,6 +248,7 @@ protected override IReadOnlyDictionary Build(Type throw new InvalidOperationException("Duplicate Shader preview contract: " + id); result.Add(id, CreateExtension(type)); } + return result; } catch (Exception failure) @@ -256,15 +261,17 @@ protected override IReadOnlyDictionary Build(Type { throw new AggregateException(failure, retirement); } + throw; } } + /// /// Releases the generation lease retained by an immutable registry snapshot. /// /// /// The immutable state snapshot consumed by this operation. /// -protected override void DisposeSnapshot(IReadOnlyDictionary snapshot) => DisposeExtensions(snapshot.Values); + protected override void DisposeSnapshot(IReadOnlyDictionary snapshot) => DisposeExtensions(snapshot.Values); } } diff --git a/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Properties/ScriptingApi.cs new file mode 100644 index 000000000..a5f010ab8 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Shaders.ImGui/Properties/ScriptingApi.cs @@ -0,0 +1,9 @@ +using Inno.Editor.Shaders.ImGui; +using Inno.Scripting.Api; + +[assembly: ScriptingApiNamespace("InnoEditor.Shaders", "Inno.Editor.Shaders.ImGui", ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ShaderNodeDrawerAttribute), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ShaderNodeDrawer), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ShaderNodeDrawContext), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ShaderPropertyInspector), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ShaderPreviews), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/HistoryActions.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/HistoryActions.cs similarity index 74% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/HistoryActions.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Actions/HistoryActions.cs index 1e328542b..5202c7bc9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/HistoryActions.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/HistoryActions.cs @@ -1,8 +1,7 @@ using Inno.Core.Input; using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; [EditorAction(GlobalInteractionIds.C_UNDO, priority: 1000)] [EditorMenu(GlobalInteractionIds.C_MAIN_MENU_AREA, "Edit/Undo", order: 100)] [EditorShortcut(KeyCode.Z, primary: true)] @@ -17,16 +16,12 @@ internal sealed class UndoEditorAction : EditorAction /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => new( - isVisible: true, - isEnabled: context.history.canUndo, - displayName: context.history.undoName is { } name - ? context.history.undoUnavailableReason is { } reason - ? $"Undo {name} ({reason})" - : $"Undo {name}" - : "Undo"); - + protected override EditorActionState Query(EditorActionContext context) => new( + isVisible: true, + isEnabled: context.history.canUndo, + displayName: context.history.undoName is { } name ? $"Undo {name}" : "Undo", + unavailableReason: context.history.undoUnavailableReason + ); /// /// Executes the prepared operation and publishes only a completed result. /// @@ -51,16 +46,12 @@ internal sealed class RedoEditorAction : EditorAction /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => new( - isVisible: true, - isEnabled: context.history.canRedo, - displayName: context.history.redoName is { } name - ? context.history.redoUnavailableReason is { } reason - ? $"Redo {name} ({reason})" - : $"Redo {name}" - : "Redo"); - + protected override EditorActionState Query(EditorActionContext context) => new( + isVisible: true, + isEnabled: context.history.canRedo, + displayName: context.history.redoName is { } name ? $"Redo {name}" : "Redo", + unavailableReason: context.history.redoUnavailableReason + ); /// /// Executes the prepared operation and publishes only a completed result. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/SelectionActions.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/SelectionActions.cs similarity index 96% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/SelectionActions.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Actions/SelectionActions.cs index 9984397cb..e5a24c756 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/SelectionActions.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/SelectionActions.cs @@ -1,7 +1,6 @@ using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; [EditorAction(GlobalInteractionIds.C_CLEAR_SELECTION)] internal sealed class ClearEditorSelectionAction : EditorAction { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/TogglePanelAction.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/TogglePanelAction.cs similarity index 62% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/TogglePanelAction.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Actions/TogglePanelAction.cs index d17e47d38..14ab346e4 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Actions/TogglePanelAction.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Actions/TogglePanelAction.cs @@ -1,10 +1,18 @@ using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; [EditorAction(GlobalInteractionIds.C_TOGGLE_PANEL, GlobalInteractionIds.C_MAIN_MENU_AREA)] internal sealed class TogglePanelAction : EditorArgumentAction { + /// + protected override EditorActionState Query(EditorActionArgumentContext context) => context.interactions.TryGetPanelState( + context.argument, + out bool isOpen + ) ? new EditorActionState( + true, + true, + isChecked: isOpen + ) : EditorActionState.hidden; /// /// Executes the prepared operation and publishes only a completed result. /// diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchAction.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchAction.cs new file mode 100644 index 000000000..c38394789 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchAction.cs @@ -0,0 +1,14 @@ +using Inno.Core.Input; +using Inno.Editor.Interactions; + +namespace Inno.Editor.Workbench; +[EditorAction(GlobalInteractionIds.C_FIND_COMMAND)] +[EditorMenu(GlobalInteractionIds.C_MAIN_MENU_AREA, "Tools/Find Command...", order: 0)] +[EditorShortcut(KeyCode.P, KeyModifier.Shift, primary: true)] +internal sealed class CommandSearchAction(CommandSearchModule commands) : EditorAction +{ + /// + protected override EditorActionState Query(EditorActionContext context) => commands.isOpen ? EditorActionState.disabled : EditorActionState.enabled; + /// + protected override void Execute(EditorActionContext context) => commands.Open(); +} diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModal.cs new file mode 100644 index 000000000..7bb0bc72d --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModal.cs @@ -0,0 +1,47 @@ +using System.Numerics; +using Inno.Editor.Core; +using Inno.Editor.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Workbench; +[EditorModal(GlobalInteractionIds.C_COMMAND_SEARCH_MODAL, "Find Command", order: 50)] +internal sealed class CommandSearchModal( + CommandSearchModule commands, + EditorUiSession ui +) : EditorModal +{ + /// + public override bool isVisible => commands.isOpen; + /// + public override bool allowScrolling => false; + /// + public override Vector2 initialSize => new(620f, 440f); + + /// + protected override void OnDraw(EditorContext context) + { + if (commands.requestsFocus) + { + Ui.SetKeyboardFocusHere(); + commands.requestsFocus = false; + } + + string search = commands.search; + _ = ui.widgets.SearchInput("commands", "Search commands...", ref search); + commands.search = search; + bool opened = Ui.BeginChild("##command-results", new Vector2(0f, -Ui.GetFrameHeightWithSpacing()), 0, ImGuiWindowFlags.NoSavedSettings); + bool chosen = false; + try + { + if (opened) + chosen = commands.Draw(ui); + } + finally + { + Ui.EndChild(); + } + + if (chosen || ui.widgets.CenteredButton("Close") || Ui.IsKeyPressed(ImGuiKey.Escape, false)) + commands.Close(); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModule.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModule.cs new file mode 100644 index 000000000..3f093368c --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Commands/CommandSearchModule.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Core.Identity; +using Inno.Editor.Core; +using Inno.Editor.ImGui; +using Inno.Editor.Interactions; + +namespace Inno.Editor.Workbench; +[EditorModule(GlobalInteractionIds.C_COMMAND_SEARCH_MODULE, order: 20)] +internal sealed class CommandSearchModule(EditorInteractions interactions) : EditorModule +{ + private string m_area = "editor/global"; + private RuntimeIdentity? m_target; + internal bool isOpen { get; private set; } + internal string search { get; set; } = string.Empty; + internal bool requestsFocus { get; set; } + + internal void Open() + { + m_area = interactions.focusedArea; + m_target = (interactions.focusedTarget as IdentityObject)?.identity.runtimeIdentity; + search = string.Empty; + requestsFocus = true; + isOpen = true; + } + + internal void Close() + { + isOpen = false; + m_target = null; + search = string.Empty; + } + + internal bool Draw(EditorUiSession ui) + { + EditorInteraction global = interactions.For(GlobalInteractionIds.C_MAIN_MENU_AREA); + EditorMenuModel globalMenu = global.BuildMenu(); + var globalIds = new HashSet(StringComparer.Ordinal); + Collect(globalMenu.items); + if (ui.menus.DrawSearchItems(global, globalMenu.items, search)) + return true; + object? target = m_target is RuntimeIdentity id && interactions.TryResolveIdentity(id, out IdentityObject? live) ? live : null; + EditorInteraction local = interactions.For(m_area, target); + EditorMenuModel commands = local.BuildCommands(); + var localItems = Leaves(commands.items).Where(item => !globalIds.Contains(item.actionId)).ToArray(); + return ui.menus.DrawSearchItems(local, localItems, search); + void Collect(IReadOnlyList items) + { + foreach (EditorMenuItem item in items) + { + if (item.actionId.Length != 0) + _ = globalIds.Add(item.actionId); + Collect(item.children); + } + } + } + + /// + protected override void OnStop(EditorContext context) => Close(); + private static IEnumerable Leaves(IReadOnlyList items) + { + foreach (EditorMenuItem item in items) + { + if (item.children.Count == 0) + yield return item; + else + { + foreach (EditorMenuItem child in Leaves(item.children)) + yield return child; + } + } + } +} diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Interactions/ExportActions.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportActions.cs similarity index 86% rename from src/composition/editor/features/Inno.Editor.Exporting/Interactions/ExportActions.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportActions.cs index e61113bd3..75dcf1557 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Interactions/ExportActions.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportActions.cs @@ -1,13 +1,8 @@ using Inno.Editor.Interactions; -namespace Inno.Editor.Exporting; - +namespace Inno.Editor.Workbench; [EditorAction(ExportInteractionIds.C_EXPORT_PLUGIN)] -[EditorMenu( - ExportInteractionIds.C_MAIN_MENU_AREA, - "File/Export as Plugin...", - order: 800, - separatorBefore: true)] +[EditorMenu(ExportInteractionIds.C_MAIN_MENU_AREA, "File/Export as Plugin...", order: 800, separatorBefore: true)] internal sealed class ExportPluginAction(ExportWindowModule window) : EditorAction { /// diff --git a/src/composition/editor/features/Inno.Editor.Exporting/ExportInteractionIds.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportInteractionIds.cs similarity index 87% rename from src/composition/editor/features/Inno.Editor.Exporting/ExportInteractionIds.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportInteractionIds.cs index 56f6d8015..e27e00e2b 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/ExportInteractionIds.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportInteractionIds.cs @@ -1,5 +1,4 @@ -namespace Inno.Editor.Exporting; - +namespace Inno.Editor.Workbench; internal static class ExportInteractionIds { internal const string C_EXPORT_GAME = "export/game"; diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportWindowModule.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportWindowModule.cs new file mode 100644 index 000000000..f83f8353f --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/ExportWindowModule.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Inno.Build; +using Inno.Build.Managed; +using Inno.Core.Settings; +using Inno.Editor.Core; +using Inno.Editor.Exporting; + +namespace Inno.Editor.Workbench; +[EditorModule("export.windows", order: 310)] +internal sealed class ExportWindowModule( + EditorContext m_editor, + EditorExportService m_exports, + BuildSettingsStore m_buildSettings, + ProjectSettingsStore m_projectSettings +) : EditorModule +{ + private bool m_gameProgressVisible; + private string m_formError = string.Empty; + internal bool isPluginVisible { get; private set; } + internal bool isGameVisible { get; private set; } + internal bool isGameProgressVisible => m_gameProgressVisible && m_exports.isGameBusy; + internal bool isPluginBusy => m_exports.isPluginBusy; + internal bool isGameBusy => m_exports.isGameBusy; + internal string pluginId => m_projectSettings.projectId.value; + internal string pluginDisplayName { get; set; } = string.Empty; + internal string pluginOutputPath { get; set; } = string.Empty; + internal string gameApplicationId => m_projectSettings.projectId.value; + internal string gameProductName { get; set; } = string.Empty; + internal string gamePersistentDataPath { get; set; } = string.Empty; + internal string gameStartupScene { get; set; } = string.Empty; + internal string gameOutputDirectory { get; set; } = string.Empty; + internal int gameWindowWidth { get; set; } = 1280; + internal int gameWindowHeight { get; set; } = 720; + internal BuildTargetId gameTarget { get; set; } + internal ManagedDeploymentId? gameManagedDeployment { get; set; } + internal IReadOnlyList availableManagedDeployments => m_exports.GetManagedDeployments(gameTarget); + internal IReadOnlyList availableGameTargets => m_exports.availableGameTargets; + internal string status => m_exports.status; + internal string error => m_formError.Length == 0 ? m_exports.error : m_formError; + internal float gameProgress => m_exports.gameProgress; + internal bool includePluginDependencies { get; set; } + + internal string GetGameTargetDisplayName(BuildTargetId target) => m_exports.GetGameTargetDisplayName(target); + internal void OpenPlugin() + { + if (isPluginBusy || isGameBusy) + return; + CloseGame(); + BuildSettings defaults = LoadBuildSettings(); + pluginDisplayName = defaults.pluginDisplayName; + pluginOutputPath = defaults.pluginOutputPath; + includePluginDependencies = defaults.includePluginDependencies; + isPluginVisible = true; + } + + internal void OpenGame() + { + if (isPluginBusy || isGameBusy) + return; + ClosePlugin(); + m_gameProgressVisible = false; + BuildSettings defaults = LoadBuildSettings(); + gameProductName = defaults.gameProductName; + gamePersistentDataPath = defaults.gamePersistentDataPath; + gameStartupScene = defaults.gameStartupScene; + gameOutputDirectory = defaults.gameOutputDirectory; + gameWindowWidth = defaults.gameWindowWidth; + gameWindowHeight = defaults.gameWindowHeight; + gameTarget = defaults.gameTarget; + gameManagedDeployment = defaults.gameManagedDeployment; + isGameVisible = true; + } + + internal void BeginPluginExport() + { + m_formError = string.Empty; + m_exports.StartPlugin(new PluginBuildRequest { pluginId = pluginId, displayName = pluginDisplayName, outputPath = ResolveOutputPath(pluginOutputPath), includeDependencies = includePluginDependencies }); + } + + internal void BeginGameExport() + { + m_formError = string.Empty; + m_exports.StartGame(new GameBuildRequest { profile = new BuildProfile { applicationId = gameApplicationId, productName = gameProductName, persistentDataPath = gamePersistentDataPath, startupScene = gameStartupScene, target = gameTarget, managedDeployment = gameManagedDeployment, windowWidth = gameWindowWidth, windowHeight = gameWindowHeight }, outputDirectory = ResolveOutputPath(gameOutputDirectory) }); + isGameVisible = false; + m_gameProgressVisible = true; + } + + internal void CancelPluginExport() => m_exports.Cancel(); + internal void CancelGameExport() => m_exports.Cancel(); + internal void ClosePlugin() + { + if (isPluginBusy) + m_exports.Cancel(); + isPluginVisible = false; + } + + internal void CloseGame() + { + if (isGameBusy) + m_exports.Cancel(); + isGameVisible = false; + } + + private BuildSettings LoadBuildSettings() + { + try + { + return m_buildSettings.Load(); + } + catch (Exception exception) when (exception is IOException or InvalidDataException or InvalidOperationException or UnauthorizedAccessException) + { + m_formError = exception.Message; + return m_buildSettings.defaultSettings; + } + } + + private string ResolveOutputPath(string path) + { + if (string.IsNullOrWhiteSpace(path)) + return path; + return Path.IsPathFullyQualified(path) ? Path.GetFullPath(path) : Path.GetFullPath(path, m_editor.projectDirectory); + } +} diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs similarity index 56% rename from src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportModal.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs index 4b11fc484..4d5ccbfc0 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs @@ -1,41 +1,35 @@ +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; using Inno.Build; using Inno.Build.Managed; using Inno.Editor.Core; using Inno.Editor.ImGui; -using Inno.Native.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Exporting; +using Ui = Inno.Editor.ImGui.ImGui; +namespace Inno.Editor.Workbench; [EditorModal("export.game", "Export as Game", order: 310)] -internal sealed unsafe class GameExportModal(ExportWindowModule window) : EditorModal +internal sealed class GameExportModal(ExportWindowModule window) : EditorModal { private const int C_TEXT_CAPACITY = 4096; - /// /// Gets whether this implementation is visible. /// public override bool isVisible => window.isGameVisible; - /// /// Gets whether this implementation can move. /// public override bool canMove => true; - /// /// Gets whether this implementation can resize. /// public override bool canResize => true; - /// /// Gets the preferred initial window size in logical editor units. /// public override Vector2 initialSize => new(760f, 540f); - /// /// Gets the smallest size that keeps the export form usable. /// @@ -49,62 +43,62 @@ internal sealed unsafe class GameExportModal(ExportWindowModule window) : Editor /// protected override void OnDraw(EditorContext context) { - EditorWidget.WrappedText( - "Build a standalone Player from the current project. Only imported runtime content " + - "is deployed; source files stay in the project."); - NativeImGui.SeparatorText("Application"); + EditorUI.current.widgets.WrappedText("Build a standalone Player from the current project. Only imported runtime content " + "is deployed; source files stay in the project."); + Ui.SeparatorText("Application"); if (BeginFields("##application_fields")) { DrawReadOnly("Application ID", window.gameApplicationId); DrawText("Product Name", "game_name", window.gameProductName, value => window.gameProductName = value); - DrawText("Persistent Data Folder", "game_data", window.gamePersistentDataPath, - value => window.gamePersistentDataPath = value); + DrawText("Persistent Data Folder", "game_data", window.gamePersistentDataPath, value => window.gamePersistentDataPath = value); DrawText("Startup Scene", "game_scene", window.gameStartupScene, value => window.gameStartupScene = value); - NativeImGui.EndTable(); + Ui.EndTable(); } - NativeImGui.SeparatorText("Window and Platform"); + + Ui.SeparatorText("Window and Platform"); if (BeginFields("##platform_fields")) { DrawWindowSize(); DrawTarget(); DrawManagedDeployment(); - NativeImGui.EndTable(); + Ui.EndTable(); } - NativeImGui.SeparatorText("Output"); + + Ui.SeparatorText("Output"); if (BeginFields("##output_fields")) { DrawText("Output Directory", "game_output", window.gameOutputDirectory, value => window.gameOutputDirectory = value); - NativeImGui.EndTable(); + Ui.EndTable(); } - NativeImGui.Spacing(); - EditorWidget.WrappedText("Defaults: Settings > Build > Game. Application ID: Settings > Project > Identity."); + + Ui.Spacing(); + EditorUI.current.widgets.WrappedText("Defaults: Settings > Build > Game. Application ID: Settings > Project > Identity."); if (!string.IsNullOrEmpty(window.error)) { - NativeImGui.Spacing(); - EditorWidget.WrappedText(window.error); + Ui.Spacing(); + EditorUI.current.widgets.WrappedText(window.error); } - NativeImGui.Separator(); + + Ui.Separator(); DrawButtons(); } private static bool BeginFields(string id) { - if (!NativeImGui.BeginTable(id, 2, (int)(ImGuiTableFlags.SizingStretchProp | - ImGuiTableFlags.NoSavedSettings | - ImGuiTableFlags.NoPadOuterX), default(System.Numerics.Vector2), 0f)) + if (!Ui.BeginTable(id, 2, (ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.NoPadOuterX), default(System.Numerics.Vector2), 0f)) { return false; } - EditorWidget.SetupPropertyColumns(); + + EditorUI.current.widgets.SetupPropertyColumns(); return true; } private static void BeginField(string label) { - NativeImGui.TableNextRow(); - _ = NativeImGui.TableSetColumnIndex(0); - EditorWidget.PropertyLabel(label); - _ = NativeImGui.TableSetColumnIndex(1); + Ui.TableNextRow(); + _ = Ui.TableSetColumnIndex(0); + EditorUI.current.widgets.PropertyLabel(label); + _ = Ui.TableSetColumnIndex(1); } private static void DrawReadOnly( @@ -112,14 +106,14 @@ private static void DrawReadOnly( string value ) { BeginField(label); - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.textDisabled); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.textDisabled); try { - EditorWidget.WrappedText(value); + EditorUI.current.widgets.WrappedText(value); } finally { - NativeImGui.PopStyleColor(); + Ui.PopStyleColor(); } } @@ -128,41 +122,41 @@ private void DrawWindowSize() int width = window.gameWindowWidth; int height = window.gameWindowHeight; BeginField("Initial Width"); - NativeImGui.SetNextItemWidth(-1f); - if (NativeImGui.InputInt("##game_window_width", ref width, 1, 100, 0)) + Ui.SetNextItemWidth(-1f); + if (Ui.InputInt("##game_window_width", ref width, 1, 100, 0)) window.gameWindowWidth = Math.Max(1, width); BeginField("Initial Height"); - NativeImGui.SetNextItemWidth(-1f); - if (NativeImGui.InputInt("##game_window_height", ref height, 1, 100, 0)) + Ui.SetNextItemWidth(-1f); + if (Ui.InputInt("##game_window_height", ref height, 1, 100, 0)) window.gameWindowHeight = Math.Max(1, height); } private void DrawManagedDeployment() { BeginField("Managed Deployment"); - NativeImGui.SetNextItemWidth(-1f); - if (!EditorWidget.BeginBoundedCombo("##game_deployment", window.gameManagedDeployment?.value ?? "Platform default")) + Ui.SetNextItemWidth(-1f); + if (!EditorUI.current.widgets.BeginBoundedCombo("##game_deployment", window.gameManagedDeployment?.value ?? "Platform default")) return; try { - if (NativeImGui.Selectable("Platform default", window.gameManagedDeployment is null)) + if (Ui.Selectable("Platform default", window.gameManagedDeployment is null)) window.gameManagedDeployment = null; foreach (ManagedDeploymentId deployment in window.availableManagedDeployments) - if (NativeImGui.Selectable(deployment.value, window.gameManagedDeployment == deployment)) + if (Ui.Selectable(deployment.value, window.gameManagedDeployment == deployment)) window.gameManagedDeployment = deployment; } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } private void DrawTarget() { BeginField("Target"); - NativeImGui.SetNextItemWidth(-1f); + Ui.SetNextItemWidth(-1f); string preview = window.GetGameTargetDisplayName(window.gameTarget); - if (!EditorWidget.BeginBoundedCombo("##game_target", preview)) + if (!EditorUI.current.widgets.BeginBoundedCombo("##game_target", preview)) return; try { @@ -171,33 +165,31 @@ private void DrawTarget() } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } private void DrawTargetChoice(BuildTargetId target) { bool selected = window.gameTarget == target; - if (NativeImGui.Selectable(window.GetGameTargetDisplayName(target), selected)) + if (Ui.Selectable(window.GetGameTargetDisplayName(target), selected)) window.gameTarget = target; if (selected) - NativeImGui.SetItemDefaultFocus(); + Ui.SetItemDefaultFocus(); } private void DrawButtons() { - NativeImGui.Spacing(); + Ui.Spacing(); string primaryLabel = "Export"; - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float closeWidth = NativeImGui.CalcTextSize("Close").X + style->FramePadding.X * 2f; - float primaryWidth = NativeImGui.CalcTextSize(primaryLabel).X + style->FramePadding.X * 2f; - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + - MathF.Max(0f, NativeImGui.GetContentRegionAvail().X - closeWidth - style->ItemSpacing.X - primaryWidth)); - if (NativeImGui.Button("Close")) + EditorUiLayout style = UiDrawing.GetLayout(); + float closeWidth = Ui.MeasureText("Close").X + style.framePadding.X * 2f; + float primaryWidth = Ui.MeasureText(primaryLabel).X + style.framePadding.X * 2f; + Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - closeWidth - style.itemSpacing.X - primaryWidth)); + if (Ui.Button("Close")) window.CloseGame(); - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.Button(primaryLabel)) + Ui.SameLine(0f, -1f); + if (Ui.Button(primaryLabel)) window.BeginGameExport(); } @@ -208,7 +200,7 @@ private static void DrawText( Action apply ) { BeginField(label); - NativeImGui.SetNextItemWidth(-1f); + Ui.SetNextItemWidth(-1f); _ = EditorImGui.InputText($"##{id}", ref value, C_TEXT_CAPACITY); apply(value); } diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportProgressModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportProgressModal.cs similarity index 70% rename from src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportProgressModal.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportProgressModal.cs index c5c640339..2ad8270f7 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/GameExportProgressModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportProgressModal.cs @@ -1,10 +1,8 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Numerics; - using Inno.Editor.Core; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; - -namespace Inno.Editor.Exporting; +namespace Inno.Editor.Workbench; [EditorModal("export.game.progress", "Exporting Game", order: 311)] internal sealed class GameExportProgressModal(ExportWindowModule window) : EditorModal { @@ -12,7 +10,6 @@ internal sealed class GameExportProgressModal(ExportWindowModule window) : Edito /// Gets whether the export progress overlay should be displayed. /// public override bool isVisible => window.isGameProgressVisible; - /// /// Gets whether export progress prevents interaction with the Editor beneath it. /// @@ -27,13 +24,9 @@ internal sealed class GameExportProgressModal(ExportWindowModule window) : Edito protected override void OnDraw(EditorContext context) { string message = string.IsNullOrEmpty(window.error) ? window.status : window.error; - EditorWidget.WrappedText(message); - EditorWidget.CenteredProgressBar( - window.gameProgress, - new Vector2(-1f, 0f), - $"{window.gameProgress:P0}"); - if (!window.isGameBusy || - !EditorWidget.CenteredButton("Cancel", EditorWidget.style.itemSpacing.Y)) + EditorUI.current.widgets.WrappedText(message); + EditorUI.current.widgets.CenteredProgressBar(window.gameProgress, new Vector2(-1f, 0f), $"{window.gameProgress:P0}"); + if (!window.isGameBusy || !EditorUI.current.widgets.CenteredButton("Cancel", EditorUI.current.widgets.style.itemSpacing.Y)) return; window.CancelGameExport(); } diff --git a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/PluginExportModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs similarity index 53% rename from src/composition/editor/features/Inno.Editor.Exporting/Presentation/PluginExportModal.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs index 1d0464cce..a426c2d01 100644 --- a/src/composition/editor/features/Inno.Editor.Exporting/Presentation/PluginExportModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs @@ -1,29 +1,26 @@ +using Inno.Editor.ImGui; +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; -using Inno.Native.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Exporting; +using Ui = Inno.Editor.ImGui.ImGui; +namespace Inno.Editor.Workbench; [EditorModal("export.plugin", "Export as Plugin", order: 300)] -internal sealed unsafe class PluginExportModal(ExportWindowModule window) : EditorModal +internal sealed class PluginExportModal(ExportWindowModule window) : EditorModal { private const int C_TEXT_CAPACITY = 4096; - /// /// Gets whether this implementation is visible. /// public override bool isVisible => window.isPluginVisible; - /// /// Gets whether this implementation can move. /// public override bool canMove => true; - /// /// Gets the preferred initial window size in logical editor units. /// @@ -37,26 +34,28 @@ internal sealed unsafe class PluginExportModal(ExportWindowModule window) : Edit /// protected override void OnDraw(EditorContext context) { - EditorWidget.WrappedText( - "Packages the current project's complete authored source. Plugin.inno, dependency declarations, " + - "settings contributions, and deterministic metadata are generated automatically."); - NativeImGui.Spacing(); - NativeImGui.BeginDisabled(window.isPluginBusy); + EditorUI.current.widgets.WrappedText("Packages the current project's complete authored source. Plugin.inno, dependency declarations, " + "settings contributions, and deterministic metadata are generated automatically."); + Ui.Spacing(); + Ui.BeginDisabled(window.isPluginBusy); try { - NativeImGui.TextUnformatted("Plugin ID"); - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, $"{window.pluginId} (from Project ID)"); + Ui.Text("Plugin ID"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, $"{window.pluginId} (from Project ID)"); DrawText("Display Name", "plugin_name", window.pluginDisplayName, value => window.pluginDisplayName = value); DrawText("Destination IPlugin", "plugin_output", window.pluginOutputPath, value => window.pluginOutputPath = value); bool includeDependencies = window.includePluginDependencies; - if (NativeImGui.Checkbox("Include dependency Plugin packages", ref includeDependencies)) + if (Ui.Checkbox("Include dependency Plugin packages", ref includeDependencies)) window.includePluginDependencies = includeDependencies; } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Identity comes from Settings > Project > Identity. Other defaults come from Build > Plugin."); + + EditorUI.current.widgets.ColoredText( + Inno.Editor.ImGui.EditorPalette.textDisabled, + "Identity comes from Settings > Project > Identity. Other defaults come from Build > Plugin." + ); DrawOutcome(); DrawButtons(); } @@ -65,38 +64,37 @@ private void DrawOutcome() { if (!string.IsNullOrEmpty(window.error)) { - NativeImGui.Spacing(); - EditorWidget.WrappedText(window.error); + Ui.Spacing(); + EditorUI.current.widgets.WrappedText(window.error); } else if (!string.IsNullOrEmpty(window.status)) { - NativeImGui.Spacing(); - EditorWidget.WrappedText(window.status); + Ui.Spacing(); + EditorUI.current.widgets.WrappedText(window.status); } } private void DrawButtons() { - NativeImGui.Spacing(); - ImGuiStyle* style = NativeImGui.Raw.GetStyle(); - float closeWidth = NativeImGui.CalcTextSize("Close").X + style->FramePadding.X * 2f; + Ui.Spacing(); + EditorUiLayout style = UiDrawing.GetLayout(); + float closeWidth = Ui.MeasureText("Close").X + style.framePadding.X * 2f; string primaryLabel = window.isPluginBusy ? "Cancel" : "Export"; - float exportWidth = NativeImGui.CalcTextSize(primaryLabel).X + style->FramePadding.X * 2f; - NativeImGui.SetCursorPosX( - NativeImGui.GetCursorPosX() + - MathF.Max(0f, NativeImGui.GetContentRegionAvail().X - closeWidth - style->ItemSpacing.X - exportWidth)); - NativeImGui.BeginDisabled(window.isPluginBusy); + float exportWidth = Ui.MeasureText(primaryLabel).X + style.framePadding.X * 2f; + Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - closeWidth - style.itemSpacing.X - exportWidth)); + Ui.BeginDisabled(window.isPluginBusy); try { - if (NativeImGui.Button("Close")) + if (Ui.Button("Close")) window.ClosePlugin(); } finally { - NativeImGui.EndDisabled(); + Ui.EndDisabled(); } - NativeImGui.SameLine(0f, -1f); - if (NativeImGui.Button(primaryLabel)) + + Ui.SameLine(0f, -1f); + if (Ui.Button(primaryLabel)) { if (window.isPluginBusy) window.CancelPluginExport(); @@ -111,8 +109,8 @@ private static void DrawText( string value, Action apply ) { - NativeImGui.TextUnformatted(label); - NativeImGui.SetNextItemWidth(-1f); + Ui.Text(label); + Ui.SetNextItemWidth(-1f); if (EditorImGui.InputText($"##{id}", ref value, C_TEXT_CAPACITY)) apply(value); else diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/GlobalInteractionIds.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/GlobalInteractionIds.cs similarity index 69% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/GlobalInteractionIds.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/GlobalInteractionIds.cs index c4e0d8327..2e2ba8575 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/GlobalInteractionIds.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/GlobalInteractionIds.cs @@ -1,8 +1,10 @@ -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; internal static class GlobalInteractionIds { internal const string C_MAIN_MENU_AREA = "editor/main-menu"; + internal const string C_FIND_COMMAND = "editor/find-command"; + internal const string C_COMMAND_SEARCH_MODULE = "editor.command-search"; + internal const string C_COMMAND_SEARCH_MODAL = "editor.command-search.dialog"; internal const string C_CLEAR_SELECTION = "editor/clear-selection"; internal const string C_REDO = "editor/redo"; internal const string C_SELECT = "editor/select"; diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj b/src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj new file mode 100644 index 000000000..d52a5c701 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Inno.Editor.Workbench.csproj @@ -0,0 +1,28 @@ + + + net10.0 + 14.0 + disable + enable + true + $(WarningsAsErrors);CS1572;CS1573;CS1591 + Editor + + + + + + + + + + + + + + + + + + + diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Menus/WorkbenchMenuSource.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Menus/WorkbenchMenuSource.cs new file mode 100644 index 000000000..adad5c98e --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Menus/WorkbenchMenuSource.cs @@ -0,0 +1,26 @@ +using Inno.Editor.Core; +using Inno.Editor.Interactions; + +namespace Inno.Editor.Workbench; +[EditorMenuSource(GlobalInteractionIds.C_MAIN_MENU_AREA)] +internal sealed class WorkbenchMenuSource : EditorMenuSource +{ + /// + public override void Build( + EditorMenuContext context, + EditorMenuBuilder builder + ) { + builder.AddGroup("File", order: 0); + builder.AddGroup("Edit", order: 100); + builder.AddGroup("Create", order: 200); + builder.AddGroup("View", order: 300); + builder.AddGroup("Window", order: 400); + builder.AddGroup("Tools", order: 500); + builder.AddGroup("Help", order: 600); + foreach (EditorPanelAttribute panel in context.interactions.GetPanelDeclarations()) + { + string path = string.IsNullOrEmpty(panel.menuPath) ? "Window/" + panel.title : "Window/" + panel.menuPath + "/" + panel.title; + builder.Add(path, GlobalInteractionIds.C_TOGGLE_PANEL, panel.order, panel.separatorBefore, panel.id); + } + } +} diff --git a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Scripting/ScriptCompilationModal.cs similarity index 68% rename from src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationModal.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Scripting/ScriptCompilationModal.cs index 938c3182e..300be3c26 100644 --- a/src/composition/editor/features/Inno.Editor.Scripting/Runtime/ScriptCompilationModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Scripting/ScriptCompilationModal.cs @@ -1,10 +1,9 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Inno.Editor.Scripting; using System.Numerics; - using Inno.Editor.Core; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; - -namespace Inno.Editor.Scripting; +namespace Inno.Editor.Workbench; /// /// Displays blocking script compiler and reload progress with cancellation controls. /// @@ -12,13 +11,12 @@ namespace Inno.Editor.Scripting; /// The scripting used to initialize this instance. /// [EditorModal("scripting.compilation", "Compiling Scripts", order: 100)] -internal sealed class ScriptCompilationModal(EditorScripting scripting) : EditorModal +internal sealed class ScriptCompilationModal(IEditorScriptCompilation scripting) : EditorModal { /// /// Gets whether this implementation is visible. /// public override bool isVisible => scripting.requiresExclusiveProgress; - /// /// Gets whether blocks interaction is enabled for this implementation. /// @@ -33,9 +31,9 @@ internal sealed class ScriptCompilationModal(EditorScripting scripting) : Editor protected override void OnDraw(EditorContext context) { float progress = scripting.progress; - EditorWidget.WrappedText(scripting.status); - EditorWidget.CenteredProgressBar(progress, new Vector2(-1f, 0f), $"{progress:P0}"); - if (EditorWidget.CenteredButton("Cancel", EditorWidget.style.itemSpacing.Y)) + EditorUI.current.widgets.WrappedText(scripting.status); + EditorUI.current.widgets.CenteredProgressBar(progress, new Vector2(-1f, 0f), $"{progress:P0}"); + if (EditorUI.current.widgets.CenteredButton("Cancel", EditorUI.current.widgets.style.itemSpacing.Y)) scripting.CancelCompilation(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/DensitySetting.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/DensitySetting.cs similarity index 78% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/DensitySetting.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Settings/DensitySetting.cs index 3f4e8f807..b9eac6bc9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/DensitySetting.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/DensitySetting.cs @@ -1,9 +1,9 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Panel.Global; +using Ui = Inno.Editor.ImGui.ImGui; +namespace Inno.Editor.Workbench; [EditorSettingPath("Editor/Appearance/Density", order: 50)] internal sealed class DensitySetting : EditorSetting { @@ -11,12 +11,10 @@ internal sealed class DensitySetting : EditorSetting /// Gets the comfortable default density. /// public override EditorSettingObject defaultValue => CreateDefault(); - /// /// Gets the appearance section containing this setting. /// public override string section => "Layout"; - /// /// Gets the user-facing density description. /// @@ -32,19 +30,19 @@ protected override void OnDraw(EditorSettingObject setting) { bool compact = setting.GetAsBoolean("compact", false); string preview = compact ? "Compact" : "Comfortable"; - NativeImGui.SetNextItemWidth(-1f); - if (!ImGuiWidget.BeginBoundedCombo("##editor_density", preview)) + Ui.SetNextItemWidth(-1f); + if (!EditorUI.current.widgets.BeginBoundedCombo("##editor_density", preview)) return; try { - if (NativeImGui.Selectable("Comfortable", !compact)) + if (Ui.Selectable("Comfortable", !compact)) setting.SetAsBoolean("compact", false); - if (NativeImGui.Selectable("Compact", compact)) + if (Ui.Selectable("Compact", compact)) setting.SetAsBoolean("compact", true); } finally { - ImGuiWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/FramePacingSettings.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/FramePacingSettings.cs similarity index 85% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/FramePacingSettings.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Settings/FramePacingSettings.cs index 19675d070..57b0598db 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/FramePacingSettings.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/FramePacingSettings.cs @@ -1,12 +1,11 @@ -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using Inno.Editor.Core; using Inno.Editor.Settings; using Inno.Platform; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Panel.Global; +using Ui = Inno.Editor.ImGui.ImGui; +namespace Inno.Editor.Workbench; [EditorSettingPath("Editor/Rendering")] internal sealed class EditorRenderingSettingsPage : EditorSetting { @@ -31,6 +30,7 @@ public override EditorSettingObject defaultValue return result; } } + /// /// Gets the presentation section that groups this setting. /// @@ -39,6 +39,7 @@ public override EditorSettingObject defaultValue /// Gets the user-facing explanation of this feature or setting. /// public override string description => "Synchronize presentation to the display refresh rate. Disable to allow higher frame rates."; + /// /// Draws this feature using the current editor presentation context. /// @@ -48,7 +49,7 @@ public override EditorSettingObject defaultValue protected override void OnDraw(EditorSettingObject setting) { bool enabled = setting.GetAsBoolean("enabled", false); - if (NativeImGui.Checkbox("##vsync", ref enabled)) + if (Ui.Checkbox("##vsync", ref enabled)) setting.SetAsBoolean("enabled", enabled); } } @@ -68,6 +69,7 @@ public override EditorSettingObject defaultValue return result; } } + /// /// Gets the presentation section that groups this setting. /// @@ -76,6 +78,7 @@ public override EditorSettingObject defaultValue /// Gets the user-facing explanation of this feature or setting. /// public override string description => "Software frame-rate limit. Zero means Unlimited. Fixed-step simulation is unaffected."; + /// /// Draws this feature using the current editor presentation context. /// @@ -85,10 +88,10 @@ public override EditorSettingObject defaultValue protected override void OnDraw(EditorSettingObject setting) { int rate = setting.GetAsInt32("value", 0); - if (NativeImGui.InputInt("##maximum_fps", ref rate, 1, 100, 0)) + if (Ui.InputInt("##maximum_fps", ref rate, 1, 100, 0)) setting.SetAsInt32("value", Math.Clamp(rate, 0, 1000)); if (rate == 0) - EditorWidget.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Unlimited"); + EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Unlimited"); } } @@ -98,6 +101,7 @@ internal sealed class EditorFramePacingModule( FramePacingOptions pacing ) : EditorModule { + private IDisposable? m_settingsSubscription; /// /// Initializes this feature when its owning runtime becomes active. /// @@ -107,20 +111,19 @@ FramePacingOptions pacing protected override void OnStart(EditorContext context) { Apply(settings); - settings.changed += Apply; + m_settingsSubscription = settings.ListenChanges(Apply); } + /// /// Stops this feature before its owning runtime releases the active generation. /// /// /// The context that supplies state and services for this operation. /// - protected override void OnStop(EditorContext context) => settings.changed -= Apply; - + protected override void OnStop(EditorContext context) => m_settingsSubscription?.Dispose(); private void Apply(EditorSettings current) { pacing.verticalSync = current.Get("Editor/Rendering/Vertical Sync").GetAsBoolean("enabled", false); - pacing.maximumFrameRate = Math.Clamp( - current.Get("Editor/Rendering/Maximum Frame Rate").GetAsInt32("value", 0), 0, 1000); + pacing.maximumFrameRate = Math.Clamp(current.Get("Editor/Rendering/Maximum Frame Rate").GetAsInt32("value", 0), 0, 1000); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/GlobalSettingsPages.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/GlobalSettingsPages.cs similarity index 96% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/GlobalSettingsPages.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Settings/GlobalSettingsPages.cs index 353a3395e..401cdb0f1 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Settings/GlobalSettingsPages.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/GlobalSettingsPages.cs @@ -1,7 +1,6 @@ using Inno.Editor.Settings; -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; [EditorSettingPath("Editor")] internal sealed class GlobalSettingsPage : EditorSetting { diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs new file mode 100644 index 000000000..bf981b2e1 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs @@ -0,0 +1,218 @@ +using Inno.Editor.ImGui; +using UiDrawing = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Inno.Editor.ImGui.ImGuiWidget; +using Inno.Editor.Settings; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Workbench; +internal abstract class IconSetting : EditorSetting +{ + private static readonly IReadOnlyList<(string Name, string Glyph)> C_ICONS = EditorIconCatalog.standard; + private readonly string m_glyph; + /// + /// Creates a validated icon setting instance. + /// + /// + /// The glyph text validated by the icon setting operation. + /// + protected IconSetting(string glyph) + { + ArgumentException.ThrowIfNullOrWhiteSpace(glyph); + m_glyph = glyph; + } + + /// + /// Gets a new value initialized to this setting's canonical default state. + /// + public override EditorSettingObject defaultValue => CreateDefault(m_glyph); + /// + /// Gets the presentation section that groups this setting. + /// + public override string section => "Editor Icons"; + + /// + /// Renders this feature using the current editor presentation context. + /// + /// + /// The mutable editor setting value currently being presented. + /// + protected override void OnDraw(EditorSettingObject setting) + { + string value = setting.GetAsString("value", m_glyph) ?? m_glyph; + float iconSlotWidth = GetIconSlotWidth(); + Ui.SetNextItemWidth(-1f); + bool isOpen = EditorUI.current.widgets.BeginBoundedCombo("##icon", value + " " + GetIconName(value)); + if (isOpen) + { + try + { + for (int i = 0; i < C_ICONS.Count; i++) + { + (string name, string candidate) = C_ICONS[i]; + Ui.PushId(name); + try + { + bool selected = string.Equals(candidate, value, StringComparison.Ordinal); + if (Ui.Selectable("##icon_option", selected, ImGuiSelectableFlags.None, new Vector2(0f, Ui.GetFrameHeight()))) + { + value = candidate; + setting.SetAsString("value", candidate); + } + + Vector2 minimum = Ui.GetItemMinimum(); + Vector2 maximum = Ui.GetItemMaximum(); + EditorUiLayout style = UiDrawing.GetLayout(); + float lineHeight = Ui.GetTextLineHeight(); + DrawIconTextAt( + Ui.GetDrawList(), + new Vector2(minimum.X + style.framePadding.X, minimum.Y + MathF.Max(0f, (maximum.Y - minimum.Y - lineHeight) * 0.5f)), + candidate, + name, + iconSlotWidth + ); + if (selected) + Ui.SetItemDefaultFocus(); + } + finally + { + Ui.PopId(); + } + } + } + finally + { + EditorUI.current.widgets.EndBoundedCombo(); + } + } + } + + private static EditorSettingObject CreateDefault(string glyph) + { + ArgumentException.ThrowIfNullOrWhiteSpace(glyph); + var result = new EditorSettingObject(); + result.SetAsString("value", glyph); + return result; + } + + private static float GetIconSlotWidth() => EditorUI.current.widgets.GetIconCatalogWidth(); + private static string GetIconName(string glyph) + { + for (int i = 0; i < C_ICONS.Count; i++) + { + if (string.Equals(C_ICONS[i].Glyph, glyph, StringComparison.Ordinal)) + return C_ICONS[i].Name; + } + + return glyph; + } + + private static void DrawIconTextAt( + EditorDrawList drawList, + Vector2 origin, + string glyph, + string text, + float iconSlotWidth + ) { + EditorUiLayout style = UiDrawing.GetLayout(); + float fontSize = Ui.GetFontSize(); + float lineHeight = Ui.GetTextLineHeight(); + uint color = Ui.GetStyleColor(ImGuiCol.Text); + EditorUI.current.widgets.AddGlyphCentered(fontSize, glyph, new Vector2(origin.X + iconSlotWidth * 0.5f, origin.Y + lineHeight * 0.5f), color); + Inno.Editor.ImGui.ImGui.DrawText(new Vector2(origin.X + iconSlotWidth + style.itemInnerSpacing.X, origin.Y), color, text); + } +} + +[EditorSettingPath("Editor/Appearance/Icons/Scene")] +internal sealed class SceneIconSetting() : IconSetting(ImGuiIcon.Cubes) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a scene document or loaded scene."; +} + +[EditorSettingPath("Editor/Appearance/Icons/GameObject")] +internal sealed class GameObjectIconSetting() : IconSetting(ImGuiIcon.Cube) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a scene GameObject."; +} + +[EditorSettingPath("Editor/Appearance/Icons/Prefab")] +internal sealed class PrefabIconSetting() : IconSetting(ImGuiIcon.DiceD6) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a reusable prefab object."; +} + +[EditorSettingPath("Editor/Appearance/Icons/Layers")] +internal sealed class LayersIconSetting() : IconSetting(ImGuiIcon.LayerGroup) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a project layer configuration."; +} + +[EditorSettingPath("Editor/Appearance/Icons/Folder")] +internal sealed class FolderIconSetting() : IconSetting(ImGuiIcon.Folder) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a source directory."; +} + +[EditorSettingPath("Editor/Appearance/Icons/File")] +internal sealed class FileIconSetting() : IconSetting(ImGuiIcon.File) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used wherever the editor presents a generic source file."; +} + +[EditorSettingPath("Editor/Appearance/Icons/Shader")] +internal sealed class ShaderIconSetting() : IconSetting(ImGuiIcon.Shader) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used consistently for Shader graph assets and their Inspector headers."; +} + +[EditorSettingPath("Editor/Appearance/Icons/ShaderSource")] +internal sealed class ShaderSourceIconSetting() : IconSetting(ImGuiIcon.RupeeSign) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used consistently for Shader source-function libraries. The default function symbol distinguishes reusable code from complete Shader graphs."; +} + +[EditorSettingPath("Editor/Appearance/Icons/Material")] +internal sealed class MaterialIconSetting() : IconSetting(ImGuiIcon.Palette) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used consistently for Material assets and their Inspector headers."; +} + +[EditorSettingPath("Editor/Appearance/Icons/RenderPipeline")] +internal sealed class RenderPipelineIconSetting() : IconSetting(ImGuiIcon.RupiahSign) +{ + /// + /// Gets the user-facing explanation of this feature or setting. + /// + public override string description => "Selects the icon used consistently for Render Pipeline assets and their Inspector headers."; +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ActualSizeSetting.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ActualSizeSetting.cs similarity index 77% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ActualSizeSetting.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ActualSizeSetting.cs index 823ed43fd..7b46f0185 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ActualSizeSetting.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ActualSizeSetting.cs @@ -1,36 +1,23 @@ +using Inno.Editor.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; - using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; -using Inno.Native.ImGui; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; -using NativeImGui = Inno.Native.ImGui.ImGui; - -namespace Inno.Editor.Panel.Global; +using Ui = Inno.Editor.ImGui.ImGui; +namespace Inno.Editor.Workbench; [EditorSettingPath("Editor/Appearance/Accessibility/Actual Size")] internal sealed class ActualSizeSetting : EditorSetting { - private static readonly (float Value, string Label)[] C_CHOICES = - [ - (0.75f, "75%"), - (0.9f, "90%"), - (1f, "100%"), - (1.1f, "110%"), - (1.25f, "125%"), - (1.5f, "150%") - ]; - + private static readonly (float Value, string Label)[] C_CHOICES = [(0.75f, "75%"), (0.9f, "90%"), (1f, "100%"), (1.1f, "110%"), (1.25f, "125%"), (1.5f, "150%")]; /// /// Gets a new value initialized to this setting's canonical default state. /// public override EditorSettingObject defaultValue => CreateDefault(); - /// /// Gets the presentation section that groups this setting. /// public override string section => "Font"; - /// /// Gets the user-facing explanation of this feature or setting. /// @@ -46,21 +33,21 @@ protected override void OnDraw(EditorSettingObject setting) { float value = setting.GetAsSingle("value", 1f); string preview = GetLabel(value); - NativeImGui.SetNextItemWidth(-1f); - if (!EditorWidget.BeginBoundedCombo("##actual_size", preview)) + Ui.SetNextItemWidth(-1f); + if (!EditorUI.current.widgets.BeginBoundedCombo("##actual_size", preview)) return; try { for (int i = 0; i < C_CHOICES.Length; i++) { (float candidate, string label) = C_CHOICES[i]; - if (NativeImGui.Selectable(label, MathF.Abs(candidate - value) < 0.0001f)) + if (Ui.Selectable(label, MathF.Abs(candidate - value) < 0.0001f)) setting.SetAsSingle("value", candidate); } } finally { - EditorWidget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } } @@ -78,6 +65,7 @@ private static string GetLabel(float value) if (MathF.Abs(C_CHOICES[i].Value - value) < 0.0001f) return C_CHOICES[i].Label; } + return $"{value * 100f:0}%"; } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/EditorZoomModule.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs similarity index 70% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/EditorZoomModule.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs index 8daf7affd..1d589689e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/EditorZoomModule.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs @@ -1,41 +1,37 @@ using System; - using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.Settings; -using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; - -namespace Inno.Editor.Panel.Global; +namespace Inno.Editor.Workbench; /// /// Applies the configured actual UI size and preserves project-local zoom multiples around it. /// +/// +/// The host-owned presentation session receiving the selected scale. +/// /// /// The validated configuration that controls this operation. /// [EditorModule("editor-zoom", order: 10)] -internal sealed class EditorZoomModule(EditorSettings settings) : EditorModule +internal sealed class EditorZoomModule( + EditorSettings settings, + EditorUiSession ui +) : EditorModule { + private IDisposable? m_settingsSubscription; private const string C_ACTUAL_SIZE_PATH = "Editor/Appearance/Accessibility/Actual Size"; private const string C_DENSITY_PATH = "Editor/Appearance/Density"; - private float m_actualSize = 1f; private int m_zoomStep; - - internal float zoom => EditorWidget.style.zoom; - + internal float zoom => ui.widgets.style.zoom; internal bool canZoomIn => ResolveZoom(m_zoomStep + 1) > zoom + 0.0001f; - internal bool canZoomOut => ResolveZoom(m_zoomStep - 1) < zoom - 0.0001f; - internal bool isActualSize => m_zoomStep == 0; internal bool ZoomIn() => SetStep(m_zoomStep + 1); - internal bool ZoomOut() => SetStep(m_zoomStep - 1); - internal bool UseActualSize() => SetStep(0); - /// /// Initializes this feature when its owning runtime becomes active. /// @@ -45,7 +41,7 @@ internal sealed class EditorZoomModule(EditorSettings settings) : EditorModule protected override void OnStart(EditorContext context) { ApplyActualSize(settings); - settings.changed += ApplyActualSize; + m_settingsSubscription = settings.ListenChanges(ApplyActualSize); } /// @@ -56,7 +52,7 @@ protected override void OnStart(EditorContext context) /// protected override void OnStop(EditorContext context) { - settings.changed -= ApplyActualSize; + m_settingsSubscription?.Dispose(); } /// @@ -79,25 +75,20 @@ protected override void Capture(EditorState state) protected override void Restore(EditorState state) { m_zoomStep = Math.Clamp(state.Get("step", 0), -10, 10); - _ = EditorWidget.style.SetZoom(ResolveZoom(m_zoomStep)); + _ = ui.widgets.style.SetZoom(ResolveZoom(m_zoomStep)); } private void ApplyActualSize(EditorSettings changedSettings) { - m_actualSize = NormalizeActualSize( - changedSettings.Get(C_ACTUAL_SIZE_PATH).GetAsSingle("value", 1f)); - _ = EditorWidget.style.SetZoom(ResolveZoom(m_zoomStep)); - _ = EditorWidget.style.SetCompactMode( - changedSettings.Get(C_DENSITY_PATH).GetAsBoolean("compact", false)); + m_actualSize = NormalizeActualSize(changedSettings.Get(C_ACTUAL_SIZE_PATH).GetAsSingle("value", 1f)); + _ = ui.widgets.style.SetZoom(ResolveZoom(m_zoomStep)); + _ = ui.widgets.style.SetCompactMode(changedSettings.Get(C_DENSITY_PATH).GetAsBoolean("compact", false)); } private float ResolveZoom(int step) { float multiplier = 1f + step * EditorStyleMetrics.C_ZOOM_STEP; - return Math.Clamp( - m_actualSize * multiplier, - EditorStyleMetrics.C_MIN_ZOOM, - EditorStyleMetrics.C_MAX_ZOOM); + return Math.Clamp(m_actualSize * multiplier, EditorStyleMetrics.C_MIN_ZOOM, EditorStyleMetrics.C_MAX_ZOOM); } private bool SetStep(int step) @@ -106,11 +97,8 @@ private bool SetStep(int step) if (MathF.Abs(resolved - zoom) < 0.0001f) return false; m_zoomStep = step; - return EditorWidget.style.SetZoom(resolved); + return ui.widgets.style.SetZoom(resolved); } - private static float NormalizeActualSize(float value) - => float.IsFinite(value) - ? Math.Clamp(value, EditorStyleMetrics.C_MIN_ZOOM, EditorStyleMetrics.C_MAX_ZOOM) - : 1f; + private static float NormalizeActualSize(float value) => float.IsFinite(value) ? Math.Clamp(value, EditorStyleMetrics.C_MIN_ZOOM, EditorStyleMetrics.C_MAX_ZOOM) : 1f; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ZoomActions.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ZoomActions.cs similarity index 89% rename from src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ZoomActions.cs rename to src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ZoomActions.cs index ac611353f..8b9e31b64 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Global/Zoom/ZoomActions.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/ZoomActions.cs @@ -1,8 +1,7 @@ using Inno.Core.Input; using Inno.Editor.Interactions; -namespace Inno.Editor.Panel.Global; - +namespace Inno.Editor.Workbench; /// /// Increases UI zoom by one multiple of the configured actual size. /// @@ -23,11 +22,7 @@ internal sealed class ZoomInEditorAction(EditorZoomModule zoom) : EditorAction /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => zoom.canZoomIn - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => zoom.canZoomIn ? EditorActionState.enabled : EditorActionState.disabled; /// /// Applies the editor action to the supplied interaction context. /// @@ -57,11 +52,7 @@ internal sealed class ZoomOutEditorAction(EditorZoomModule zoom) : EditorAction /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => zoom.canZoomOut - ? EditorActionState.enabled - : EditorActionState.disabled; - + protected override EditorActionState Query(EditorActionContext context) => zoom.canZoomOut ? EditorActionState.enabled : EditorActionState.disabled; /// /// Applies the editor action to the supplied interaction context. /// @@ -91,11 +82,7 @@ internal sealed class ActualSizeEditorAction(EditorZoomModule zoom) : EditorActi /// /// The validated editor action state that represents the completed operation. /// - protected override EditorActionState Query(EditorActionContext context) - => zoom.isActualSize - ? EditorActionState.disabled - : EditorActionState.enabled; - + protected override EditorActionState Query(EditorActionContext context) => zoom.isActualSize ? EditorActionState.disabled : EditorActionState.enabled; /// /// Applies the editor action to the supplied interaction context. /// diff --git a/tests/Invoke-EditorSmoke.ps1 b/tests/Invoke-EditorSmoke.ps1 index 94bccb94d..6266b7052 100644 --- a/tests/Invoke-EditorSmoke.ps1 +++ b/tests/Invoke-EditorSmoke.ps1 @@ -2,6 +2,7 @@ param( [Parameter(Mandatory = $true)][string]$EditorAssembly, [Parameter(Mandatory = $true)][string]$ProjectDirectory, [Parameter(Mandatory = $true)][string]$BackendPattern, + [string]$GraphicsApi, [string]$Dotnet = 'dotnet', [int]$Frames = 3, [int]$TimeoutSeconds = 180 @@ -30,6 +31,10 @@ $start.WorkingDirectory = $ProjectDirectory foreach ($argument in @($EditorAssembly, $ProjectDirectory, '--smoke-frames', [string]$Frames)) { $start.ArgumentList.Add($argument) } +if ($GraphicsApi) { + $start.ArgumentList.Add('--graphics-api') + $start.ArgumentList.Add($GraphicsApi) +} $process = [Diagnostics.Process]::new() $process.StartInfo = $start $copies = [Threading.CancellationTokenSource]::new() diff --git a/tests/Invoke-IsolatedTests.ps1 b/tests/Invoke-IsolatedTests.ps1 index 288979140..bbedacead 100644 --- a/tests/Invoke-IsolatedTests.ps1 +++ b/tests/Invoke-IsolatedTests.ps1 @@ -14,8 +14,9 @@ $Dotnet = (Get-Command $Dotnet -ErrorAction Stop).Source $sdkDirectory = Split-Path $Dotnet -Parent $sdkEnvironment = @('--environment', "DOTNET_ROOT=$sdkDirectory", '--environment', "PATH=$sdkDirectory$([IO.Path]::PathSeparator)$env:PATH") -# Project-mode dotnet test supplies DOTNET_HOST_PATH itself; assembly-mode VSTest needs the explicit host. -$assemblyEnvironment = @($sdkEnvironment) + @('--environment', "DOTNET_HOST_PATH=$Dotnet") +# Assembly-mode VSTest derives DOTNET_ROOT from the explicit host; passing both duplicates its environment key. +$assemblyEnvironment = @('--environment', "DOTNET_HOST_PATH=$Dotnet", + '--environment', "PATH=$sdkDirectory$([IO.Path]::PathSeparator)$env:PATH") $repositoryRoot = Split-Path $PSScriptRoot -Parent $solutionPath = [System.IO.Path]::GetFullPath($Solution, $repositoryRoot) @@ -100,7 +101,9 @@ foreach ($project in $projects) { $assembly = $targetPath[0] $listed = @(& $Dotnet test $assembly --list-tests --filter $isolatedFilter @assemblyEnvironment 2>&1) $discoveryExit = $LASTEXITCODE - if ($discoveryExit -ne 0) { + $listed | Set-Content -LiteralPath (Join-Path $resultsRoot ($project.BaseName + '.discovery.log')) -Encoding utf8 + $discoveryErrors = @($listed | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) + if ($discoveryExit -ne 0 -or $discoveryErrors.Count -ne 0) { Add-CheckResult 'Discovery' $project.FullName 'Failed' $discoveryExit ($listed -join [Environment]::NewLine) continue } diff --git a/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs new file mode 100644 index 000000000..6d24cb67c --- /dev/null +++ b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs @@ -0,0 +1,41 @@ +using System; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using Inno.Editor.Graph.ImGui; +using Xunit; + +namespace Inno.Editor.Graph.Tests; +public sealed class GraphCanvasViewTests +{ + [Theory] + [InlineData(0.1f)] + [InlineData(1f)] + [InlineData(4f)] + public void ViewportRoundTripPreservesTheGraphPoint(float zoom) + { + var state = new GraphCanvasState(); + state.SetViewport(new(-172f, 38f), zoom); + var view = new GraphCanvasView(state, new(100f, 200f), new(800f, 600f)); + GraphPosition point = new(-21.25f, 90f); + GraphPosition actual = view.ToGraph(view.ToScreen(point)); + Assert.Equal(point.x, actual.x, 3); + Assert.Equal(point.y, actual.y, 3); + } + + [Fact] + public void ConnectionHitTestUsesTheDisplayedRouteIncludingEndpoints() + { + Assert.True(GraphCanvasView.HitConnection(new(0f, 0f), Vector2.Zero, new(200f, 100f))); + Assert.True(GraphCanvasView.HitConnection(new(100f, 50f), Vector2.Zero, new(200f, 100f))); + Assert.False(GraphCanvasView.HitConnection(new(100f, 120f), Vector2.Zero, new(200f, 100f))); + Assert.True(GraphCanvasView.HitConnection(new(200f, 100f), Vector2.Zero, new(200f, 100f))); + } + + [Fact] + public void InvalidViewportAndHitToleranceAreRejected() + { + Assert.Throws(() => new GraphCanvasView(new GraphCanvasState(), Vector2.Zero, new(float.NaN, 100f))); + Assert.Throws(() => GraphCanvasView.HitConnection(Vector2.Zero, Vector2.Zero, Vector2.One, -1f)); + } +} diff --git a/tests/editor/Inno.Editor.Graph.Tests/Inno.Editor.Graph.Tests.csproj b/tests/editor/Inno.Editor.Graph.Tests/Inno.Editor.Graph.Tests.csproj index ec0f714d7..cff03d55f 100644 --- a/tests/editor/Inno.Editor.Graph.Tests/Inno.Editor.Graph.Tests.csproj +++ b/tests/editor/Inno.Editor.Graph.Tests/Inno.Editor.Graph.Tests.csproj @@ -10,6 +10,7 @@ + @@ -27,7 +28,6 @@ - diff --git a/tests/editor/Inno.Editor.ImGui.Tests/EditorUiSessionTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/EditorUiSessionTests.cs new file mode 100644 index 000000000..1e30dca5e --- /dev/null +++ b/tests/editor/Inno.Editor.ImGui.Tests/EditorUiSessionTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading; +using Inno.Editor.ImGui; +using Xunit; + +namespace Inno.Editor.ImGui.Tests; +public sealed class EditorUiSessionTests +{ + [Fact] + public void StyleAndControlsBelongToOneSession() + { + using var first = new EditorUiSession(); + using var second = new EditorUiSession(); + first.widgets.style.SetZoom(1.5f); + second.widgets.style.SetCompactMode(true); + Assert.NotSame(first.widgets, second.widgets); + Assert.Equal(1.5f, first.widgets.style.zoom); + Assert.Equal(1f, second.widgets.style.zoom); + Assert.False(first.widgets.style.isCompact); + Assert.True(second.widgets.style.isCompact); + } + + [Fact] + public void RejectedScopeRetirementPreservesTheActiveSession() + { + using var first = new EditorUiSession(); + using var second = new EditorUiSession(); + IDisposable outer = first.Enter(); + IDisposable inner = second.Enter(); + Assert.Throws(outer.Dispose); + Assert.Same(second, EditorUiSession.current); + Assert.Throws(first.Dispose); + inner.Dispose(); + Assert.Same(first, EditorUiSession.current); + outer.Dispose(); + Assert.Throws(() => EditorUiSession.current); + } + + [Fact] + public void RetiredSessionsRejectActivationAndControlUse() + { + var session = new EditorUiSession(); + session.Dispose(); + Assert.True(session.isDisposed); + Assert.Throws(() => session.Enter()); + Assert.Throws(() => session.widgets.CenteredButton("Retired")); + } + + [Fact] + public void ASessionCannotBeActivatedFromAnotherThread() + { + using var session = new EditorUiSession(); + Exception? failure = null; + var thread = new Thread(() => failure = Record.Exception(() => session.Enter())); + thread.Start(); + Assert.True(thread.Join(TimeSpan.FromSeconds(5))); + Assert.IsType(failure); + } +} diff --git a/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj b/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj index 99a0bbd0b..ca09bb5c3 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj +++ b/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj @@ -12,7 +12,7 @@ Inno.Editor.ImGui.Tests.NativeWindowTestHost Editor editor - windows-x64 + $(InnoNativeTarget) @@ -35,5 +35,4 @@ - diff --git a/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs index 4c8c0bb4a..1bc7f7d67 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs @@ -1,3 +1,4 @@ +using ImGuiWindowFlags = Inno.Editor.ImGui.ImGuiWindowFlags; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; @@ -15,9 +16,7 @@ using NativeImGui = Inno.Native.ImGui.ImGui; [assembly: CollectionBehavior(DisableTestParallelization = true)] - namespace Inno.Editor.ImGui.Tests; - public sealed unsafe class ViewportMetricsTests { [Theory] @@ -27,14 +26,12 @@ public void FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback(bool comm { if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback) + "." + commandKeys)) return; - using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); - using var window = application.CreateWindow(new PlatformWindowOptions - { title = "Viewport metrics", width = 320, height = 240, visible = false, highPixelDensity = true }); - using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, - new ImGuiInteractionOptions(commandKeys, 1), new FixtureRenderer()); + using var window = application.CreateWindow(new PlatformWindowOptions { title = "Viewport metrics", width = 320, height = 240, visible = false, highPixelDensity = true }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, new ImGuiInteractionOptions(commandKeys, 1), new FixtureRenderer()); context.SetIniFile(null); - context.RenderFrame(() => { + context.RenderFrame(() => + { Assert.Equal(commandKeys, (NativeImGui.Raw.GetIO()->ConfigMacOsxBehaviors != 0)); Assert.True(NativeImGui.Raw.GetPlatformIO()->PlatformGetWindowFramebufferScale == default); int logicalWidth = 0; @@ -45,8 +42,10 @@ public void FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback(bool comm SDL.Raw.GetWindowSizeInPixels((SDLWindow*)window.sdlWindowHandle, &pixelWidth, &pixelHeight); Vector2 expected = new(pixelWidth / (float)logicalWidth, pixelHeight / (float)logicalHeight); Assert.Equal(expected, Unsafe.BitCast(NativeImGui.Raw.GetMainViewport()->FramebufferScale)); - Assert.Equal(SDL.Raw.GetWindowDisplayScale((SDLWindow*)window.sdlWindowHandle) - / SDL.Raw.GetWindowPixelDensity((SDLWindow*)window.sdlWindowHandle), NativeImGui.Raw.GetMainViewport()->DpiScale); + Assert.Equal( + SDL.Raw.GetWindowDisplayScale((SDLWindow*)window.sdlWindowHandle) / SDL.Raw.GetWindowPixelDensity((SDLWindow*)window.sdlWindowHandle), + NativeImGui.Raw.GetMainViewport()->DpiScale + ); Assert.Equal(new Vector2(logicalWidth, logicalHeight), Unsafe.BitCast(NativeImGui.Raw.GetIO()->DisplaySize)); }); SDL.Raw.SetWindowSize((SDLWindow*)window.sdlWindowHandle, 450, 300); @@ -58,14 +57,16 @@ public void MetricRefreshPreservesRequestedViewportGeometry() { if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(MetricRefreshPreservesRequestedViewportGeometry))) return; - using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); - using var window = application.CreateWindow(new PlatformWindowOptions - { title = "Viewport geometry owner", width = 320, height = 240, visible = false }); - using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, - new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); + using var window = application.CreateWindow(new PlatformWindowOptions { title = "Viewport geometry owner", width = 320, height = 240, visible = false }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); context.SetIniFile(null); - var viewport = new ImGuiViewport { ID = 4218, Size = Unsafe.BitCast(new Vector2(100, 90)), Pos = Unsafe.BitCast(new Vector2(30, 30)) }; + var viewport = new ImGuiViewport + { + ID = 4218, + Size = Unsafe.BitCast(new Vector2(100, 90)), + Pos = Unsafe.BitCast(new Vector2(30, 30)) + }; var io = NativeImGui.Raw.GetPlatformIO(); io->PlatformCreateWindow(&viewport); try @@ -75,7 +76,9 @@ public void MetricRefreshPreservesRequestedViewportGeometry() Vector2 requestedSize = new(140, 110); viewport.Pos = viewport.WorkPos = Unsafe.BitCast(requestedPosition); viewport.Size = viewport.WorkSize = Unsafe.BitCast(requestedSize); - context.RenderFrame(() => { }); + context.RenderFrame(() => + { + }); Assert.Equal(requestedPosition, Unsafe.BitCast(viewport.Pos)); Assert.Equal(requestedPosition, Unsafe.BitCast(viewport.WorkPos)); Assert.Equal(requestedSize, Unsafe.BitCast(viewport.Size)); @@ -95,12 +98,9 @@ public void DetachedWindowAppliesRequestsAfterPlatformEventReconciliation() { if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(DetachedWindowAppliesRequestsAfterPlatformEventReconciliation))) return; - using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); - using var window = application.CreateWindow(new PlatformWindowOptions - { title = "Viewport movement", width = 320, height = 240, visible = false }); - using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, - new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); + using var window = application.CreateWindow(new PlatformWindowOptions { title = "Viewport movement", width = 320, height = 240, visible = false }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); context.SetIniFile(null); var io = NativeImGui.Raw.GetIO(); io->ConfigViewportsNoAutoMerge = (true ? (byte)1 : (byte)0); @@ -108,7 +108,8 @@ public void DetachedWindowAppliesRequestsAfterPlatformEventReconciliation() { Vector2 requested = new(500 + frame * 7, 200 + frame * 3); uint viewportId = 0; - context.RenderFrame(() => { + context.RenderFrame(() => + { NativeImGui.SetNextWindowPos(requested); NativeImGui.SetNextWindowSize(new Vector2(200, 120)); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated("Detached movement", allowsNull: false); @@ -122,11 +123,14 @@ public void DetachedWindowAppliesRequestsAfterPlatformEventReconciliation() int y = 0; Assert.True(SDL.Raw.GetWindowPosition(SDL.Raw.GetWindowFromId(windowId), &x, &y) != 0); Assert.Equal(requested, new Vector2(x, y)); - // A real OS move acknowledgement owns reconciliation in the following frame. // Complete that frame before submitting the next application position request. - while (application.PollEvent(out _)) { } - context.RenderFrame(() => { + while (application.PollEvent(out _)) + { + } + + context.RenderFrame(() => + { using NativeStringLease nativeTestText1 = Utf8Codec.EncodeNullTerminated("Detached movement", allowsNull: false); NativeImGui.Raw.Begin((sbyte*)nativeTestText1.pointer, null, (int)(ImGuiWindowFlags.NoFocusOnAppearing)); Assert.Equal(requested, NativeImGui.GetWindowPos()); @@ -141,12 +145,9 @@ public void LogicalDpiTransitionRetainsThePreviousScaleUntilImGuiStartsItsFrame( { if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(LogicalDpiTransitionRetainsThePreviousScaleUntilImGuiStartsItsFrame))) return; - using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); - using var window = application.CreateWindow(new PlatformWindowOptions - { title = "Logical DPI transition", width = 320, height = 240, visible = false }); - using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, - new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); + using var window = application.CreateWindow(new PlatformWindowOptions { title = "Logical DPI transition", width = 320, height = 240, visible = false }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); context.SetIniFile(null); float previous = 0f; context.RenderFrame(() => previous = NativeImGui.Raw.GetMainViewport()->DpiScale); @@ -163,14 +164,16 @@ public void ViewportCallbacksRegisterOneSurfaceAndReleaseItBeforeNativeDestructi { if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(ViewportCallbacksRegisterOneSurfaceAndReleaseItBeforeNativeDestruction))) return; - using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); - using var window = application.CreateWindow(new PlatformWindowOptions - { title = "Viewport ownership", width = 320, height = 240, visible = false }); + using var window = application.CreateWindow(new PlatformWindowOptions { title = "Viewport ownership", width = 320, height = 240, visible = false }); var renderer = new FixtureRenderer(); - using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, - new ImGuiInteractionOptions(false, 1), renderer); - var viewport = new ImGuiViewport { ID = 4217, Size = Unsafe.BitCast(new Vector2(100, 90)), Pos = Unsafe.BitCast(new Vector2(30, 30)) }; + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.EnableViewports, new ImGuiInteractionOptions(false, 1), renderer); + var viewport = new ImGuiViewport + { + ID = 4217, + Size = Unsafe.BitCast(new Vector2(100, 90)), + Pos = Unsafe.BitCast(new Vector2(30, 30)) + }; var io = NativeImGui.Raw.GetPlatformIO(); io->PlatformCreateWindow(&viewport); try @@ -186,6 +189,7 @@ public void ViewportCallbacksRegisterOneSurfaceAndReleaseItBeforeNativeDestructi { io->PlatformDestroyWindow(&viewport); } + Assert.Single(application.GetWindows()); Assert.Null(renderer.target); Assert.True(viewport.PlatformUserData == null); @@ -195,24 +199,44 @@ private sealed class FixtureRenderer : IPlatformImGuiRenderer { internal PlatformImGuiViewportTarget? target; public bool supportsViewports => true; - public void RenderMain(IntPtr drawData) { } + + public void RenderMain(IntPtr drawData) + { + } + public void SynchronizeMainOutput( int pixelWidth, int pixelHeight - ) { } + ) { + } + public void CreateViewport(PlatformImGuiViewportTarget target) => this.target = target; - public void ResizeViewport(PlatformImGuiViewportTarget target) { } + public void ResizeViewport(PlatformImGuiViewportTarget target) + { + } + public void RenderViewport( PlatformImGuiViewportTarget target, IntPtr drawData - ) { } - public void PresentViewport(PlatformImGuiViewportTarget target) { } + ) { + } + + public void PresentViewport(PlatformImGuiViewportTarget target) + { + } + public Task RetireViewport(PlatformImGuiViewportTarget target) { this.target = null; return Task.CompletedTask; } - public void DrainViewportRetirements() { } - public void Dispose() { } + + public void DrainViewportRetirements() + { + } + + public void Dispose() + { + } } } diff --git a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs index c595e2eb8..2f80de903 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs @@ -1,9 +1,12 @@ +using ImGuiWindowFlags = Inno.Editor.ImGui.ImGuiWindowFlags; +using ImGuiMouseButton = Inno.Editor.ImGui.ImGuiMouseButton; +using ImGuiCond = Inno.Editor.ImGui.ImGuiCond; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; using System; using System.Numerics; - using Inno.Native.ImGui; using Xunit; using NativeImGui = Inno.Native.ImGui.ImGui; @@ -11,9 +14,58 @@ using EditorImGui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.ImGui.Tests; - public sealed unsafe class WidgetLayoutTests { + [Fact] + public void DrawingBorrowRejectsAnotherWindowOrFrame() + { + using NativeContext context = new(1f); + Inno.Editor.ImGui.EditorDrawList borrow = default; + context.Frame(() => + { + BeginWindow("Drawing owner", new Vector2(400f, 300f)); + borrow = EditorImGui.GetDrawList(); + borrow.Line(new(30f, 30f), new(60f, 60f), uint.MaxValue); + _ = EditorImGui.BeginChild("Other owner", new Vector2(100f, 100f)); + Assert.Throws(() => borrow.Line(Vector2.Zero, Vector2.One, 0)); + EditorImGui.EndChild(); + NativeImGui.End(); + }); + context.Frame(() => + { + BeginWindow("Drawing owner", new Vector2(400f, 300f)); + Assert.Throws(() => borrow.Line(Vector2.Zero, Vector2.One, 0)); + NativeImGui.End(); + }); + Assert.Throws(() => default(Inno.Editor.ImGui.EditorDrawList).Line(Vector2.Zero, Vector2.One, 0)); + } + + [Fact] + public void BoundedTextControlsDoNotAllocateOneBufferAndEncoderPerSubmission() + { + using NativeContext context = new(1f); + string value = "Unicode: 漢字 😀"; + long allocated = 0; + Action draw = () => + { + BeginWindow("Text allocation", new Vector2(400f, 300f)); + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int index = 0; index < 128; index++) + { + EditorImGui.PushId(index); + _ = EditorImGui.InputText("Value", ref value, 1024); + EditorImGui.PopId(); + } + + allocated = GC.GetAllocatedBytesForCurrentThread() - before; + NativeImGui.End(); + }; + context.Frame(draw); + context.Frame(draw); + Assert.Equal("Unicode: 漢字 😀", value); + Assert.True(allocated < 128L * 512, $"Unexpected steady-state allocation: {allocated} bytes for 128 fields."); + } + [Fact] public void TimelineWorkspace_UsesOneBoundedChildScrollOwner() { @@ -30,25 +82,28 @@ public void TimelineWorkspace_UsesOneBoundedChildScrollOwner() { if (visible) { - Widget.SectionLayout(() => + EditorUI.current.widgets.SectionLayout(() => { - if (Widget.SectionHeader("Frames")) + if (EditorUI.current.widgets.SectionHeader("Frames")) { for (int index = 0; index < 40; index++) EditorImGui.Text("Frame " + index); } }); } + childScroll = NativeImGui.GetScrollMaxY(); } finally { EditorImGui.EndChild(); } + parentScroll = NativeImGui.GetScrollMaxY(); NativeImGui.End(); }); } + Assert.True(childScroll > 200f); Assert.Equal(0f, parentScroll); } @@ -109,20 +164,17 @@ public void PropertyRows_WrapWithinTwoToThreeColumnsWithoutScrollbars(float zoom { BeginWindow("Property layout", new Vector2(320f, 270f)); fontHeight = NativeImGui.GetTextLineHeight(); - Widget.PropertyRow( - "persistence", - () => - { - labelWidth = NativeImGui.GetContentRegionAvail().X; - Widget.PropertyLabel("Persistent data directory for this exported application"); - labelSize = NativeImGui.GetItemRectSize(); - }, - () => - { - valueWidth = NativeImGui.GetContentRegionAvail().X; - string value = "Application data"; - Widget.SearchInput("directory", "Directory", ref value); - }); + EditorUI.current.widgets.PropertyRow("persistence", () => + { + labelWidth = NativeImGui.GetContentRegionAvail().X; + EditorUI.current.widgets.PropertyLabel("Persistent data directory for this exported application"); + labelSize = NativeImGui.GetItemRectSize(); + }, () => + { + valueWidth = NativeImGui.GetContentRegionAvail().X; + string value = "Application data"; + EditorUI.current.widgets.SearchInput("directory", "Directory", ref value); + }); scroll = new Vector2(NativeImGui.GetScrollMaxX(), NativeImGui.GetScrollMaxY()); NativeImGui.End(); }); @@ -159,7 +211,7 @@ float zoom { BeginWindow("Selector layout", new Vector2(400f, 360f)); NativeImGui.SetNextItemWidth(260f); - bool visible = Widget.BeginBoundedCombo("tags", "Untagged"); + bool visible = EditorUI.current.widgets.BeginBoundedCombo("tags", "Untagged"); if (visible) { opened = true; @@ -168,21 +220,20 @@ float zoom popupPosition = NativeImGui.GetWindowPos(); popupSize = NativeImGui.GetWindowSize(); scroll = new Vector2(NativeImGui.GetScrollMaxX(), NativeImGui.GetScrollMaxY()); - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } else { controlMinimum = NativeImGui.GetItemRectMin(); controlMaximum = NativeImGui.GetItemRectMax(); } + NativeImGui.End(); }; - context.Frame(draw); context.Click((controlMinimum + controlMaximum) * 0.5f, 0, draw); for (int frame = 0; frame < 6; frame++) context.Frame(draw); - Assert.True(opened); Assert.InRange(popupPosition.Y, controlMaximum.Y - 1f, controlMaximum.Y + 1f); Assert.True(popupPosition.X >= 20f); @@ -209,28 +260,27 @@ public void SelectorsNearWindowBottom_UseOnlyRemainingDownwardSpace(float vertic BeginWindow("Bottom selector", new Vector2(400f, 360f)); NativeImGui.SetCursorPosY(verticalOffset); NativeImGui.SetNextItemWidth(260f); - if (Widget.BeginBoundedCombo("objects", "Selected object")) + if (EditorUI.current.widgets.BeginBoundedCombo("objects", "Selected object")) { opened = true; for (int index = 0; index < 100; index++) NativeImGui.Selectable($"Object {index}"); popupPosition = NativeImGui.GetWindowPos(); popupSize = NativeImGui.GetWindowSize(); - Widget.EndBoundedCombo(); + EditorUI.current.widgets.EndBoundedCombo(); } else { controlMinimum = NativeImGui.GetItemRectMin(); controlMaximum = NativeImGui.GetItemRectMax(); } + NativeImGui.End(); }; - context.Frame(draw); context.Click((controlMinimum + controlMaximum) * 0.5f, 0, draw); for (int frame = 0; frame < 6; frame++) context.Frame(draw); - Assert.True(opened); Assert.InRange(popupPosition.Y, controlMaximum.Y - 1f, controlMaximum.Y + 1f); Assert.True(popupPosition.Y + popupSize.Y <= 381f); @@ -257,27 +307,26 @@ public void ContextMenu_SearchFillsItsActualContentWidthWithoutScrollbars(float NativeImGui.InvisibleButton("Scene", new Vector2(200f, 30f)); controlMinimum = NativeImGui.GetItemRectMin(); controlMaximum = NativeImGui.GetItemRectMax(); - if (Widget.BeginContextMenu("scene-actions")) + if (EditorUI.current.widgets.BeginContextMenu("scene-actions")) { opened = true; availableWidth = NativeImGui.GetContentRegionAvail().X; - Widget.SearchInput("commands", "Search commands...", ref query); + EditorUI.current.widgets.SearchInput("commands", "Search commands...", ref query); searchWidth = NativeImGui.GetItemRectSize().X; NativeImGui.MenuItem("Set Active Scene", string.Empty, false, true); NativeImGui.MenuItem("Create", string.Empty, false, true); NativeImGui.MenuItem("Rename", "F2", false, true); NativeImGui.MenuItem("Unload", "Delete", false, true); scroll = new Vector2(NativeImGui.GetScrollMaxX(), NativeImGui.GetScrollMaxY()); - Widget.EndContextMenu(); + EditorUI.current.widgets.EndContextMenu(); } + NativeImGui.End(); }; - context.Frame(draw); context.Click((controlMinimum + controlMaximum) * 0.5f, 1, draw); for (int frame = 0; frame < 6; frame++) context.Frame(draw); - Assert.True(opened); Assert.InRange(availableWidth - searchWidth, 0f, 1.1f); Assert.Equal(Vector2.Zero, scroll); @@ -294,21 +343,19 @@ public void OverlappingWindows_OnlyForegroundClickableTextReceivesPress() { BeginWindow("Background panel", new Vector2(400f, 300f)); NativeImGui.SetCursorScreenPos(new Vector2(100f, 100f)); - if (Widget.ClickableText("close", "X", new Vector2(40f, 40f))) + if (EditorUI.current.widgets.ClickableText("close", "X", new Vector2(40f, 40f))) backgroundPresses++; NativeImGui.End(); BeginWindow("Foreground shader", new Vector2(400f, 300f)); NativeImGui.SetCursorScreenPos(new Vector2(100f, 100f)); - if (Widget.ClickableText("action", "Edit", new Vector2(40f, 40f))) + if (EditorUI.current.widgets.ClickableText("action", "Edit", new Vector2(40f, 40f))) foregroundPresses++; hit = (NativeImGui.GetItemRectMin() + NativeImGui.GetItemRectMax()) * 0.5f; NativeImGui.End(); }; - context.Frame(draw); context.Frame(draw); context.Click(hit, 0, draw); - Assert.Equal(0, backgroundPresses); Assert.Equal(1, foregroundPresses); } @@ -328,19 +375,16 @@ float zoom Action draw = () => { BeginWindow("Tree content", new Vector2(400f, 300f)); - var result = Widget.TreeNode( - $"content-{isLeaf}-{zoom}", - _ => Widget.IconText("O", "Selected object", false), - new() - { - isLeaf = isLeaf, - drawViewportOverlay = () => - { - float rowY = NativeImGui.GetItemRectMin().Y; - NativeImGui.SetCursorScreenPos(new Vector2(360f, rowY)); - _ = Widget.ClickableIcon("visibility", "V", "Visibility"); - } - }); + var result = EditorUI.current.widgets.TreeNode($"content-{isLeaf}-{zoom}", _ => EditorUI.current.widgets.IconText( + "O", + "Selected object", + false + ), new() { isLeaf = isLeaf, drawViewportOverlay = () => + { + float rowY = NativeImGui.GetItemRectMin().Y; + NativeImGui.SetCursorScreenPos(new Vector2(360f, rowY)); + _ = EditorUI.current.widgets.ClickableIcon("visibility", "V", "Visibility"); + } }); hit = new Vector2(result.contentMin.X + 80f, (result.min.Y + result.max.Y) * 0.5f); if (result.isClicked) presses++; @@ -348,11 +392,9 @@ float zoom NativeImGui.TreePop(); NativeImGui.End(); }; - context.Frame(draw); context.Frame(draw); context.Click(hit, 0, draw, settleHover: false); - Assert.Equal(1, presses); } @@ -366,13 +408,8 @@ public void TreeRows_DisclosureClickExpandsWithoutSelectingContent() Action draw = () => { BeginWindow("Tree disclosure", new Vector2(400f, 300f)); - var result = Widget.TreeNode( - "disclosure", - _ => NativeImGui.TextUnformatted("Parent"), - new() { isLeaf = false }); - hit = new Vector2( - result.contentMin.X - NativeImGui.GetTreeNodeToLabelSpacing() * 0.5f, - (result.min.Y + result.max.Y) * 0.5f); + var result = EditorUI.current.widgets.TreeNode("disclosure", _ => NativeImGui.TextUnformatted("Parent"), new() { isLeaf = false }); + hit = new Vector2(result.contentMin.X - NativeImGui.GetTreeNodeToLabelSpacing() * 0.5f, (result.min.Y + result.max.Y) * 0.5f); opened = result.isOpen; if (result.isClicked) presses++; @@ -380,12 +417,10 @@ public void TreeRows_DisclosureClickExpandsWithoutSelectingContent() NativeImGui.TreePop(); NativeImGui.End(); }; - context.Frame(draw); context.Frame(draw); context.Click(hit, 0, draw); context.Frame(draw); - Assert.True(opened); Assert.Equal(0, presses); } @@ -402,30 +437,21 @@ public void TreeRows_OverlayConsumesItsOwnClick(bool settleHover) Action draw = () => { BeginWindow("Tree overlay", new Vector2(400f, 300f)); - var result = Widget.TreeNode( - $"overlay-{settleHover}", - _ => NativeImGui.TextUnformatted("Object"), - new() - { - isLeaf = true, - drawViewportOverlay = () => - { - float rowY = NativeImGui.GetItemRectMin().Y; - NativeImGui.SetCursorScreenPos(new Vector2(360f, rowY)); - if (NativeImGui.SmallButton("Eye")) - overlayPresses++; - overlayHit = (NativeImGui.GetItemRectMin() + NativeImGui.GetItemRectMax()) * 0.5f; - } - }); + var result = EditorUI.current.widgets.TreeNode($"overlay-{settleHover}", _ => NativeImGui.TextUnformatted("Object"), new() { isLeaf = true, drawViewportOverlay = () => + { + float rowY = NativeImGui.GetItemRectMin().Y; + NativeImGui.SetCursorScreenPos(new Vector2(360f, rowY)); + if (NativeImGui.SmallButton("Eye")) + overlayPresses++; + overlayHit = (NativeImGui.GetItemRectMin() + NativeImGui.GetItemRectMax()) * 0.5f; + } }); if (result.isClicked) rowPresses++; NativeImGui.End(); }; - context.Frame(draw); context.Frame(draw); context.Click(overlayHit, 0, draw, settleHover); - Assert.Equal(1, overlayPresses); Assert.Equal(0, rowPresses); } @@ -441,10 +467,7 @@ public void TreeRows_ContentDoubleClickExpandsOnce() Action draw = () => { BeginWindow("Tree double click", new Vector2(400f, 300f)); - var result = Widget.TreeNode( - "double-click", - _ => NativeImGui.TextUnformatted("Parent"), - new() { isLeaf = false }); + var result = EditorUI.current.widgets.TreeNode("double-click", _ => NativeImGui.TextUnformatted("Parent"), new() { isLeaf = false }); hit = new Vector2(result.contentMin.X + 80f, (result.min.Y + result.max.Y) * 0.5f); opened = result.isOpen; if (result.isClicked) @@ -455,18 +478,38 @@ public void TreeRows_ContentDoubleClickExpandsOnce() NativeImGui.TreePop(); NativeImGui.End(); }; - context.Frame(draw); context.Frame(draw); context.Click(hit, 0, draw); context.Click(hit, 0, draw); context.Frame(draw); - Assert.Equal(2, presses); Assert.Equal(1, doublePresses); Assert.True(opened); } + [Fact] + public void MixedValueRestoresOuterStateAfterNestedGroupsAndFailures() + { + using NativeContext context = new(1f); + context.Frame(() => + { + BeginWindow("Mixed scope", new Vector2(400f, 300f)); + var widgets = EditorUI.current.widgets; + Assert.False(widgets.isMixedValue); + widgets.MixedValue(true, () => + { + Assert.True(widgets.isMixedValue); + widgets.MixedValue(false, () => Assert.True(widgets.isMixedValue)); + Assert.True(widgets.isMixedValue); + }); + Assert.False(widgets.isMixedValue); + Assert.Throws(() => widgets.MixedValue(true, () => throw new InvalidOperationException("rejected draw"))); + Assert.False(widgets.isMixedValue); + NativeImGui.End(); + }); + } + private static void BeginWindow( string name, Vector2 size @@ -474,25 +517,30 @@ Vector2 size NativeImGui.SetNextWindowPos(new Vector2(20f, 20f), (int)(ImGuiCond.Always), default(Vector2)); NativeImGui.SetNextWindowSize(size, (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated(name, allowsNull: false); - NativeImGui.Raw.Begin((sbyte*)nativeTestText0.pointer, null, (int)(ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove - | ImGuiWindowFlags.NoSavedSettings)); + NativeImGui.Raw.Begin( + (sbyte*)nativeTestText0.pointer, + null, + (int)(ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoSavedSettings) + ); } private sealed class NativeContext : IDisposable { private readonly ImGuiNativeContext* m_context; - + private readonly Inno.Editor.ImGui.EditorUiSession m_ui = new(); + private readonly IDisposable m_uiScope; public NativeContext(float zoom) { m_context = NativeImGui.Raw.CreateContext(null); + m_uiScope = m_ui.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(1024f, 768f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; io->IniFilename = null; - Widget.style.SetZoom(zoom); - Widget.SetupStyle(); + EditorUI.current.widgets.style.SetZoom(zoom); + EditorUI.current.widgets.SetupStyle(); } public void Frame(Action draw) @@ -520,8 +568,9 @@ public void Click( public void Dispose() { + m_uiScope.Dispose(); + m_ui.Dispose(); NativeImGui.Raw.DestroyContext(m_context); - Widget.style.SetZoom(1f); } } } diff --git a/tests/editor/Inno.Editor.Inspection.Tests/ConditionalInspectionTests.cs b/tests/editor/Inno.Editor.Inspection.Tests/ConditionalInspectionTests.cs index 92a0269ff..85d9cbbab 100644 --- a/tests/editor/Inno.Editor.Inspection.Tests/ConditionalInspectionTests.cs +++ b/tests/editor/Inno.Editor.Inspection.Tests/ConditionalInspectionTests.cs @@ -2,6 +2,8 @@ using Inno.Adapter.Modules.DotNet; using System; using System.IO; +using System.Linq; +using Inno.Editor.Annotations; using Inno.Core.Logging; using Inno.Core.Serialization; using Inno.Editor.Core; @@ -11,7 +13,6 @@ using Xunit; namespace Inno.Editor.Inspection.Tests; - public sealed class ConditionalInspectionTests : IDisposable { private readonly string m_root = Path.Combine(Path.GetTempPath(), "inno-inspection-" + Guid.NewGuid().ToString("N")); @@ -20,18 +21,16 @@ public sealed class ConditionalInspectionTests : IDisposable private readonly SerializationRegistry m_serialization; private readonly LogRouter m_logs = new(); private readonly EditorInteractionRuntime m_runtime; - public ConditionalInspectionTests() { Directory.CreateDirectory(m_root); - m_modules = new(new() - { - catalogSource = new DotNetAssemblyCatalogSource(typeof(ConditionalInspectionTests).Assembly) }); + m_modules = new(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(ConditionalInspectionTests).Assembly) }); m_types = new(m_modules, new ReflectionTypeCatalogSource()); m_serialization = new(m_types, new ReflectionSerializationMetadataSource()); m_runtime = new(new EditorContext(m_root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_types, m_logs, [m_types, m_serialization]); m_runtime.Start(); } + [Fact] public void ConditionalInspectorPeersResolveByPredicateAndRejectActualAmbiguity() { @@ -43,14 +42,37 @@ public void ConditionalInspectorPeersResolveByPredicateAndRejectActualAmbiguity( Assert.IsType(first); Assert.True(inspectors.TryResolveExact(m_runtime.context, new ConditionalTarget(2, false), renderer, out var second, out _)); Assert.IsType(second); - InvalidOperationException error = Assert.Throws(() => - inspectors.TryResolve(m_runtime.context, new ConditionalTarget(2, true), renderer, out _, out _)); + InvalidOperationException error = Assert.Throws(() => inspectors.TryResolve(m_runtime.context, new ConditionalTarget(2, true), renderer, out _, out _)); Assert.Contains("both accept", error.Message); - Assert.Throws(() => - inspectors.TryResolveExact(m_runtime.context, new ConditionalTarget(2, true), renderer, out _, out _)); + Assert.Throws(() => inspectors.TryResolveExact(m_runtime.context, new ConditionalTarget(2, true), renderer, out _, out _)); + IInspectionDrawer CreateDrawer(Type type) => (IInspectionDrawer)Activator.CreateInstance(type, nonPublic: true)!; + } + + [Fact] + public void PropertyAvailabilityUsesCurrentOwnerConditionsWithoutRequiringNativeUi() + { + using var properties = new PropertyDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + using var attributes = new InspectorAttributeDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + var renderer = new SerializedPropertyRenderer(properties, attributes, m_runtime.interactions, new ProbePropertyEdits(), m_logs); + var target = new AvailabilityTarget(); + var values = m_serialization.GetProperties(target); + SerializedProperty conditional = values.Single(value => value.name == nameof(AvailabilityTarget.value)); + Assert.False(renderer.GetPropertyAvailability(target, conditional).isVisible); + target.visible = true; + Assert.True(renderer.GetPropertyAvailability(target, conditional).isVisible); + Assert.True(renderer.GetPropertyAvailability(target, values.Single(value => value.name == nameof(AvailabilityTarget.readOnlyValue))).isReadOnly); + } - IInspectionDrawer CreateDrawer(Type type) - => (IInspectionDrawer)Activator.CreateInstance(type, nonPublic: true)!; + [StableTypeId("eeeb7c59-2af3-4cc3-81d2-c853feef3874")] + public sealed class AvailabilityTarget : ISerializable + { + public bool visible { get; set; } + + [SerializableProperty, ShowIf(nameof(visible))] + public int value { get; set; } + + [SerializableProperty, InspectorReadOnly] + public int readOnlyValue { get; set; } } public sealed record ConditionalTarget( @@ -61,6 +83,7 @@ bool ambiguous public sealed class ConditionalFirst : InspectionDrawer { public override string icon => ""; + protected override bool CanInspect(ConditionalTarget target) => target.kind == 1; protected override (string name, Action? setter) BindName( InspectionDrawContext context, @@ -69,12 +92,15 @@ ConditionalTarget target protected override void Draw( InspectionDrawContext context, ConditionalTarget target - ) { } + ) { + } } + [InspectionDrawer(typeof(ConditionalTarget), conditional: true)] public sealed class ConditionalSecond : InspectionDrawer { public override string icon => ""; + protected override bool CanInspect(ConditionalTarget target) => target.kind == 2; protected override (string name, Action? setter) BindName( InspectionDrawContext context, @@ -83,12 +109,15 @@ ConditionalTarget target protected override void Draw( InspectionDrawContext context, ConditionalTarget target - ) { } + ) { + } } + [InspectionDrawer(typeof(ConditionalTarget), conditional: true)] public sealed class ConditionalOverlap : InspectionDrawer { public override string icon => ""; + protected override bool CanInspect(ConditionalTarget target) => target.ambiguous; protected override (string name, Action? setter) BindName( InspectionDrawContext context, @@ -97,20 +126,22 @@ ConditionalTarget target protected override void Draw( InspectionDrawContext context, ConditionalTarget target - ) { } + ) { + } } + private sealed class ProbePropertyEdits : IInspectionPropertyEditService { public bool ChangeProperty( object owner, string propertyName, Action mutation, - string historyName - ) - => throw new InvalidOperationException("Resolution must not edit a property."); + string historyName, + string? mergeKey, + string? componentName + ) => throw new InvalidOperationException("Resolution must not edit a property."); } - public void Dispose() { m_runtime.Dispose(); @@ -121,4 +152,3 @@ public void Dispose() Directory.Delete(m_root, true); } } - diff --git a/tests/editor/Inno.Editor.Inspection.Tests/Inno.Editor.Inspection.Tests.csproj b/tests/editor/Inno.Editor.Inspection.Tests/Inno.Editor.Inspection.Tests.csproj index 2b197c70d..75917ed19 100644 --- a/tests/editor/Inno.Editor.Inspection.Tests/Inno.Editor.Inspection.Tests.csproj +++ b/tests/editor/Inno.Editor.Inspection.Tests/Inno.Editor.Inspection.Tests.csproj @@ -7,9 +7,11 @@ false true Editor + $(DefineConstants);INNO_EDITOR + @@ -25,5 +27,8 @@ - + + + + diff --git a/tests/editor/Inno.Editor.Interactions.Tests/AssetHistoryTests.cs b/tests/editor/Inno.Editor.Interactions.Tests/AssetHistoryTests.cs index abc6a605e..91de7bcb7 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/AssetHistoryTests.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/AssetHistoryTests.cs @@ -3,7 +3,6 @@ using Inno.Adapter.Modules.DotNet; using System; using System.IO; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Core.Diagnostics; @@ -14,20 +13,18 @@ using Inno.Core.Serialization; using Inno.Core.Settings; using Inno.Editor.Core; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Plugins.Authoring; using Xunit; namespace Inno.Editor.Interactions.Tests; - public sealed class AssetHistoryTests : IDisposable { + private readonly Inno.Editor.ImGui.EditorUiSession m_ui = new(); private readonly IdentityAllocator m_identities = new(); private readonly IDisposable m_identityScope; - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoAssetHistoryTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoAssetHistoryTests", Guid.NewGuid().ToString("N")); private readonly ModuleHost m_modules; private readonly TypeCatalog m_types; private readonly SerializationRegistry m_serialization; @@ -37,65 +34,47 @@ public sealed class AssetHistoryTests : IDisposable private readonly PluginEnvironment m_plugins; private readonly ProjectSettingsStore m_settings; private readonly EditorInteractionRuntime m_runtime; - public AssetHistoryTests() { string assetRoot = Path.Combine(m_projectRoot, "Assets"); Directory.CreateDirectory(assetRoot); m_identityScope = m_identities.EnterScope(); - _ = typeof(AssetEditorModule); + _ = typeof(AssetEditingModule); _ = typeof(TextAsset); - m_modules = new ModuleHost(new ModuleHostOptions - { catalogSource = new DotNetAssemblyCatalogSource(typeof(AssetHistoryTests).Assembly) }); + m_modules = new ModuleHost(new ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(AssetHistoryTests).Assembly) }); m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); m_serialization = new SerializationRegistry(m_types, new ReflectionSerializationMetadataSource()); string libraryRoot = Path.Combine(m_projectRoot, "Library"); string pluginRoot = Path.Combine(m_projectRoot, "Plugins"); - var pluginSources = new PluginSourceService( - m_serialization, - pluginRoot, - libraryRoot); + var pluginSources = new PluginSourceService(m_serialization, pluginRoot, libraryRoot); PluginScanResult pluginScan = pluginSources.Scan(); - AssetPipelineOptions options = AssetPipelineOptions.Create( - assetRoot, - libraryRoot); + AssetPipelineOptions options = AssetPipelineOptions.Create(assetRoot, libraryRoot); options = options with { enableFileSystemWatcher = false }; - m_assets = new AssetPipeline( - m_modules, - m_types, - m_serialization, - m_identities, - m_diagnostics, - m_logs, - options); + m_assets = new AssetPipeline(m_modules, m_types, m_serialization, m_identities, m_diagnostics, m_logs, options); m_settings = new ProjectSettingsStore( new FileByteDocumentStore(Path.GetFullPath(Path.Combine(m_projectRoot, "Settings.Project.inno"))), m_types, m_serialization, new ProjectId("tests.editor"), - AssetSerializationContext.Create(m_assets)); - m_plugins = new PluginEnvironment( - m_assets, - m_settings, - m_serialization, - pluginRoot, - libraryRoot, - pluginScan, - m_modules.generations); + AssetSerializationContext.Create(m_assets) + ); + m_plugins = new PluginEnvironment(m_assets, m_settings, m_serialization, pluginRoot, libraryRoot, pluginScan, m_modules.generations); m_runtime = new EditorInteractionRuntime( new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_types, m_logs, - [m_types, m_serialization, m_identities, m_assets, m_plugins]); + [m_types, m_serialization, m_identities, m_assets, m_plugins, m_ui] + ); m_runtime.Start(); } public void Dispose() { m_runtime.Dispose(); + m_ui.Dispose(); m_plugins.Dispose(); m_assets.Dispose(); m_settings.Dispose(); @@ -108,15 +87,30 @@ public void Dispose() Directory.Delete(m_projectRoot, recursive: true); } + [Fact] + public void MultiSelectionDeleteUsesOneAtomicHistoryEntry() + { + m_assets.CreateDirectory(AssetPath.Project("First")); + m_assets.CreateDirectory(AssetPath.Project("First/Child")); + m_assets.CreateDirectory(AssetPath.Project("Second")); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("First"), out AssetFileEntry first)); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("First/Child"), out AssetFileEntry child)); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("Second"), out AssetFileEntry second)); + m_runtime.interactions.SetSelection([child, first, second], child); + Assert.True(m_runtime.interactions.For("panel/asset.file-browser", second).Execute("file-browser/delete")); + Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("First"), out _)); + Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("Second"), out _)); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("First/Child"), out _)); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("Second"), out _)); + } + [Fact] public void CreateFolderUndoAndRedoUseTheAssetDatabase() { - Assert.True(m_runtime.interactions - .For("panel/asset.file-browser", string.Empty) - .Execute("file-browser/create-folder")); + Assert.True(m_runtime.interactions.For("panel/asset.file-browser", string.Empty).Execute("file-browser/create-folder")); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("New Folder"), out AssetFileEntry created)); Assert.True(created.isDirectory); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("New Folder"), out _)); Assert.True(m_runtime.interactions.history.Redo().succeeded); @@ -130,18 +124,13 @@ public void DeleteFolderUndoRestoresIdentityMetadataAndEmptyDescendants() m_assets.CreateDirectory(AssetPath.Project("Folder/Empty")); Assert.True(m_assets.TryGetPersistentId(AssetPath.Project("Folder"), out Guid folderId)); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("Folder"), out AssetFileEntry folder)); - - Assert.True(m_runtime.interactions - .For("panel/asset.file-browser", folder) - .Execute("file-browser/delete")); + Assert.True(m_runtime.interactions.For("panel/asset.file-browser", folder).Execute("file-browser/delete")); Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("Folder"), out _)); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("Folder/Empty"), out AssetFileEntry restoredEmpty)); Assert.True(restoredEmpty.isDirectory); Assert.True(m_assets.TryGetPersistentId(AssetPath.Project("Folder"), out Guid restoredId)); Assert.Equal(folderId, restoredId); - Assert.True(m_runtime.interactions.history.Redo().succeeded); Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("Folder"), out _)); } @@ -151,13 +140,9 @@ public void AssetDeleteHistorySurvivesATypeCatalogRefresh() { m_assets.CreateDirectory(AssetPath.Project("ReloadSafe")); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("ReloadSafe"), out AssetFileEntry folder)); - Assert.True(m_runtime.interactions - .For("panel/asset.file-browser", folder) - .Execute("file-browser/delete")); - + Assert.True(m_runtime.interactions.For("panel/asset.file-browser", folder).Execute("file-browser/delete")); m_types.Rebuild(); _ = m_runtime.panelCount; - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("ReloadSafe"), out _)); } @@ -170,32 +155,22 @@ public void FileBrowserDropMovesFilesAndFoldersWithUndoRedo() m_assets.CreateDirectory(AssetPath.Project("From/Folder/Child")); m_assets.CreateDirectory(AssetPath.Project("To")); Assert.True(m_assets.Save(AssetPath.Project("From/Value.txt"), new TextAsset("value"))); - Assert.True(m_assets.TryGetFileSystemEntry( - AssetPath.Project("From/Value.txt"), - out AssetFileEntry file)); - - RuntimeIdentity fileToken = m_runtime.interactions - .For("panel/asset.file-browser", file) - .BeginDrag(new EditorDragData(file, "Value.txt")); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("From/Value.txt"), out AssetFileEntry file)); + RuntimeIdentity fileToken = m_runtime.interactions.For("panel/asset.file-browser", file).BeginDrag(new EditorDragData(file, "Value.txt")); EditorInteraction destination = m_runtime.interactions.For("panel/asset.file-browser", "To"); Assert.True(destination.QueryDrop(fileToken, EditorDropPlacement.Into).canDrop); Assert.True(destination.Drop(fileToken, EditorDropPlacement.Into).accepted); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("To/Value.txt"), out _)); Assert.False(m_assets.TryGetFileSystemEntry(AssetPath.Project("From/Value.txt"), out _)); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("From/Value.txt"), out _)); Assert.True(m_runtime.interactions.history.Redo().succeeded); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("To/Value.txt"), out _)); - Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("From/Folder"), out AssetFileEntry folder)); - RuntimeIdentity folderToken = m_runtime.interactions - .For("panel/asset.file-browser", folder) - .BeginDrag(new EditorDragData(folder, "Folder")); + RuntimeIdentity folderToken = m_runtime.interactions.For("panel/asset.file-browser", folder).BeginDrag(new EditorDragData(folder, "Folder")); Assert.True(destination.QueryDrop(folderToken, EditorDropPlacement.Into).canDrop); Assert.True(destination.Drop(folderToken, EditorDropPlacement.Into).accepted); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("To/Folder/Child"), out _)); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("From/Folder/Child"), out _)); } @@ -208,21 +183,9 @@ public void FileBrowserDropRejectsAFolderOwnDescendantAndNameCollisions() m_assets.CreateDirectory(AssetPath.Project("Target")); m_assets.CreateDirectory(AssetPath.Project("Target/Source")); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project("Source"), out AssetFileEntry source)); - - RuntimeIdentity descendantToken = m_runtime.interactions - .For("panel/asset.file-browser", source) - .BeginDrag(new EditorDragData(source, "Source")); - Assert.False(m_runtime.interactions - .For("panel/asset.file-browser", "Source/Child") - .QueryDrop(descendantToken, EditorDropPlacement.Into) - .canDrop); - - RuntimeIdentity collisionToken = m_runtime.interactions - .For("panel/asset.file-browser", source) - .BeginDrag(new EditorDragData(source, "Source")); - Assert.False(m_runtime.interactions - .For("panel/asset.file-browser", "Target") - .QueryDrop(collisionToken, EditorDropPlacement.Into) - .canDrop); + RuntimeIdentity descendantToken = m_runtime.interactions.For("panel/asset.file-browser", source).BeginDrag(new EditorDragData(source, "Source")); + Assert.False(m_runtime.interactions.For("panel/asset.file-browser", "Source/Child").QueryDrop(descendantToken, EditorDropPlacement.Into).canDrop); + RuntimeIdentity collisionToken = m_runtime.interactions.For("panel/asset.file-browser", source).BeginDrag(new EditorDragData(source, "Source")); + Assert.False(m_runtime.interactions.For("panel/asset.file-browser", "Target").QueryDrop(collisionToken, EditorDropPlacement.Into).canDrop); } } diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorHistoryTests.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorHistoryTests.cs index d8f17cee9..73c4c5b5e 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/EditorHistoryTests.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorHistoryTests.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; - using Inno.Extensibility.Modules; using Inno.Extensibility.Types; using Inno.Core.Logging; @@ -11,18 +10,84 @@ using Xunit; namespace Inno.Editor.Interactions.Tests; - public sealed class EditorHistoryTests { [Fact] - public void ExecuteUndoAndRedoMoveNeutralChangeBetweenStacks() + public void SeparateGesturesNeverMergeTheSamePropertyAcrossCommitBoundaries() + { + using var fixture = new HistoryFixture(); + for (int step = 0; step < 2; step++) + { + using var gesture = new EditorEditGesture(fixture.history, "Drag Value"); + Assert.True(fixture.history.Execute("Update", NeutralValueHistoryHandler.CreateChange(1, step, step + 1, gesture.mergeKey)).succeeded); + gesture.Commit(); + } + + Assert.True(fixture.history.Undo().succeeded); + Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); + Assert.True(fixture.history.Undo().succeeded); + Assert.Equal(0, NeutralValueHistoryHandler.GetValue(1)); + } + + [Fact] + public void BlockedGestureCancellationRetainsOwnershipUntilRecovery() { using var fixture = new HistoryFixture(); + using var gesture = new EditorEditGesture(fixture.history, "Drag Value"); + Assert.True(fixture.history.Execute("Update", NeutralValueHistoryHandler.CreateChange(1, 0, 5, gesture.mergeKey)).succeeded); + NeutralValueHistoryHandler.Block(1, EditorHistoryDirection.Undo); + Assert.False(gesture.Cancel().succeeded); + Assert.True(gesture.isActive); + Assert.Equal(5, NeutralValueHistoryHandler.GetValue(1)); + NeutralValueHistoryHandler.Unblock(1, EditorHistoryDirection.Undo); + Assert.True(gesture.Cancel().succeeded); + Assert.False(gesture.isActive); + Assert.Equal(0, NeutralValueHistoryHandler.GetValue(1)); + Assert.False(fixture.history.canUndo); + } - EditorHistoryResult applied = fixture.history.Execute( - "Change Value", - NeutralValueHistoryHandler.CreateChange(slot: 1, before: 0, after: 10)); + [Theory] + [InlineData("commit")] + [InlineData("rollback")] + [InlineData("dispose")] + public void RejectedOutOfOrderCompletionPreservesBothTransactions(string completion) + { + using var fixture = new HistoryFixture(); + using EditorHistoryTransaction outer = fixture.history.BeginTransaction("Outer"); + Assert.True(fixture.history.Execute("First", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + using EditorHistoryTransaction inner = fixture.history.BeginTransaction("Inner"); + Assert.True(fixture.history.Execute("Second", NeutralValueHistoryHandler.CreateChange(2, 0, 2)).succeeded); + Assert.Throws(() => + { + switch (completion) + { + case "commit": + outer.Commit(); + break; + case "rollback": + _ = outer.Rollback(); + break; + default: + outer.Dispose(); + break; + } + }); + Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); + Assert.Equal(2, NeutralValueHistoryHandler.GetValue(2)); + Assert.True(inner.Rollback().succeeded); + Assert.Equal(0, NeutralValueHistoryHandler.GetValue(2)); + outer.Commit(); + Assert.Equal("Outer", fixture.history.undoName); + Assert.True(fixture.history.Undo().succeeded); + Assert.Equal(0, NeutralValueHistoryHandler.GetValue(1)); + Assert.False(fixture.history.canUndo); + } + [Fact] + public void ExecuteUndoAndRedoMoveNeutralChangeBetweenStacks() + { + using var fixture = new HistoryFixture(); + EditorHistoryResult applied = fixture.history.Execute("Change Value", NeutralValueHistoryHandler.CreateChange(slot: 1, before: 0, after: 10)); Assert.True(applied.succeeded); Assert.Equal(10, NeutralValueHistoryHandler.GetValue(1)); Assert.Equal("Change Value", fixture.history.undoName); @@ -37,23 +102,18 @@ public void ExecuteUndoAndRedoMoveNeutralChangeBetweenStacks() public void FailedTransitionRemainsAvailableForRetry() { using var fixture = new HistoryFixture(); - Assert.True(fixture.history.Execute( - "Conditional", - NeutralValueHistoryHandler.CreateChange(slot: 1, before: 0, after: 1)).succeeded); - + Assert.True(fixture.history.Execute("Conditional", NeutralValueHistoryHandler.CreateChange(slot: 1, before: 0, after: 1)).succeeded); NeutralValueHistoryHandler.Block(slot: 1, EditorHistoryDirection.Undo); Assert.False(fixture.history.Undo().succeeded); Assert.True(fixture.history.canUndo); Assert.Equal("Conditional", fixture.history.undoName); Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); - NeutralValueHistoryHandler.Unblock(slot: 1, EditorHistoryDirection.Undo); Assert.True(fixture.history.Undo().succeeded); NeutralValueHistoryHandler.Block(slot: 1, EditorHistoryDirection.Redo); Assert.False(fixture.history.Redo().succeeded); Assert.True(fixture.history.canRedo); Assert.Equal(0, NeutralValueHistoryHandler.GetValue(1)); - NeutralValueHistoryHandler.Unblock(slot: 1, EditorHistoryDirection.Redo); Assert.True(fixture.history.Redo().succeeded); Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); @@ -64,14 +124,9 @@ public void AdjacentChangesWithTheSameStableMergeKeyCoalesce() { using var fixture = new HistoryFixture(); NeutralValueHistoryHandler.SetValue(slot: 1, value: 2); - fixture.history.RecordApplied( - "Change Value", - NeutralValueHistoryHandler.CreateChange(1, 1, 2, mergeKey: "value/1")); + fixture.history.RecordApplied("Change Value", NeutralValueHistoryHandler.CreateChange(1, 1, 2, mergeKey: "value/1")); NeutralValueHistoryHandler.SetValue(slot: 1, value: 3); - fixture.history.RecordApplied( - "Change Value", - NeutralValueHistoryHandler.CreateChange(1, 2, 3, mergeKey: "value/1")); - + fixture.history.RecordApplied("Change Value", NeutralValueHistoryHandler.CreateChange(1, 2, 3, mergeKey: "value/1")); Assert.True(fixture.history.Undo().succeeded); Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); Assert.False(fixture.history.canUndo); @@ -85,12 +140,8 @@ public void TransactionCommitsAndRevertsAtomically() using var fixture = new HistoryFixture(); using (EditorHistoryTransaction transaction = fixture.history.BeginTransaction("Batch")) { - Assert.True(fixture.history.Execute( - "First", - NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); - Assert.True(fixture.history.Execute( - "Second", - NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("First", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("Second", NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); transaction.Commit(); } @@ -107,16 +158,10 @@ public void TransactionCommitsAndRevertsAtomically() public void RecordingANewMutationReleasesTheRedoBranch() { using var fixture = new HistoryFixture(); - Assert.True(fixture.history.Execute( - "First", - NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("First", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); Assert.True(fixture.history.Undo().succeeded); Assert.True(fixture.history.canRedo); - - Assert.True(fixture.history.Execute( - "Replacement", - NeutralValueHistoryHandler.CreateChange(1, 0, 2)).succeeded); - + Assert.True(fixture.history.Execute("Replacement", NeutralValueHistoryHandler.CreateChange(1, 0, 2)).succeeded); Assert.False(fixture.history.canRedo); Assert.Equal(2, NeutralValueHistoryHandler.GetValue(1)); } @@ -125,18 +170,13 @@ public void RecordingANewMutationReleasesTheRedoBranch() public void IsolatedBranchIsReleasedAndRestoresTheEditingStacks() { using var fixture = new HistoryFixture(); - Assert.True(fixture.history.Execute( - "Edit Value", - NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("Edit Value", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); Assert.True(fixture.history.Undo().succeeded); - using (fixture.runtime.interactions.BeginHistoryIsolation()) { Assert.False(fixture.history.canUndo); Assert.False(fixture.history.canRedo); - Assert.True(fixture.history.Execute( - "Runtime Value", - NeutralValueHistoryHandler.CreateChange(1, 0, 2)).succeeded); + Assert.True(fixture.history.Execute("Runtime Value", NeutralValueHistoryHandler.CreateChange(1, 0, 2)).succeeded); Assert.Equal("Runtime Value", fixture.history.undoName); } @@ -152,16 +192,10 @@ public void FailedTransactionRollbackCanBeRetriedWithoutPartialState() { using var fixture = new HistoryFixture(); EditorHistoryTransaction transaction = fixture.history.BeginTransaction("Batch"); - Assert.True(fixture.history.Execute( - "First", - NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); - Assert.True(fixture.history.Execute( - "Second", - NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("First", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("Second", NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); NeutralValueHistoryHandler.Block(slot: 1, EditorHistoryDirection.Undo); - EditorHistoryResult firstAttempt = transaction.Rollback(); - Assert.False(firstAttempt.succeeded); Assert.True(firstAttempt.statePreserved); Assert.Equal(1, NeutralValueHistoryHandler.GetValue(1)); @@ -178,17 +212,11 @@ public void CompensationFailureFaultsHistoryAndRejectsFurtherTransactions() { using var fixture = new HistoryFixture(); EditorHistoryTransaction transaction = fixture.history.BeginTransaction("Batch"); - Assert.True(fixture.history.Execute( - "First", - NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); - Assert.True(fixture.history.Execute( - "Second", - NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("First", NeutralValueHistoryHandler.CreateChange(1, 0, 1)).succeeded); + Assert.True(fixture.history.Execute("Second", NeutralValueHistoryHandler.CreateChange(2, 0, 1)).succeeded); NeutralValueHistoryHandler.Block(slot: 1, EditorHistoryDirection.Undo); NeutralValueHistoryHandler.Block(slot: 2, EditorHistoryDirection.Redo); - EditorHistoryResult result = transaction.Rollback(); - Assert.False(result.succeeded); Assert.False(result.statePreserved); Assert.True(fixture.history.isFaulted); @@ -202,12 +230,7 @@ public void CompensationFailureFaultsHistoryAndRejectsFurtherTransactions() public void MissingHandlerCreatesAnExplicitAvailabilityBarrier() { using var fixture = new HistoryFixture(); - fixture.history.RecordApplied( - "Unavailable Change", - new EditorHistoryChange( - "tests/missing-history-handler", - EditorHistoryPayload.FromBytes([1, 2, 3]))); - + fixture.history.RecordApplied("Unavailable Change", new EditorHistoryChange("tests/missing-history-handler", EditorHistoryPayload.FromBytes([1, 2, 3]))); Assert.False(fixture.history.canUndo); Assert.Contains("tests/missing-history-handler", fixture.history.undoUnavailableReason); Assert.False(fixture.history.Undo().succeeded); @@ -223,12 +246,7 @@ public void LargePayloadUsesTheBoundedSessionDiskStore() BitConverter.GetBytes(9).CopyTo(payload, sizeof(int)); BitConverter.GetBytes(32).CopyTo(payload, sizeof(int) * 2); NeutralValueHistoryHandler.SetValue(slot: 1, value: 32); - fixture.history.RecordApplied( - "Large Neutral Change", - new EditorHistoryChange( - NeutralValueHistoryHandler.KIND, - EditorHistoryPayload.FromBytes(payload))); - + fixture.history.RecordApplied("Large Neutral Change", new EditorHistoryChange(NeutralValueHistoryHandler.KIND, EditorHistoryPayload.FromBytes(payload))); Assert.Equal(0, fixture.history.residentBytes); Assert.Equal(payload.LongLength, fixture.history.diskBytes); Assert.True(fixture.history.Undo().succeeded); @@ -240,13 +258,9 @@ public void NeutralHistorySurvivesATypeCatalogGenerationChange() { using var fixture = new HistoryFixture(); NeutralValueHistoryHandler.SetValue(slot: 1, value: 14); - fixture.history.RecordApplied( - "Change Neutral Value", - NeutralValueHistoryHandler.CreateChange(1, 3, 14)); - + fixture.history.RecordApplied("Change Neutral Value", NeutralValueHistoryHandler.CreateChange(1, 3, 14)); fixture.types.Rebuild(); _ = fixture.runtime.panelCount; - Assert.True(fixture.history.Undo().succeeded); Assert.Equal(3, NeutralValueHistoryHandler.GetValue(1)); Assert.True(fixture.history.Redo().succeeded); @@ -255,32 +269,26 @@ public void NeutralHistorySurvivesATypeCatalogGenerationChange() private sealed class HistoryFixture : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoEditorHistoryTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoEditorHistoryTests", Guid.NewGuid().ToString("N")); private readonly ModuleHost m_modules; private readonly LogRouter m_logs = new(); - internal HistoryFixture() { Directory.CreateDirectory(Path.Combine(m_projectRoot, "Assets")); NeutralValueHistoryHandler.Reset(); - m_modules = new ModuleHost(new ModuleHostOptions - { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorHistoryTests).Assembly) }); + m_modules = new ModuleHost(new ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorHistoryTests).Assembly) }); types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); runtime = new EditorInteractionRuntime( new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), types, m_logs, - [types]); + [types] + ); runtime.Start(); } internal TypeCatalog types { get; } - internal EditorInteractionRuntime runtime { get; } - internal IEditorHistory history => runtime.interactions.history; public void Dispose() @@ -299,10 +307,8 @@ public void Dispose() public sealed class NeutralValueHistoryHandler : EditorHistoryHandler { public const string KIND = "tests/neutral-values"; - private static readonly HashSet<(int Slot, EditorHistoryDirection Direction)> S_BLOCKED = []; private static readonly Dictionary S_VALUES = []; - public static EditorHistoryChange CreateChange( int slot, int before, @@ -316,27 +322,19 @@ public static EditorHistoryChange CreateChange( return new EditorHistoryChange(KIND, EditorHistoryPayload.FromBytes(bytes), mergeKey); } - public static int GetValue(int slot) - => S_VALUES.TryGetValue(slot, out int value) ? value : 0; - + public static int GetValue(int slot) => S_VALUES.TryGetValue(slot, out int value) ? value : 0; public static void SetValue( int slot, int value - ) - => S_VALUES[slot] = value; - + ) => S_VALUES[slot] = value; public static void Block( int slot, EditorHistoryDirection direction - ) - => S_BLOCKED.Add((slot, direction)); - + ) => S_BLOCKED.Add((slot, direction)); public static void Unblock( int slot, EditorHistoryDirection direction - ) - => S_BLOCKED.Remove((slot, direction)); - + ) => S_BLOCKED.Remove((slot, direction)); public static void Reset() { S_BLOCKED.Clear(); @@ -347,11 +345,7 @@ protected override EditorHistoryAvailability Query( EditorHistoryContext context, EditorHistoryChange change, EditorHistoryDirection direction - ) - => change.payload.length >= sizeof(int) * 3 - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable("The neutral value payload is truncated."); - + ) => change.payload.length >= sizeof(int) * 3 ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable("The neutral value payload is truncated."); protected override EditorHistoryResult Apply( EditorHistoryContext context, EditorHistoryChange change, @@ -361,9 +355,7 @@ EditorHistoryDirection direction int slot = BitConverter.ToInt32(bytes, 0); if (S_BLOCKED.Contains((slot, direction))) return EditorHistoryResult.Failure($"Slot {slot} is blocked for {direction}."); - int offset = direction == EditorHistoryDirection.Undo - ? sizeof(int) - : sizeof(int) * 2; + int offset = direction == EditorHistoryDirection.Undo ? sizeof(int) : sizeof(int) * 2; S_VALUES[slot] = BitConverter.ToInt32(bytes, offset); return EditorHistoryResult.Success(); } @@ -382,11 +374,7 @@ out EditorHistoryChange? merged return false; } - merged = CreateChange( - slot, - BitConverter.ToInt32(olderBytes, sizeof(int)), - BitConverter.ToInt32(newerBytes, sizeof(int) * 2), - older.mergeKey); + merged = CreateChange(slot, BitConverter.ToInt32(olderBytes, sizeof(int)), BitConverter.ToInt32(newerBytes, sizeof(int) * 2), older.mergeKey); return true; } } diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs new file mode 100644 index 000000000..19de0c708 --- /dev/null +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Inno.Core.Identity; +using Inno.Editor.Core; +using Inno.Editor.Interactions; +using Xunit; + +namespace Inno.Editor.Interactions.Tests; +public sealed partial class EditorRuntimeTests +{ + [Fact] + public void RejectedDomainRetirementOnAnotherThreadPreservesTheRoutingLease() + { + var allocator = new IdentityAllocator(); + var target = new SelectionQueueTarget(); + Assert.True(allocator.Register(target)); + using IDisposable registration = m_runtime.interactions.RegisterIdentityDomain(allocator); + Exception? failure = null; + var worker = new System.Threading.Thread(() => failure = Record.Exception(registration.Dispose)); + worker.Start(); + Assert.True(worker.Join(TimeSpan.FromSeconds(5))); + Assert.IsType(failure); + Assert.True(m_runtime.interactions.TryResolveIdentity(target.identity.runtimeIdentity!.Value, out IdentityObject? resolved)); + Assert.Same(target, resolved); + } + + [Fact] + public void TemporaryIdentityDomainsResolveSelectionAndDragOnlyWhileTheirRoutingLeaseIsAlive() + { + var allocator = new IdentityAllocator(); + var target = new SelectionQueueTarget(); + Assert.True(allocator.Register(target)); + RuntimeIdentity identity = target.identity.runtimeIdentity!.Value; + Assert.False(m_runtime.interactions.TryResolveIdentity(identity, out _)); + IDisposable registration = m_runtime.interactions.RegisterIdentityDomain(allocator); + Assert.Throws(() => m_runtime.interactions.RegisterIdentityDomain(allocator)); + Assert.True(m_runtime.interactions.TryResolveIdentity(identity, out IdentityObject? resolved)); + Assert.Same(target, resolved); + m_runtime.interactions.SetSelection(target); + Assert.Same(target, m_runtime.interactions.selection.selectedTarget); + RuntimeIdentity drag = m_runtime.interactions.For("tests/temporary", target).BeginDrag(new EditorDragData(target, "temporary")); + Assert.True(m_runtime.interactions.TryGetDragSource(drag, out _)); + registration.Dispose(); + registration.Dispose(); + Assert.False(m_runtime.interactions.TryResolveIdentity(identity, out _)); + Assert.False(m_runtime.interactions.TryGetDragSource(drag, out _)); + Assert.Null(m_runtime.interactions.selection.selectedTarget); + Assert.Equal(1, m_runtime.interactions.selection.count); + Assert.NotNull(allocator.Get(identity)); + using IDisposable rejoined = m_runtime.interactions.RegisterIdentityDomain(allocator); + registration.Dispose(); + Assert.Same(target, m_runtime.interactions.selection.selectedTarget); + Assert.False(m_runtime.interactions.TryGetActiveDragIdentity(out _)); + } + + [Fact] + public void SelectionFinishesPendingEditsBeforeChangingAndPreservesStateWhenPreparationRejects() + { + var first = new SelectionQueueTarget(); + var second = new SelectionQueueTarget(); + Assert.True(m_identities.Register(first)); + Assert.True(m_identities.Register(second)); + m_runtime.interactions.SetSelection(first); + using var hub = m_runtime.interactions.events.CreateHub(); + using var gesture = new EditorEditGesture(m_runtime.interactions.history, "Pending Input"); + using (hub.Listen(_ => + { + Assert.Same(first, m_runtime.interactions.selection.selectedTarget); + gesture.Commit(); + })) + { + m_runtime.interactions.SetSelection(second); + } + Assert.False(gesture.isActive); + ulong revision = m_runtime.interactions.selection.version; + using var rejecting = hub.Listen(_ => throw new InvalidOperationException("Pending edit cannot finish.")); + Assert.Throws(() => m_runtime.interactions.SetSelection(first)); + Assert.Throws(() => m_runtime.interactions.SetSelection([first], first)); + Assert.Same(second, m_runtime.interactions.selection.selectedTarget); + Assert.Equal(revision, m_runtime.interactions.selection.version); + } + + [Fact] + public void OrderedSelectionDeduplicatesIdentitiesAndRetainsItsSnapshotUntilItChanges() + { + var first = new SelectionQueueTarget(); + var second = new SelectionQueueTarget(); + Assert.True(m_identities.Register(first)); + Assert.True(m_identities.Register(second)); + m_runtime.interactions.SetSelection([first, second, first], first); + Assert.Equal(2, m_runtime.interactions.selection.count); + Assert.Same(second, m_runtime.interactions.selection.selectedTarget); + Assert.Same(first, m_runtime.interactions.selection.anchor); + EditorSelectionSnapshot snapshot = m_runtime.interactions.selection.Capture(); + Assert.Equal(first.identity.runtimeIdentity, snapshot.identities[0]); + Assert.Equal(second.identity.runtimeIdentity, snapshot.primary); + ulong revision = m_runtime.interactions.selection.version; + m_runtime.interactions.SetSelection([first, second], first); + Assert.Equal(revision, m_runtime.interactions.selection.version); + Assert.Same(snapshot, m_runtime.interactions.selection.Capture()); + } + + [Fact] + public void RangeSelectionUsesVisibleOrderAndTogglePreservesTheAnchor() + { + var first = new SelectionQueueTarget(); + var second = new SelectionQueueTarget(); + var third = new SelectionQueueTarget(); + Assert.True(m_identities.Register(first)); + Assert.True(m_identities.Register(second)); + Assert.True(m_identities.Register(third)); + m_runtime.interactions.SetSelection(first); + m_runtime.interactions.SelectRange([first, second, third], third); + Assert.Equal(3, m_runtime.interactions.selection.count); + m_runtime.interactions.ToggleSelection(second); + Assert.False(m_runtime.interactions.selection.Contains(second)); + Assert.Same(first, m_runtime.interactions.selection.anchor); + Assert.Same(third, m_runtime.interactions.selection.selectedTarget); + m_runtime.interactions.SelectRange([third, second, first], second); + var targets = new List(); + Assert.Equal(2, m_runtime.interactions.selection.CopyTargets(targets)); + Assert.Equal([first, second], targets); + } + + [Fact] + public void SelectionNeverConflatesPersistentIdentitiesFromDifferentDomains() + { + Guid persistent = Guid.NewGuid(); + var first = new SelectionQueueTarget(); + var second = new SelectionQueueTarget(); + Assert.True(m_identities.Register(first, persistent)); + Assert.True(m_secondaryIdentities.Register(second, persistent)); + m_runtime.interactions.SetSelection([first, second], first); + Assert.Equal(2, m_runtime.interactions.selection.count); + Assert.True(m_runtime.interactions.selection.Contains(first)); + Assert.True(m_runtime.interactions.selection.Contains(second)); + Assert.NotEqual(first.identity.runtimeIdentity, second.identity.runtimeIdentity); + } + + [Fact] + public void RejectedSelectionDoesNotModifyItsPreviousMembershipOrRevision() + { + var registered = new SelectionQueueTarget(); + Assert.True(m_identities.Register(registered)); + m_runtime.interactions.SetSelection(registered); + ulong revision = m_runtime.interactions.selection.version; + var unregistered = new SelectionQueueTarget(); + Assert.Throws(() => m_runtime.interactions.SetSelection([registered, unregistered], registered)); + Assert.Equal(revision, m_runtime.interactions.selection.version); + Assert.Same(registered, m_runtime.interactions.selection.selectedTarget); + } + + [Fact] + public void QueuedActionsRejectRetiredTargetsInsteadOfExecutingTheirReplacement() + { + Guid persistent = Guid.NewGuid(); + var previous = new SelectionQueueTarget(); + Assert.True(m_identities.Register(previous, persistent)); + m_runtime.interactions.For("tests/identity-queue", previous).Enqueue("tests/identity-queue"); + Assert.True(m_identities.Unregister(previous)); + var replacement = new SelectionQueueTarget(); + Assert.True(m_identities.Register(replacement, persistent)); + m_runtime.Update(new EditorFrame()); + Assert.Equal(0, previous.executions); + Assert.Equal(0, replacement.executions); + m_runtime.interactions.For("tests/identity-queue", replacement).Enqueue("tests/identity-queue"); + m_runtime.Update(new EditorFrame()); + Assert.Equal(1, replacement.executions); + } + + [Fact] + public void QueuedSelectionAndFocusDoNotKeepRetiredIdentityObjectsAlive() + { + WeakReference reference = QueueAndRetireSelectionTarget(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(reference.IsAlive); + Assert.Null(m_runtime.interactions.selection.selectedTarget); + Assert.Null(m_runtime.interactions.focusedTarget); + m_runtime.Update(new EditorFrame()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private WeakReference QueueAndRetireSelectionTarget() + { + var target = new SelectionQueueTarget(); + Assert.True(m_identities.Register(target)); + m_runtime.interactions.SetSelection(target); + m_runtime.interactions.For("tests/identity-queue", target).Focus(); + m_runtime.interactions.For("tests/identity-queue", target).Enqueue("tests/identity-queue"); + Assert.True(m_identities.Unregister(target)); + return new WeakReference(target); + } + + public sealed class SelectionQueueTarget : IdentityObject + { + public int executions { get; set; } + } + + [EditorAction("tests/identity-queue")] + public sealed class IdentityQueuedAction : EditorAction + { + protected override void Execute(EditorActionContext context) => context.target.executions++; + } +} diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.cs index a7e755359..7ab18aa73 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Reflection; - using Inno.Extensibility.Modules; using Inno.Extensibility.Reload; using Inno.Core.Diagnostics; @@ -23,13 +22,9 @@ using Xunit; namespace Inno.Editor.Interactions.Tests; - -public sealed class EditorRuntimeTests : IDisposable +public sealed partial class EditorRuntimeTests : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoEditorRuntimeTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoEditorRuntimeTests", Guid.NewGuid().ToString("N")); private readonly DiagnosticHub m_diagnostics = new(); private readonly LogRouter m_logs = new(); private readonly IdentityAllocator m_identities = new(); @@ -38,15 +33,12 @@ public sealed class EditorRuntimeTests : IDisposable private readonly ModuleHost m_modules; private readonly TypeCatalog m_types; private readonly EditorInteractionRuntime m_runtime; - public EditorRuntimeTests() { Directory.CreateDirectory(Path.Combine(m_projectRoot, "Assets")); m_diagnosticScope = m_diagnostics.EnterScope(); - m_modules = new ModuleHost(new ModuleHostOptions - { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorRuntimeTests).Assembly) }); + m_modules = new ModuleHost(new ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorRuntimeTests).Assembly) }); m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); - TestModule.startCount = 0; TestModule.stopCount = 0; TestModule.stateValue = 0; @@ -81,7 +73,6 @@ public EditorRuntimeTests() ZThrowingDisposeModule.disposeCount = 0; ZThrowingDisposeModule.throwOnDispose = false; ShutdownOrder.events.Clear(); - m_runtime = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super"))); m_runtime.Start(); } @@ -100,9 +91,7 @@ public void Dispose() private EditorInteractionRuntime CreateRuntime( EditorContext context, params object[] hostServices - ) - => new(context, m_types, m_logs, [m_types, m_identities, m_secondaryIdentities, .. hostServices]); - + ) => new(context, m_types, m_logs, [m_types, m_identities, m_secondaryIdentities, ..hostServices]); [Fact] public void FeatureLookupReturnsOnlyStartedNonQuarantinedModules() { @@ -125,9 +114,7 @@ public void HistoryAndExtensionStateContractsDoNotExposeStandaloneWorkspaceTypes Assert.Null(core.GetType("Inno.Editor.Core.EditorWorkspaceStateWriter")); Assert.Null(typeof(EditorHistoryChange).GetProperty("version")); ConstructorInfo constructor = Assert.Single(typeof(EditorHistoryHandlerAttribute).GetConstructors()); - Assert.Collection( - constructor.GetParameters(), - static parameter => Assert.Equal(typeof(string), parameter.ParameterType)); + Assert.Collection(constructor.GetParameters(), static parameter => Assert.Equal(typeof(string), parameter.ParameterType)); } [Fact] @@ -135,7 +122,6 @@ public void EditorDensityDefaultsToComfortableAndCanSwitchIdempotently() { var style = new EditorStyleMetrics(); float comfortablePadding = style.framePadding.Y; - Assert.False(style.isCompact); Assert.True(style.SetCompactMode(true)); Assert.True(style.isCompact); @@ -151,12 +137,8 @@ public void InteractionRuntimeInjectsOneStableAssignableHostService() { var service = new TestHostService(); HostServicePanel.current = null; - using EditorInteractionRuntime runtime = CreateRuntime( - new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), - service); - + using EditorInteractionRuntime runtime = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), service); runtime.Start(); - Assert.Contains(runtime.panels, static panel => panel.id == "tests.host-service"); Assert.Same(service, HostServicePanel.current); } @@ -166,9 +148,7 @@ public void InteractionRuntimeRejectsExtensionWhenHostServiceIsUnavailable() { HostServicePanel.current = null; using EditorInteractionRuntime runtime = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super"))); - runtime.Start(); - Assert.DoesNotContain(runtime.panels, static panel => panel.id == "tests.host-service"); Assert.Null(HostServicePanel.current); } @@ -185,8 +165,7 @@ public void InteractionIdentifiersArePlainValidatedStringsWithoutWrapperTypes() Assert.Equal(typeof(string), typeof(EditorInteractions).GetProperty("focusedArea")!.PropertyType); Assert.Equal(typeof(string), typeof(EditorInteraction).GetProperty("area")!.PropertyType); Assert.Equal(typeof(string), typeof(EditorPanelExtension).GetProperty("id")!.PropertyType); - MethodInfo forMethod = Assert.Single(typeof(EditorInteractions).GetMethods() - .Where(static method => method.Name == "For")); + MethodInfo forMethod = Assert.Single(typeof(EditorInteractions).GetMethods().Where(static method => method.Name == "For")); Assert.Equal(typeof(string), forMethod.GetParameters()[0].ParameterType); Assert.Throws(() => m_runtime.interactions.For(string.Empty)); Assert.Throws(() => m_runtime.interactions.TogglePanel(" ")); @@ -197,16 +176,13 @@ public void InteractionIdentifiersArePlainValidatedStringsWithoutWrapperTypes() [Fact] public void PanelOpenAndCloseOperationsAreIdempotentAndRequestFocus() { - EditorPanelExtension panel = Assert.Single( - m_runtime.panels.Where(static candidate => candidate.id == "tests.panel")); + EditorPanelExtension panel = Assert.Single(m_runtime.panels.Where(static candidate => candidate.id == "tests.panel")); Assert.False(panel.isOpen); - Assert.True(m_runtime.interactions.OpenPanel("tests.panel")); Assert.True(m_runtime.interactions.OpenPanel("tests.panel")); Assert.True(panel.isOpen); Assert.True(panel.TakeFocusRequest()); Assert.False(panel.TakeFocusRequest()); - Assert.True(m_runtime.interactions.ClosePanel("tests.panel")); Assert.True(m_runtime.interactions.ClosePanel("tests.panel")); Assert.False(panel.isOpen); @@ -221,11 +197,9 @@ public void DocumentHostOpensOneInstanceAndRequiresExplicitDirtyCloseDecision() var provider = new TestDocumentProvider(); using IDisposable lease = documents.RegisterProvider(provider); Guid assetId = Guid.NewGuid(); - EditorDocumentContext opened = documents.Open("./Assets/Hero.ispriteatlas2d", assetId); EditorDocumentContext focused = documents.Open("Assets/OtherName.ispriteatlas2d", assetId); documents.SetDirty(opened.documentId); - Assert.Same(opened, focused); Assert.Equal("Assets/OtherName.ispriteatlas2d", opened.assetPath); Assert.Equal("OtherName.ispriteatlas2d", opened.title); @@ -245,17 +219,13 @@ public void DocumentPathReassignmentRetiresOnlyCleanStaleOwner() var provider = new TestDocumentProvider(); using IDisposable lease = documents.RegisterProvider(provider); const string path = "Assets/Shared.ispriteatlas2d"; - EditorDocumentContext first = documents.Open(path, Guid.NewGuid()); EditorDocumentContext replacement = documents.Open(path, Guid.NewGuid()); - Assert.NotSame(first, replacement); Assert.Single(documents.documents); Assert.Equal(1, provider.closeCount); - documents.SetDirty(replacement.documentId); - InvalidOperationException failure = Assert.Throws( - () => documents.Open(path, Guid.NewGuid())); + InvalidOperationException failure = Assert.Throws(() => documents.Open(path, Guid.NewGuid())); Assert.Contains("unsaved changes", failure.Message, StringComparison.Ordinal); Assert.Same(replacement, Assert.Single(documents.documents)); } @@ -269,15 +239,11 @@ public void DocumentStateSurvivesProviderGenerationReplacement() EditorDocumentContext opened = documents.Open("Assets/World.itilemap2d", Guid.NewGuid()); opened.SetViewParameter("zoom", "2.5"); documents.SetDirty(opened.documentId); - firstLease.Dispose(); - Assert.False(opened.isProviderAvailable); Assert.False(documents.Save(opened.documentId)); - var replacement = new TestDocumentProvider(); using IDisposable replacementLease = documents.RegisterProvider(replacement); - Assert.True(opened.isProviderAvailable); Assert.Equal(1, replacement.openCount); EditorDocumentContext restored = Assert.Single(documents.documents); @@ -300,24 +266,11 @@ public void ViewportToolCapturesOnePointerAndCommitsOneGestureTransaction() new Inno.Core.Mathematics.Vector2(2f, 3f), coordinates.ScreenToWorld(new Inno.Core.Mathematics.Vector2(2f, 3f)), 0, - KeyModifier.None); - + KeyModifier.None + ); Assert.True(session.HandlePointer(down)); - Assert.False(session.HandlePointer(new EditorViewportPointerEvent( - 8, - EditorViewportPointerPhase.Move, - default, - default, - 0, - KeyModifier.None))); - Assert.True(session.HandlePointer(new EditorViewportPointerEvent( - 7, - EditorViewportPointerPhase.Up, - down.screenPosition, - down.worldPosition, - 0, - KeyModifier.None))); - + Assert.False(session.HandlePointer(new EditorViewportPointerEvent(8, EditorViewportPointerPhase.Move, default, default, 0, KeyModifier.None))); + Assert.True(session.HandlePointer(new EditorViewportPointerEvent(7, EditorViewportPointerPhase.Up, down.screenPosition, down.worldPosition, 0, KeyModifier.None))); Assert.Equal(1, tool.downCount); Assert.Equal(1, tool.upCount); Assert.True(history.canUndo); @@ -341,24 +294,11 @@ public void StatisticsUseStableReplacementAndOneFrameOrderHandoff() EditorStatistics statistics = m_runtime.context.statistics; var id = new EditorStatisticId("tests.statistics.frame-time"); var groupId = new EditorStatisticGroupId("tests.statistics"); - statistics.Publish(new EditorStatistic( - id, - groupId, - "Tests", - "Frame Time", - "10 ms")); - statistics.Publish(new EditorStatistic( - id, - groupId, - "Tests", - "Frame Time", - "8 ms")); - + statistics.Publish(new EditorStatistic(id, groupId, "Tests", "Frame Time", "10 ms")); + statistics.Publish(new EditorStatistic(id, groupId, "Tests", "Frame Time", "8 ms")); Assert.Equal("8 ms", Assert.Single(statistics.GetSnapshot()).value); - m_runtime.Update(new EditorFrame(0.016f, 0.016f, isFocused: true)); Assert.Equal("8 ms", Assert.Single(statistics.GetSnapshot()).value); - m_runtime.Update(new EditorFrame(0.016f, 0.032f, isFocused: true)); Assert.Empty(statistics.GetSnapshot()); } @@ -367,14 +307,10 @@ public void StatisticsUseStableReplacementAndOneFrameOrderHandoff() public void BlockingModuleDefersOnlyModulesOrderedAfterIt() { UpdateBarrierModule.block = true; - m_runtime.Update(new EditorFrame(0.016f, 0.016f, isFocused: true)); - Assert.Equal(0, FollowingUpdateModule.updateCount); UpdateBarrierModule.block = false; - m_runtime.Update(new EditorFrame(0.016f, 0.032f, isFocused: true)); - Assert.Equal(1, FollowingUpdateModule.updateCount); } @@ -382,10 +318,8 @@ public void BlockingModuleDefersOnlyModulesOrderedAfterIt() public void ThrowingModuleBarrierIsQuarantinedWithoutStoppingFollowingModules() { UpdateBarrierModule.throwWhenRead = true; - m_runtime.Update(new EditorFrame(0.016f, 0.016f, isFocused: true)); m_runtime.Update(new EditorFrame(0.016f, 0.032f, isFocused: true)); - Assert.Equal(1, UpdateBarrierModule.barrierReadCount); Assert.Equal(2, FollowingUpdateModule.updateCount); } @@ -394,14 +328,8 @@ public void ThrowingModuleBarrierIsQuarantinedWithoutStoppingFollowingModules() public void ActionResolutionPrefersExactAreaAndTarget() { var target = new DerivedTarget(); - - EditorActionState exact = m_runtime.interactions - .For("tests/special", target) - .Query("tests.resolve"); - EditorActionState fallback = m_runtime.interactions - .For("tests/other", target) - .Query("tests.resolve"); - + EditorActionState exact = m_runtime.interactions.For("tests/special", target).Query("tests.resolve"); + EditorActionState fallback = m_runtime.interactions.For("tests/other", target).Query("tests.resolve"); Assert.Equal("area", exact.displayName); Assert.Equal("base", fallback.displayName); } @@ -413,10 +341,7 @@ public void ActionTypeMetadataIsCapturedOnceAndNeverReadOnTheRoutingPath() MetadataAction.throwWhenRead = true; try { - EditorInteraction interaction = m_runtime.interactions.For( - "tests/action-metadata", - new BaseTarget()); - + EditorInteraction interaction = m_runtime.interactions.For("tests/action-metadata", new BaseTarget()); Assert.True(interaction.Query("tests.action-metadata").isEnabled); Assert.True(interaction.Execute("tests.action-metadata")); Assert.True(interaction.Query("tests.action-metadata").isEnabled); @@ -434,9 +359,7 @@ public void QueuedActionExecutesAtRuntimeSafePoint() { m_runtime.interactions.For("tests/other").Enqueue("tests.deferred"); Assert.Equal(0, DeferredAction.executeCount); - m_runtime.Update(new EditorFrame(0.016f, 1f, isFocused: true)); - Assert.Equal(1, DeferredAction.executeCount); } @@ -444,17 +367,13 @@ public void QueuedActionExecutesAtRuntimeSafePoint() public void BuiltInUndoAndRedoActionsExposeHistoryThroughMenusAndShortcuts() { NeutralHistoryHandler.value = 1; - m_runtime.interactions.history.RecordApplied( - "Change Test Value", - NeutralHistoryHandler.CreateChange(before: 0, after: 1)); + m_runtime.interactions.history.RecordApplied("Change Test Value", NeutralHistoryHandler.CreateChange(before: 0, after: 1)); EditorInteraction global = m_runtime.interactions.For("editor/global"); - EditorActionState undo = global.Query("editor/undo"); Assert.True(undo.isEnabled); Assert.Equal("Undo Change Test Value", undo.displayName); Assert.True(global.Execute("editor/undo")); Assert.Equal(0, NeutralHistoryHandler.value); - EditorActionState redo = global.Query("editor/redo"); Assert.True(redo.isEnabled); Assert.Equal("Redo Change Test Value", redo.displayName); @@ -466,13 +385,9 @@ public void BuiltInUndoAndRedoActionsExposeHistoryThroughMenusAndShortcuts() public void NeutralHistorySurvivesATypeCatalogGenerationChange() { NeutralHistoryHandler.value = 14; - m_runtime.interactions.history.RecordApplied( - "Change Neutral Value", - NeutralHistoryHandler.CreateChange(before: 3, after: 14)); - + m_runtime.interactions.history.RecordApplied("Change Neutral Value", NeutralHistoryHandler.CreateChange(before: 3, after: 14)); m_types.Rebuild(); _ = m_runtime.panelCount; - Assert.True(m_runtime.interactions.history.canUndo); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(3, NeutralHistoryHandler.value); @@ -483,12 +398,7 @@ public void NeutralHistorySurvivesATypeCatalogGenerationChange() [Fact] public void MissingHistoryHandlerCreatesAnExplicitBarrier() { - m_runtime.interactions.history.RecordApplied( - "Unavailable Change", - new EditorHistoryChange( - "tests/missing-history-handler", - EditorHistoryPayload.FromBytes([1, 2, 3]))); - + m_runtime.interactions.history.RecordApplied("Unavailable Change", new EditorHistoryChange("tests/missing-history-handler", EditorHistoryPayload.FromBytes([1, 2, 3]))); Assert.False(m_runtime.interactions.history.canUndo); Assert.Contains("tests/missing-history-handler", m_runtime.interactions.history.undoUnavailableReason); Assert.False(m_runtime.interactions.history.Undo().succeeded); @@ -502,12 +412,7 @@ public void LargeNeutralPayloadSpillsToTheBoundedSessionDiskStore() byte[] bytes = new byte[128 * 1024]; BitConverter.GetBytes(9).CopyTo(bytes, 0); BitConverter.GetBytes(32).CopyTo(bytes, sizeof(int)); - m_runtime.interactions.history.RecordApplied( - "Large Neutral Change", - new EditorHistoryChange( - NeutralHistoryHandler.KIND, - EditorHistoryPayload.FromBytes(bytes))); - + m_runtime.interactions.history.RecordApplied("Large Neutral Change", new EditorHistoryChange(NeutralHistoryHandler.KIND, EditorHistoryPayload.FromBytes(bytes))); Assert.Equal(0, m_runtime.interactions.history.residentBytes); Assert.Equal(bytes.LongLength, m_runtime.interactions.history.diskBytes); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -518,14 +423,11 @@ public void LargeNeutralPayloadSpillsToTheBoundedSessionDiskStore() public void AttributeMenusSupportArbitraryDepthAndDynamicEntries() { EditorMenuModel menu = m_runtime.interactions.For("tests/menu").BuildMenu(); - EditorMenuItem tools = Assert.Single(menu.items); Assert.Equal("Tools", tools.label); EditorMenuItem create = Assert.Single(tools.children); Assert.Equal("Create", create.label); - Assert.Equal( - ["Asset", "Libraries", "Generated"], - create.children.Select(static item => item.label)); + Assert.Equal(["Asset", "Libraries", "Generated"], create.children.Select(static item => item.label)); EditorMenuItem libraries = Assert.Single(create.children.Where(static item => item.label == "Libraries")); Assert.True(libraries.separatorBefore); Assert.False(tools.separatorBefore); @@ -537,47 +439,36 @@ public void AttributeMenusSupportArbitraryDepthAndDynamicEntries() public void ToolbarResolvesContextualIconTooltipStateAndDispatch() { EditorInteraction interaction = m_runtime.interactions.For("tests/toolbar"); - EditorToolbarItem initial = Assert.Single(interaction.BuildToolbar().items); Assert.Equal("tests.toolbar", initial.actionId); Assert.Equal(EditorToolbarIcon.Play, initial.icon); Assert.Equal("Start Runtime", initial.tooltip); Assert.False(initial.status.isChecked); - ToolbarAction.isChecked = true; EditorToolbarItem active = Assert.Single(interaction.BuildToolbar().items); Assert.Equal(EditorToolbarIcon.Stop, active.icon); Assert.Equal("Stop Runtime", active.tooltip); Assert.True(active.status.isChecked); - interaction.Enqueue(active.actionId); m_runtime.Update(new EditorFrame(0.016f, 1f, isFocused: true)); Assert.Equal(1, ToolbarAction.executeCount); } [Fact] - public void MainMenuPlacesGeneratedPanelTogglesUnderPanel() + public void WorkbenchPlacesPanelTogglesUnderWindow() { - EditorMenuModel menu = m_runtime.interactions - .For("editor/main-menu") - .BuildMenu(); - - EditorMenuItem panel = Assert.Single(menu.items.Where(static item => item.label == "Panel")); + EditorMenuModel menu = m_runtime.interactions.For("editor/main-menu").BuildMenu(); + EditorMenuItem panel = Assert.Single(menu.items.Where(static item => item.label == "Window")); EditorMenuItem testing = Assert.Single(panel.children.Where(static item => item.label == "Testing")); EditorMenuItem test = Assert.Single(testing.children.Where(static item => item.label == "Test")); Assert.False(test.status.isChecked); EditorMenuItem? view = menu.items.SingleOrDefault(static item => item.label == "View"); Assert.True(view is null || view.children.All(static item => item.label != "Test")); - Assert.True(m_runtime.interactions.TogglePanel("tests.panel")); - EditorMenuModel openMenu = m_runtime.interactions - .For("editor/main-menu") - .BuildMenu(); - EditorMenuItem openPanel = Assert.Single(openMenu.items.Where(static item => item.label == "Panel")); - EditorMenuItem openTesting = Assert.Single( - openPanel.children.Where(static item => item.label == "Testing")); - EditorMenuItem openTest = Assert.Single( - openTesting.children.Where(static item => item.label == "Test")); + EditorMenuModel openMenu = m_runtime.interactions.For("editor/main-menu").BuildMenu(); + EditorMenuItem openPanel = Assert.Single(openMenu.items.Where(static item => item.label == "Window")); + EditorMenuItem openTesting = Assert.Single(openPanel.children.Where(static item => item.label == "Testing")); + EditorMenuItem openTest = Assert.Single(openTesting.children.Where(static item => item.label == "Test")); Assert.True(openTest.status.isChecked); } @@ -585,25 +476,20 @@ public void MainMenuPlacesGeneratedPanelTogglesUnderPanel() public void EveryShortcutDeclaredByTheSameActionCanDispatch() { m_runtime.interactions.For("tests/multiple-shortcuts").Focus(); - m_runtime.HandleKeyPressed(new KeyPressedEvent(0, KeyCode.F3, KeyModifier.None)); m_runtime.HandleKeyPressed(new KeyPressedEvent(0, KeyCode.F4, KeyModifier.None)); - Assert.Equal(2, MultiShortcutAction.executeCount); } [Fact] public void ProductKeyboardPolicyControlsDiscoveryLabelsAndShortcutDispatch() { - using var runtime = CreateRuntime(new EditorContext(m_projectRoot, - new EditorKeyboardPolicy(KeyModifier.Super, "Cmd"))); + using var runtime = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(KeyModifier.Super, "Cmd"))); runtime.Start(); NeutralHistoryHandler.value = 1; - runtime.interactions.history.RecordApplied("Change Test Value", - NeutralHistoryHandler.CreateChange(before: 0, after: 1)); + runtime.interactions.history.RecordApplied("Change Test Value", NeutralHistoryHandler.CreateChange(before: 0, after: 1)); EditorInteraction global = runtime.interactions.For("editor/global"); global.Focus(); - Assert.True(global.TryGetShortcut("editor/undo", out HotKeyGesture gesture)); Assert.Equal(KeyModifier.Super, gesture.modifiers); Assert.Equal("Cmd+Z", gesture.ToString()); @@ -618,9 +504,7 @@ public void ProductKeyboardPolicyControlsDiscoveryLabelsAndShortcutDispatch() public void ActionExecutionFailureCancelsStateActivatedBeforeTheException() { EditorInteraction interaction = m_runtime.interactions.For("tests/throw-after-activate"); - Assert.False(interaction.Execute("tests.throw-after-activate")); - Assert.False(interaction.IsActive("tests.throw-after-activate")); Assert.Equal(1, ThrowAfterActivateAction.cancelCount); } @@ -630,20 +514,13 @@ public void ActionOwnsValidatedMultiFrameState() { var target = new InteractionTarget(); EditorInteraction interaction = m_runtime.interactions.For("tests/other", target); - Assert.True(interaction.Execute("tests.interaction")); Assert.True(interaction.IsActive("tests.interaction")); - - Assert.True(interaction.Present( - "tests.interaction", - new InteractionPresentation(string.Empty, submit: true))); + Assert.True(interaction.Present("tests.interaction", new InteractionPresentation(string.Empty, submit: true))); Assert.True(interaction.IsActive("tests.interaction")); Assert.Equal("A name is required.", target.validationMessage); Assert.Null(target.committedValue); - - Assert.True(interaction.Present( - "tests.interaction", - new InteractionPresentation("Renamed", submit: true))); + Assert.True(interaction.Present("tests.interaction", new InteractionPresentation("Renamed", submit: true))); Assert.Equal("Renamed", target.committedValue); Assert.False(interaction.IsActive("tests.interaction")); Assert.False(interaction.Present("tests.interaction")); @@ -656,9 +533,7 @@ public void ChangingSelectionFinishesThePreviousTargetsActivePresentation() EditorInteraction interaction = m_runtime.interactions.For("tests/other", target); Assert.True(interaction.Select()); Assert.True(interaction.Execute("tests.commit-on-presentation-lost")); - Assert.True(m_runtime.interactions.For("tests/other", new DerivedTarget()).Select()); - Assert.Equal("Committed on focus loss", target.committedValue); Assert.False(interaction.IsActive("tests.commit-on-presentation-lost")); } @@ -668,15 +543,12 @@ public void SelectionAndFocusUseTheLightweightAreaHandle() { var target = new DerivedTarget(); EditorInteraction interaction = m_runtime.interactions.For("tests/other", target); - interaction.Focus(); Assert.Equal("tests/other", m_runtime.interactions.focusedArea); Assert.Same(target, m_runtime.interactions.focusedTarget); - Assert.True(interaction.Select()); Assert.Same(target, m_runtime.interactions.selection.selectedTarget); Assert.True(interaction.isSelected); - Assert.True(m_runtime.interactions.For("tests/other").Select()); Assert.Null(m_runtime.interactions.selection.selectedTarget); Assert.Null(typeof(EditorSelectionState).GetMethod("Select")); @@ -691,30 +563,19 @@ public void IdentityResolutionUsesTheOwningDomain() Assert.True(m_secondaryIdentities.Register(secondary)); RuntimeIdentity primaryIdentity = primary.identity.runtimeIdentity!.Value; RuntimeIdentity secondaryIdentity = secondary.identity.runtimeIdentity!.Value; - Assert.True(m_runtime.interactions.TryResolveIdentity(primaryIdentity, out IdentityObject? primaryResult)); Assert.Same(primary, primaryResult); Assert.True(m_runtime.interactions.TryResolveIdentity(secondaryIdentity, out IdentityObject? secondaryResult)); Assert.Same(secondary, secondaryResult); - Assert.True(m_runtime.interactions.TryResolveIdentity( - secondaryIdentity.domainId, - secondary.identity.persistentId, - out IdentityObject? persistentResult)); + Assert.True(m_runtime.interactions.TryResolveIdentity(secondaryIdentity.domainId, secondary.identity.persistentId, out IdentityObject? persistentResult)); Assert.Same(secondary, persistentResult); - var crossDomainAlias = new RuntimeIdentity(secondaryIdentity.domainId, primaryIdentity.runtimeId); - Assert.NotSame(primary, m_runtime.interactions.TryResolveIdentity(crossDomainAlias, out IdentityObject? alias) - ? alias - : null); - + Assert.NotSame(primary, m_runtime.interactions.TryResolveIdentity(crossDomainAlias, out IdentityObject? alias) ? alias : null); Guid persistentId = secondary.identity.persistentId; Assert.True(m_secondaryIdentities.Unregister(secondary)); var replacement = new DragSource(); Assert.True(m_secondaryIdentities.Register(replacement, persistentId)); - Assert.True(m_runtime.interactions.TryResolveIdentity( - secondaryIdentity.domainId, - persistentId, - out IdentityObject? replacementResult)); + Assert.True(m_runtime.interactions.TryResolveIdentity(secondaryIdentity.domainId, persistentId, out IdentityObject? replacementResult)); Assert.Same(replacement, replacementResult); } @@ -725,14 +586,9 @@ public void TypedDropRoutesAndCancelsItsManagedSession() DragSource source = Register(new DragSource()); var target = new DropTarget(); var data = new EditorDragData(source, "source"); - RuntimeIdentity supersededIdentity = m_runtime.interactions - .For("tests/other", supersededSource) - .BeginDrag(new EditorDragData(supersededSource, "superseded")); - RuntimeIdentity identity = m_runtime.interactions - .For("tests/other", source) - .BeginDrag(data); + RuntimeIdentity supersededIdentity = m_runtime.interactions.For("tests/other", supersededSource).BeginDrag(new EditorDragData(supersededSource, "superseded")); + RuntimeIdentity identity = m_runtime.interactions.For("tests/other", source).BeginDrag(data); EditorInteraction dropTarget = m_runtime.interactions.For("tests/drop", target); - Assert.NotEqual(supersededIdentity, identity); Assert.False(m_runtime.interactions.TryGetDragSource(supersededIdentity, out _)); Assert.True(dropTarget.QueryDrop(identity, EditorDropPlacement.Into).canDrop); @@ -747,10 +603,8 @@ public void TypedDropRoutesAndCancelsItsManagedSession() public void UnregisteredDragSourceCancelsTheManagedSession() { DragSource source = Register(new DragSource()); - RuntimeIdentity identity = m_runtime.interactions.For("tests/drag", source).BeginDrag( - new EditorDragData(source, "source")); + RuntimeIdentity identity = m_runtime.interactions.For("tests/drag", source).BeginDrag(new EditorDragData(source, "source")); Assert.True(m_identities.Unregister(source)); - Assert.False(m_runtime.interactions.TryGetDragSource(identity, out _)); Assert.False(m_runtime.interactions.TryGetDragSource(identity, out _)); } @@ -766,9 +620,7 @@ private TIdentity Register(TIdentity value) public void ThrowingPanelPresentationPropertyQuarantinesOnlyThatPanel() { TestPanel.throwFromWindowPadding = true; - EditorPanelExtension panel = Assert.Single( - m_runtime.panels.Where(static value => value.id == "tests.panel")); - + EditorPanelExtension panel = Assert.Single(m_runtime.panels.Where(static value => value.id == "tests.panel")); Assert.False(panel.TryGetWindowPresentation(out _, out _, out _)); Assert.False(panel.isOpen); } @@ -777,17 +629,13 @@ public void ThrowingPanelPresentationPropertyQuarantinesOnlyThatPanel() public void ShutdownDetachesPanelsBeforeStoppingModulesThenDisposesInteractionsAndExtensions() { ZThrowingDisposeModule.throwOnDispose = true; - AggregateException failure = Assert.Throws(m_runtime.Dispose); Assert.Contains("Injected extension disposal failure", failure.ToString()); Assert.Equal(GenerationState.Faulted, m_modules.generations.state); Assert.Throws(() => m_modules.generations.EnsureReady("reload after failed extension retirement")); - Assert.True(ADisposeObserverModule.historyAvailableDuringStop); Assert.True(ADisposeObserverModule.historyDisposedDuringDispose); - Assert.True( - ShutdownOrder.events.IndexOf("panel.detach") < - ShutdownOrder.events.IndexOf("module.stop")); + Assert.True(ShutdownOrder.events.IndexOf("panel.detach") < ShutdownOrder.events.IndexOf("module.stop")); Assert.Equal(1, ADisposeObserverModule.disposeCount); Assert.Equal(1, ZThrowingDisposeModule.disposeCount); } @@ -805,7 +653,10 @@ public void FailedModuleStartIsStoppedBeforeCandidateDisposal() Assert.Equal(stops + 1, TestModule.stopCount); Assert.Equal(GenerationState.Ready, m_modules.generations.state); } - finally { TestModule.throwOnStart = false; } + finally + { + TestModule.throwOnStart = false; + } } [Fact] @@ -824,7 +675,10 @@ public void FailedPanelAttachmentIsCompensatedBeforeItIsQuarantined() runtime.Dispose(); Assert.Equal(detaches + 1, TestPanel.detachCount); } - finally { TestPanel.throwOnAttach = false; } + finally + { + TestPanel.throwOnAttach = false; + } } [Theory] @@ -864,8 +718,10 @@ public void PendingPanelAndModuleDrainBeforeHistoryAndExtensionResourcesAreDispo Assert.True(ADisposeObserverModule.historyDisposedDuringDispose); Assert.Equal(1, ADisposeObserverModule.disposeCount); Assert.Equal(GenerationState.Ready, m_modules.generations.state); - Assert.Equal(new[] { "panel.detach", "panel.detach", "panel.detach", "module.stop", "module.stop", "module.stop" }, - ShutdownOrder.events.Where(static value => value is "panel.detach" or "module.stop")); + Assert.Equal( + new[] { "panel.detach", "panel.detach", "panel.detach", "module.stop", "module.stop", "module.stop" }, + ShutdownOrder.events.Where(static value => value is "panel.detach" or "module.stop") + ); } [Fact] @@ -873,10 +729,8 @@ public void RebuildRetainsHostExtensionsInsteadOfRestartingModules() { int starts = TestModule.startCount; int attaches = TestPanel.attachCount; - m_types.Rebuild(); _ = m_runtime.panelCount; - Assert.Equal(starts, TestModule.startCount); Assert.Equal(attaches, TestPanel.attachCount); } @@ -885,24 +739,19 @@ public void RebuildRetainsHostExtensionsInsteadOfRestartingModules() public void ModuleStateAndPanelVisibilityRestoreForTheSameProject() { TestModule.stateValue = 42; - EditorPanelExtension panel = Assert.Single( - m_runtime.panels.Where(static value => value.id == "tests.panel")); + EditorPanelExtension panel = Assert.Single(m_runtime.panels.Where(static value => value.id == "tests.panel")); panel.isOpen = true; m_runtime.Update(new EditorFrame(0.016f, 3f, isFocused: true)); m_runtime.Dispose(); - string settingsPath = Path.Combine(m_projectRoot, "editor.ini"); Assert.True(File.Exists(settingsPath)); string document = File.ReadAllText(settingsPath); Assert.Contains("[InnoEditor][Module.tests.state]", document); Assert.DoesNotContain("[InnoEditor][Module.tests.update-barrier]", document); - using EditorInteractionRuntime restored = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super"))); restored.Start(); - Assert.Equal(42, TestModule.restoredStateValue); - Assert.True(Assert.Single( - restored.panels.Where(static value => value.id == "tests.panel")).isOpen); + Assert.True(Assert.Single(restored.panels.Where(static value => value.id == "tests.panel")).isOpen); } [Fact] @@ -910,16 +759,11 @@ public void MalformedModuleStateValueUsesTheExtensionFallback() { m_runtime.Dispose(); var context = new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); - context.SetLayoutSection("Module.tests.state", new Dictionary - { - ["value"] = "not-json" - }); + context.SetLayoutSection("Module.tests.state", new Dictionary { ["value"] = "not-json" }); context.SaveLayout(); TestModule.restoredStateValue = -1; - using EditorInteractionRuntime restored = CreateRuntime(context); restored.Start(); - Assert.Equal(0, TestModule.restoredStateValue); } @@ -932,17 +776,11 @@ public void ExtensionStateCaptureFailure_PublishesCurrentDiagnosticUntilRetrySuc { TestModule.captureFailure = true; m_runtime.Update(new EditorFrame(0.016f, 3f, isFocused: true)); - - DiagnosticReport report = Assert.Single(sink.reports.Values.Where(static value => - value.source.displayName == "Editor State Capture")); + DiagnosticReport report = Assert.Single(sink.reports.Values.Where(static value => value.source.displayName == "Editor State Capture")); Assert.Equal("EDITOR-STATE-CAPTURE", Assert.Single(report.diagnostics).code); - TestModule.captureFailure = false; m_runtime.Update(new EditorFrame(0.016f, 6f, isFocused: true)); - - Assert.DoesNotContain( - sink.reports.Values, - static value => value.source.displayName == "Editor State Capture"); + Assert.DoesNotContain(sink.reports.Values, static value => value.source.displayName == "Editor State Capture"); } finally { @@ -957,18 +795,11 @@ public void UnifiedEditorIniPreservesLayoutAndExtensionStateSectionsTogether() const string layout = "[Window][Hierarchy]\nPos=10,20\nSize=300,400"; var context = new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); context.SetImGuiLayout(layout); - context.SetLayoutSection("Module.tests", new Dictionary - { - ["openScenes"] = "[\"Scenes/Test.iscene\"]" - }); - + context.SetLayoutSection("Module.tests", new Dictionary { ["openScenes"] = "[\"Scenes/Test.iscene\"]" }); Assert.True(context.SaveLayoutIfChanged()); var restored = new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); - Assert.Equal(layout, restored.imguiLayout); - Assert.True(restored.TryGetLayoutSection( - "Module.tests", - out IReadOnlyDictionary values)); + Assert.True(restored.TryGetLayoutSection("Module.tests", out IReadOnlyDictionary values)); Assert.Equal("[\"Scenes/Test.iscene\"]", values["openScenes"]); string document = File.ReadAllText(restored.layoutPath); Assert.Contains("[Window][Hierarchy]", document); @@ -982,22 +813,17 @@ public void StartupRegistryRefreshCannotOverwriteModuleStateBeforeRestore() { m_runtime.Dispose(); var context = new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); - context.SetLayoutSection("Module.tests.state", new Dictionary - { - ["value"] = "91" - }); + context.SetLayoutSection("Module.tests.state", new Dictionary { ["value"] = "91" }); context.SaveLayout(); TestModule.startCount = 0; TestModule.restoredStateValue = 0; TestModule.rebuildDuringRestore = true; - using EditorInteractionRuntime restored = CreateRuntime(new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super"))); restored.Start(); Assert.Equal(91, TestModule.restoredStateValue); TestModule.stateValue = TestModule.restoredStateValue; restored.Update(new EditorFrame(0.016f, 0.016f, isFocused: true)); restored.SaveState(); - string document = File.ReadAllText(Path.Combine(m_projectRoot, "editor.ini")); Assert.Contains("[InnoEditor][Module.tests.state]", document); Assert.Contains("value=91", document); @@ -1008,19 +834,14 @@ private sealed class TestDiagnosticSink : IDiagnosticSink { internal Dictionary reports { get; } = new(StringComparer.Ordinal); - public void Replace(DiagnosticReport report) - => reports[report.source.id] = report; - - public void Clear(DiagnosticSource source) - => reports.Remove(source.id); + public void Replace(DiagnosticReport report) => reports[report.source.id] = report; + public void Clear(DiagnosticSource source) => reports.Remove(source.id); } - } public class BaseTarget; public sealed class DerivedTarget : BaseTarget; public sealed class DragSource : IdentityObject; - public sealed class DropTarget { public bool wasDropped { get; set; } @@ -1037,14 +858,11 @@ public sealed record InteractionPresentation( bool submit, bool cancel = false ); - [EditorHistoryHandler(NeutralHistoryHandler.KIND)] public sealed class NeutralHistoryHandler : EditorHistoryHandler { public const string KIND = "tests/neutral-value"; - public static int value; - public static EditorHistoryChange CreateChange( int before, int after @@ -1059,47 +877,36 @@ protected override EditorHistoryAvailability Query( EditorHistoryContext context, EditorHistoryChange change, EditorHistoryDirection direction - ) - => change.payload.length >= sizeof(int) * 2 - ? EditorHistoryAvailability.Available() - : EditorHistoryAvailability.Unavailable("The neutral value payload is truncated."); - + ) => change.payload.length >= sizeof(int) * 2 ? EditorHistoryAvailability.Available() : EditorHistoryAvailability.Unavailable("The neutral value payload is truncated."); protected override EditorHistoryResult Apply( EditorHistoryContext context, EditorHistoryChange change, EditorHistoryDirection direction ) { byte[] bytes = change.payload.ReadBytes(); - value = BitConverter.ToInt32( - bytes, - direction == EditorHistoryDirection.Undo ? 0 : sizeof(int)); + value = BitConverter.ToInt32(bytes, direction == EditorHistoryDirection.Undo ? 0 : sizeof(int)); return EditorHistoryResult.Success(); } } public interface ITestHostService; - public sealed class TestHostService : ITestHostService; - [EditorPanel("tests.host-service", "Host Service")] public sealed class HostServicePanel : EditorPanel { public static ITestHostService? current; - public HostServicePanel(ITestHostService service) { current = service; } - protected override void OnDraw(EditorContext context) - => _ = context; + protected override void OnDraw(EditorContext context) => _ = context; } [EditorModule("tests.state")] public sealed class TestModule : EditorModule { private readonly TypeCatalog m_types; - public static int startCount; public static int stopCount; public static int stateValue; @@ -1109,7 +916,6 @@ public sealed class TestModule : EditorModule public static bool throwOnStart; public static bool throwOnStop; public static int pendingStops; - public TestModule(TypeCatalog types) { m_types = types; @@ -1157,15 +963,12 @@ public sealed class UpdateBarrierModule : EditorModule public static bool block; public static bool throwWhenRead; public static int barrierReadCount; - public override bool blocksFollowingUpdates { get { barrierReadCount++; - return throwWhenRead - ? throw new InvalidOperationException("Injected module barrier failure.") - : block; + return throwWhenRead ? throw new InvalidOperationException("Injected module barrier failure.") : block; } } } @@ -1174,7 +977,6 @@ public override bool blocksFollowingUpdates public sealed class FollowingUpdateModule : EditorModule { public static int updateCount; - protected override void OnUpdate(EditorContext context) => updateCount++; } @@ -1188,10 +990,7 @@ public sealed class TestPanel(TestModule module) : EditorPanel public static bool throwOnAttach; public static bool throwOnDetach; public static int pendingDetaches; - - public override bool useWindowPadding => throwFromWindowPadding - ? throw new InvalidOperationException("Injected panel presentation failure.") - : true; + public override bool useWindowPadding => throwFromWindowPadding ? throw new InvalidOperationException("Injected panel presentation failure.") : true; protected override void OnAttach(EditorContext context) { @@ -1231,23 +1030,19 @@ public sealed class MultiPresentationSetting : ISerializable [ProjectSettingPath("Project/Tests/Multi/Primary")] public sealed class PrimaryMultiPresentationEditor : ProjectSettingEditor { - protected override void OnDraw(MultiPresentationSetting setting) - => _ = setting; + protected override void OnDraw(MultiPresentationSetting setting) => _ = setting; } [ProjectSettingPath("Project/Tests/Multi/Secondary")] public sealed class SecondaryMultiPresentationEditor : ProjectSettingEditor { - protected override void OnDraw(MultiPresentationSetting setting) - => _ = setting; + protected override void OnDraw(MultiPresentationSetting setting) => _ = setting; } [EditorAction("tests.resolve")] public sealed class BaseResolveAction : EditorAction { - protected override EditorActionState Query(EditorActionContext context) - => new(true, true, displayName: "base"); - + protected override EditorActionState Query(EditorActionContext context) => new(true, true, displayName: "base"); protected override void Execute(EditorActionContext context) { } @@ -1256,9 +1051,7 @@ protected override void Execute(EditorActionContext context) [EditorAction("tests.resolve", "tests/special", priority: 100)] public sealed class AreaResolveAction : EditorAction { - protected override EditorActionState Query(EditorActionContext context) - => new(true, true, displayName: "area"); - + protected override EditorActionState Query(EditorActionContext context) => new(true, true, displayName: "area"); protected override void Execute(EditorActionContext context) { } @@ -1268,7 +1061,6 @@ protected override void Execute(EditorActionContext context) public sealed class DeferredAction : EditorAction { public static int executeCount; - protected override void Execute(EditorActionContext context) => executeCount++; } @@ -1277,15 +1069,12 @@ public sealed class MetadataAction : EditorAction { public static int targetTypeReadCount; public static bool throwWhenRead; - public override Type? targetType { get { targetTypeReadCount++; - return throwWhenRead - ? throw new InvalidOperationException("Action metadata was read at runtime.") - : typeof(BaseTarget); + return throwWhenRead ? throw new InvalidOperationException("Action metadata was read at runtime.") : typeof(BaseTarget); } } @@ -1300,24 +1089,16 @@ protected override void Execute(EditorActionContext context) public sealed class MultiShortcutAction : EditorAction { public static int executeCount; - protected override void Execute(EditorActionContext context) => executeCount++; } [EditorAction("tests.toolbar", "tests/toolbar")] -[EditorToolbarItem( - "tests/toolbar", - EditorToolbarIcon.Play, - "Start Runtime", - activeIcon: EditorToolbarIcon.Stop)] +[EditorToolbarItem("tests/toolbar", EditorToolbarIcon.Play, "Start Runtime", activeIcon: EditorToolbarIcon.Stop)] public sealed class ToolbarAction : EditorAction { public static bool isChecked; public static int executeCount; - - protected override EditorActionState Query(EditorActionContext context) - => new(true, true, isChecked, isChecked ? "Stop Runtime" : "Start Runtime"); - + protected override EditorActionState Query(EditorActionContext context) => new(true, true, isChecked, isChecked ? "Stop Runtime" : "Start Runtime"); protected override void Execute(EditorActionContext context) => executeCount++; } @@ -1325,7 +1106,6 @@ protected override EditorActionState Query(EditorActionContext context) public sealed class ThrowAfterActivateAction : EditorAction { public static int cancelCount; - protected override void Execute(EditorActionContext context) { Activate(context); @@ -1341,7 +1121,6 @@ public sealed class ADisposeObserverModule(EditorInteractions interactions) : Ed public static bool historyAvailableDuringStop; public static bool historyDisposedDuringDispose; public static int disposeCount; - protected override void OnStop(EditorContext context) { if (interactions.history.isFaulted) @@ -1369,7 +1148,6 @@ public sealed class ZThrowingDisposeModule : EditorModule { public static bool throwOnDispose; public static int disposeCount; - protected override void OnDispose() { disposeCount++; @@ -1384,11 +1162,9 @@ internal static class ShutdownOrder } [EditorAction("tests.interaction")] -public sealed class InteractionAction : - EditorPresentationAction +public sealed class InteractionAction : EditorPresentationAction { private string m_value = string.Empty; - protected override void Execute(EditorActionContext context) { m_value = "Initial"; @@ -1396,8 +1172,7 @@ protected override void Execute(EditorActionContext context) Activate(context); } - protected override bool Present( - EditorActionContext context) + protected override bool Present(EditorActionContext context) { InteractionPresentation presentation = context.argument; if (presentation.cancel) @@ -1428,7 +1203,6 @@ protected override bool Present( public sealed class CommitOnPresentationLostAction : EditorAction { private InteractionTarget? m_target; - protected override void Execute(EditorActionContext context) { m_target = context.target; @@ -1471,9 +1245,7 @@ EditorMenuBuilder builder [EditorDrop("tests/drop")] public sealed class TestDrop : EditorDrop { - protected override EditorDropStatus Query(EditorDropContext context) - => EditorDropStatus.Accept(); - + protected override EditorDropStatus Query(EditorDropContext context) => EditorDropStatus.Accept(); protected override EditorDropResult Drop(EditorDropContext context) { context.target.wasDropped = true; @@ -1486,15 +1258,17 @@ public sealed class TestDocumentProvider : EditorDocumentProvider public int openCount { get; private set; } public int saveCount { get; private set; } public int closeCount { get; private set; } - public override string id => "tests.documents"; - public override bool CanOpen(string assetPath) - => assetPath.EndsWith(".ispriteatlas2d", StringComparison.Ordinal) - || assetPath.EndsWith(".itilemap2d", StringComparison.Ordinal); - + public override bool CanOpen(string assetPath) => assetPath.EndsWith( + ".ispriteatlas2d", + StringComparison.Ordinal + ) + || assetPath.EndsWith( + ".itilemap2d", + StringComparison.Ordinal + ); public override void Open(EditorDocumentContext context) => openCount++; - public override bool Save(EditorDocumentContext context) { saveCount++; @@ -1506,20 +1280,15 @@ public override bool Save(EditorDocumentContext context) public sealed class TestViewportCoordinates : IEditorViewportCoordinateConverter { - public Inno.Core.Mathematics.Vector2 ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition) - => screenPosition * 2f; - - public Inno.Core.Mathematics.Vector2 WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition) - => worldPosition / 2f; + public Inno.Core.Mathematics.Vector2 ScreenToWorld(Inno.Core.Mathematics.Vector2 screenPosition) => screenPosition * 2f; + public Inno.Core.Mathematics.Vector2 WorldToScreen(Inno.Core.Mathematics.Vector2 worldPosition) => worldPosition / 2f; } public sealed class TestViewportTool(IEditorHistory history) : EditorViewportTool { public int downCount { get; private set; } public int upCount { get; private set; } - public override string id => "tests.viewport.paint"; - public override EditorViewportCursor cursor => EditorViewportCursor.Crosshair; public override void OnPointerDown( @@ -1530,9 +1299,7 @@ EditorViewportPointerEvent pointer context.CapturePointer(pointer.pointerId); context.BeginHistoryGesture("Paint Tile"); NeutralHistoryHandler.value = 1; - history.RecordApplied( - "Paint Tile Cell", - NeutralHistoryHandler.CreateChange(before: 0, after: 1)); + history.RecordApplied("Paint Tile Cell", NeutralHistoryHandler.CreateChange(before: 0, after: 1)); } public override void OnPointerUp( diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorSettingsTests.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorSettingsTests.cs index 1dfa0a061..8de8e5c4e 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/EditorSettingsTests.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorSettingsTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; - using Inno.Extensibility.Modules; using Inno.Extensibility.Types; using Inno.Core.Diagnostics; @@ -18,13 +17,9 @@ using Xunit; namespace Inno.Editor.Interactions.Tests; - public sealed class EditorSettingsTests : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoEditorSettingsTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoEditorSettingsTests", Guid.NewGuid().ToString("N")); private readonly EditorInteractionRuntime m_runtime; private readonly EditorSettings m_settings; private readonly IEditorConsole m_console; @@ -34,12 +29,10 @@ public sealed class EditorSettingsTests : IDisposable private readonly DiagnosticHub m_diagnostics = new(); private readonly FakePlayMode m_playMode = new(); private readonly LogRouter m_logs = new(); - public EditorSettingsTests() { Directory.CreateDirectory(m_projectRoot); - m_modules = new ModuleHost(new ModuleHostOptions - { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorSettingsTests).Assembly) }); + m_modules = new ModuleHost(new ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorSettingsTests).Assembly) }); _ = typeof(EditorSettings); _ = System.Reflection.Assembly.Load("Inno.Editor.Panel.Logging"); m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); @@ -50,12 +43,11 @@ public EditorSettingsTests() new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_types, m_logs, - [m_types, m_serialization, m_diagnostics, m_playMode]); + [m_types, m_serialization, m_diagnostics, m_playMode] + ); m_runtime.Start(); - m_settings = SettingsCaptureModule.current - ?? throw new InvalidOperationException("The Settings module was not discovered."); - m_console = SettingsCaptureModule.console - ?? throw new InvalidOperationException("The Console module was not discovered."); + m_settings = SettingsCaptureModule.current ?? throw new InvalidOperationException("The Settings module was not discovered."); + m_console = SettingsCaptureModule.console ?? throw new InvalidOperationException("The Console module was not discovered."); } public void Dispose() @@ -76,20 +68,14 @@ public void ApplyPersistsObjectsAtTheProjectRootAndPublishesOnlyTheService() { int changedCount = 0; EditorSettings? published = null; - m_settings.changed += settings => + using IDisposable subscription = m_settings.ListenChanges(settings => { changedCount++; published = settings; - }; + }); var value = new EditorSettingObject(); value.SetAsInt32("value", 10); - - Assert.True(m_settings.Apply( - new Dictionary(StringComparer.Ordinal) - { - ["Editor/Tests/Values/Project Count"] = value - })); - + Assert.True(m_settings.Apply(new Dictionary(StringComparer.Ordinal) { ["Editor/Tests/Values/Project Count"] = value })); string path = Path.Combine(m_projectRoot, "Settings.Editor.inno"); Assert.True(File.Exists(path)); Assert.False(Directory.Exists(Path.Combine(m_projectRoot, "Settings"))); @@ -102,16 +88,11 @@ public void ApplyPersistsObjectsAtTheProjectRootAndPublishesOnlyTheService() public void ChangedSubscriberFailureDoesNotChangeHistoryResultOrSkipLaterSubscribers() { int delivered = 0; - m_settings.changed += _ => throw new InvalidOperationException("subscriber"); - m_settings.changed += _ => delivered++; + using IDisposable failed = m_settings.ListenChanges(_ => throw new InvalidOperationException("subscriber")); + using IDisposable deliveredSubscription = m_settings.ListenChanges(_ => delivered++); var value = new EditorSettingObject(); value.SetAsInt32("value", 11); - - Assert.True(m_settings.Apply( - new Dictionary(StringComparer.Ordinal) - { - ["Editor/Tests/Values/Project Count"] = value - })); + Assert.True(m_settings.Apply(new Dictionary(StringComparer.Ordinal) { ["Editor/Tests/Values/Project Count"] = value })); Assert.Equal(1, delivered); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(2, delivered); @@ -126,7 +107,6 @@ public void GetReturnsIsolatedObjectsAndRejectsPagesOrMissingPaths() { EditorSettingObject first = m_settings.Get("Editor/Tests/Values/Project Count"); first.SetAsInt32("value", 99); - Assert.Equal(3, m_settings.Get("Editor/Tests/Values/Project Count").GetAsInt32("value")); Assert.Throws(() => m_settings.Get("Editor/Tests")); Assert.Throws(() => m_settings.Get("Editor/Tests/Missing")); @@ -135,25 +115,12 @@ public void GetReturnsIsolatedObjectsAndRejectsPagesOrMissingPaths() [Fact] public void ResetRemovesThePathOverrideAndRestoresTheDefinitionObject() { - EditorSetting definition = Assert.Single( - m_settings.definitions, - static setting => setting.path == "Editor/Tests/Values/Project Count"); + EditorSetting definition = Assert.Single(m_settings.definitions, static setting => setting.path == "Editor/Tests/Values/Project Count"); var value = new EditorSettingObject(); value.SetAsInt32("value", 8); - Assert.True(m_settings.Apply( - new Dictionary - { - ["Editor/Tests/Values/Project Count"] = value - })); + Assert.True(m_settings.Apply(new Dictionary { ["Editor/Tests/Values/Project Count"] = value })); Assert.False(definition.IsDefault(m_settings.Get("Editor/Tests/Values/Project Count"))); - - Assert.True(m_settings.Apply( - new Dictionary(), - new HashSet(StringComparer.Ordinal) - { - "Editor/Tests/Values/Project Count" - })); - + Assert.True(m_settings.Apply(new Dictionary(), new HashSet(StringComparer.Ordinal) { "Editor/Tests/Values/Project Count" })); Assert.Equal(3, m_settings.Get("Editor/Tests/Values/Project Count").GetAsInt32("value")); Assert.True(definition.IsDefault(m_settings.Get("Editor/Tests/Values/Project Count"))); Assert.NotSame(definition.defaultValue, definition.defaultValue); @@ -167,13 +134,7 @@ public void OneApplyCreatesOneSharedHistoryEntryForAllSettings() count.SetAsInt32("value", 7); var toggle = new EditorSettingObject(); toggle.SetAsBoolean("value", false); - - Assert.True(m_settings.Apply( - new Dictionary(StringComparer.Ordinal) - { - ["Editor/Tests/Values/Project Count"] = count, - ["Editor/Tests/Values/Project Toggle"] = toggle - })); + Assert.True(m_settings.Apply(new Dictionary(StringComparer.Ordinal) { ["Editor/Tests/Values/Project Count"] = count, ["Editor/Tests/Values/Project Toggle"] = toggle })); Assert.Equal("Apply Settings", m_runtime.interactions.history.undoName); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(3, m_settings.Get("Editor/Tests/Values/Project Count").GetAsInt32("value")); @@ -189,16 +150,10 @@ public void ClearOnPlaySettingDefaultsToTrueAndImmediatelyControlsTheConsole() const string path = "Editor/Diagnostics/Console/Clear on Play"; Assert.True(m_settings.Get(path).GetAsBoolean("value")); Assert.True(m_console.clearOnPlay); - var disabled = new EditorSettingObject(); disabled.SetAsBoolean("value", false); - Assert.True(m_settings.Apply( - new Dictionary(StringComparer.Ordinal) - { - [path] = disabled - })); + Assert.True(m_settings.Apply(new Dictionary(StringComparer.Ordinal) { [path] = disabled })); Assert.False(m_console.clearOnPlay); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_console.clearOnPlay); } @@ -206,17 +161,16 @@ public void ClearOnPlaySettingDefaultsToTrueAndImmediatelyControlsTheConsole() [Fact] public void CatalogKeepsRawStringPathsAndAlphabeticalSections() { - EditorSetting page = Assert.Single( - m_settings.definitions, - static definition => definition.path == "Editor/Tests"); + EditorSetting page = Assert.Single(m_settings.definitions, static definition => definition.path == "Editor/Tests"); Assert.False(page.hasValue); Assert.Equal("Test-only settings overview.", page.description); Assert.Equal( new[] { "Alpha", "Numbers" }, - m_settings.definitions - .Where(static definition => definition.pagePath == "Editor/Tests/Values") - .OrderBy(static definition => definition.section, StringComparer.Ordinal) - .Select(static definition => definition.section)); + m_settings.definitions.Where(static definition => definition.pagePath == "Editor/Tests/Values").OrderBy( + static definition => definition.section, + StringComparer.Ordinal + ).Select(static definition => definition.section) + ); } [Fact] @@ -231,21 +185,15 @@ public void SettingObjectRoundTripsSupportedPrimitivesAndArrays() value.SetAsSingle("single", 1.25f); value.SetAsDouble("double", 2.5d); value.SetAsString("string", "value"); - value.SetAsBooleanArray("booleans", [true, false]); + value.SetAsBooleanArray("booleans", [true, false ]); value.SetAsInt32Array("int32s", [-1, 2]); value.SetAsUInt32Array("uint32s", [1u, 2u]); value.SetAsSingleArray("singles", [1.5f, 2.5f]); value.SetAsDoubleArray("doubles", [3.5d, 4.5d]); value.SetAsStringArray("strings", ["first", null, "third"]); - - Assert.True(m_settings.Apply( - new Dictionary(StringComparer.Ordinal) - { - ["Editor/Tests/Objects/Primitives"] = value - })); + Assert.True(m_settings.Apply(new Dictionary(StringComparer.Ordinal) { ["Editor/Tests/Objects/Primitives"] = value })); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.True(m_runtime.interactions.history.Redo().succeeded); - EditorSettingObject restored = m_settings.Get("Editor/Tests/Objects/Primitives"); Assert.True(restored.GetAsBoolean("boolean")); Assert.Equal(-32, restored.GetAsInt32("int32")); @@ -260,30 +208,22 @@ public void SettingObjectRoundTripsSupportedPrimitivesAndArrays() Assert.Equal(new[] { 1u, 2u }, restored.GetAsUInt32Array("uint32s")); Assert.Equal(new[] { 1.5f, 2.5f }, restored.GetAsSingleArray("singles")); Assert.Equal(new[] { 3.5d, 4.5d }, restored.GetAsDoubleArray("doubles")); - Assert.Equal( - new string?[] { "first", null, "third" }, - restored.GetAsStringArray("strings")); - - string?[] strings = restored.GetAsStringArray("strings"); + Assert.Equal(new string? [] { "first", null, "third" }, restored.GetAsStringArray("strings")); + string? [] strings = restored.GetAsStringArray("strings"); strings[0] = "mutated"; Assert.Equal("first", restored.GetAsStringArray("strings")[0]); - Assert.Throws( - () => restored.SetAsSingle("invalid", float.PositiveInfinity)); + Assert.Throws(() => restored.SetAsSingle("invalid", float.PositiveInfinity)); Assert.Throws(() => restored.GetAsString(string.Empty)); } [Fact] public void DrawTracksDifferenceFromEachStagedObjectsFirstDraw() { - EditorSetting definition = Assert.Single( - m_settings.definitions, - static setting => setting.path == "Editor/Tests/Objects/Draw Mutation"); + EditorSetting definition = Assert.Single(m_settings.definitions, static setting => setting.path == "Editor/Tests/Objects/Draw Mutation"); var value = new EditorSettingObject(); - Assert.True(definition.Draw(value)); Assert.Equal(42, value.GetAsInt32("value")); Assert.True(definition.Draw(value)); - var alreadyCommitted = new EditorSettingObject(); alreadyCommitted.SetAsInt32("value", 42); Assert.False(definition.Draw(alreadyCommitted)); @@ -297,7 +237,6 @@ IEditorConsole editorConsole { internal static EditorSettings? current; internal static IEditorConsole? console; - protected override void OnStart(EditorContext context) { current = settings; @@ -308,25 +247,14 @@ protected override void OnStart(EditorContext context) private sealed class FakePlayMode : IEditorPlayMode { public EditorPlayModeState state => EditorPlayModeState.Editing; - public bool isPlaying => false; - public string? lastFailure => null; - public LogSessionId activeSessionId => LogSessionId.none; - public event Action? stateChanged - { - add - { - } - remove - { - } - } + private readonly Inno.Core.Events.EventDispatcher m_stateDispatcher = new(); + public IDisposable ListenStateChanges(Action listener) => m_stateDispatcher.CreateHub(); public bool EnterPlayMode() => false; - public bool ExitPlayMode() => false; } @@ -340,9 +268,7 @@ private sealed class TestSettingsPage : EditorSetting private sealed class TestCountSetting : EditorSetting { public override EditorSettingObject defaultValue => CreateDefault(); - public override string section => "Numbers"; - public override string description => "Exercises path-addressed project persistence."; protected override void OnDraw(EditorSettingObject setting) @@ -361,7 +287,6 @@ private static EditorSettingObject CreateDefault() private sealed class TestToggleSetting : EditorSetting { public override EditorSettingObject defaultValue => CreateDefault(); - public override string section => "Alpha"; protected override void OnDraw(EditorSettingObject setting) @@ -391,7 +316,6 @@ private sealed class TestDrawMutationSetting : EditorSetting { public override EditorSettingObject defaultValue => new(); - protected override void OnDraw(EditorSettingObject setting) - => setting.SetAsInt32("value", 42); + protected override void OnDraw(EditorSettingObject setting) => setting.SetAsInt32("value", 42); } } diff --git a/tests/editor/Inno.Editor.Interactions.Tests/Inno.Editor.Interactions.Tests.csproj b/tests/editor/Inno.Editor.Interactions.Tests/Inno.Editor.Interactions.Tests.csproj index 9e76fd0e6..9203b7889 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/Inno.Editor.Interactions.Tests.csproj +++ b/tests/editor/Inno.Editor.Interactions.Tests/Inno.Editor.Interactions.Tests.csproj @@ -10,6 +10,7 @@ + @@ -26,7 +27,7 @@ - + @@ -53,7 +54,6 @@ - diff --git a/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.Selection.cs b/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.Selection.cs new file mode 100644 index 000000000..f544b02d9 --- /dev/null +++ b/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.Selection.cs @@ -0,0 +1,131 @@ +using System; +using Inno.Core.Identity; +using Inno.Core.Serialization; +using Inno.Extensibility.Types; +using Inno.Scene; +using Inno.Scene.Components; +using Xunit; + +namespace Inno.Editor.Interactions.Tests; +public sealed partial class SceneHistoryTests +{ + [Fact] + public void BatchPropertyEditPreservesIndependentBeforeValuesInOneUndo() + { + GameScene scene = CreateScene(); + var first = scene.CreateObject("First").AddComponent(); + var second = scene.CreateObject("Second").AddComponent(); + first.value = 12; + second.value = 34; + Assert.False(m_edits.HaveSamePropertyValues([first, second], "value")); + Assert.True(m_edits.ChangeProperties([first, second], "value", () => first.value = 42, "Edit Selection")); + Assert.Equal(42, first.value); + Assert.Equal(42, second.value); + Assert.True(m_edits.HaveSamePropertyValues([first, second], "value")); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + Assert.Equal(12, first.value); + Assert.Equal(34, second.value); + Assert.True(m_runtime.interactions.history.Redo().succeeded); + Assert.Equal(42, first.value); + Assert.Equal(42, second.value); + } + + [Fact] + public void BatchAxisEditPreservesUneditedComponentsIncludingEqualPrimarySamples() + { + GameScene scene = CreateScene(); + var first = scene.CreateObject("First").transform; + var second = scene.CreateObject("Second").transform; + first.localPosition = new(1, 2, 3); + second.localPosition = new(4, 5, 6); + Assert.True(m_edits.ChangeProperties([first, second], "localPosition", () => first.localPosition = new(9, 2, 3), "Edit X", componentName: "x")); + Assert.Equal(new Inno.Core.Mathematics.Vector3(9, 5, 6), second.localPosition); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + Assert.Equal(new Inno.Core.Mathematics.Vector3(4, 5, 6), second.localPosition); + Assert.True(m_edits.ChangeProperties([first, second], "localPosition", () => { }, "Use Primary X", componentName: "x")); + Assert.Equal(new Inno.Core.Mathematics.Vector3(1, 5, 6), second.localPosition); + Assert.Throws(() => m_edits.ChangeProperties([first, second], "localPosition", () => first.localPosition = default, "Rejected Component", componentName: "notAComponent")); + Assert.Equal(new Inno.Core.Mathematics.Vector3(1, 2, 3), first.localPosition); + } + + [Fact] + public void LaterBatchSetterFailureRestoresAllEarlierTargets() + { + GameScene scene = CreateScene(); + var first = scene.CreateObject("First").AddComponent(); + var second = scene.CreateObject("Second").AddComponent(); + first.value = 12; + second.value = 34; + second.rejectAnswer = true; + Assert.ThrowsAny(() => m_edits.ChangeProperties([first, second], "value", () => first.value = 42, "Rejected Selection")); + Assert.Equal(12, first.value); + Assert.Equal(34, second.value); + Assert.False(m_runtime.interactions.history.canUndo); + } + + [Fact] + public void BatchDeleteNormalizesSelectedAncestorsAndRestoresEverySubtree() + { + GameScene scene = CreateScene(); + GameObject root = scene.CreateObject("Root"); + GameObject child = scene.CreateObject("Child"); + GameObject other = scene.CreateObject("Other"); + child.transform.SetParent(root.transform); + Guid rootId = root.identity.persistentId; + Guid childId = child.identity.persistentId; + Guid otherId = other.identity.persistentId; + m_edits.DeleteGameObjects([child, root, other]); + Assert.Null(IdentityAllocator.current.Get(rootId)); + Assert.Null(IdentityAllocator.current.Get(childId)); + Assert.Null(IdentityAllocator.current.Get(otherId)); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + GameObject restoredRoot = Assert.IsType(IdentityAllocator.current.Get(rootId)); + GameObject restoredChild = Assert.IsType(IdentityAllocator.current.Get(childId)); + Assert.Same(restoredRoot.transform, restoredChild.transform.parent); + Assert.NotNull(IdentityAllocator.current.Get(otherId)); + Assert.True(m_runtime.interactions.history.Redo().succeeded); + Assert.Null(IdentityAllocator.current.Get(rootId)); + } + + [Fact] + public void BatchMovePreservesRootOrderAndRestoresIndependentPlacements() + { + GameScene scene = CreateScene(); + GameObject first = scene.CreateObject("First"); + GameObject second = scene.CreateObject("Second"); + GameObject destination = scene.CreateObject("Destination"); + GameObject child = scene.CreateObject("Child"); + child.transform.SetParent(first.transform); + m_edits.MoveGameObjects([first, child, second], scene, destination); + Assert.Same(destination.transform, first.transform.parent); + Assert.Same(destination.transform, second.transform.parent); + Assert.Same(first.transform, child.transform.parent); + Assert.Equal(0, first.transform.siblingIndex); + Assert.Equal(1, second.transform.siblingIndex); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + Assert.Null(first.transform.parent); + Assert.Null(second.transform.parent); + Assert.Same(first.transform, child.transform.parent); + Assert.True(m_runtime.interactions.history.Redo().succeeded); + Assert.Same(destination.transform, second.transform.parent); + Assert.Throws(() => m_edits.MoveGameObjects([destination], scene, child)); + Assert.Same(destination.transform, first.transform.parent); + } +} + +[StableTypeId("9ea6d04d-4ed3-48f3-ac3a-b09372a575ab")] +internal sealed class BatchValidationComponent : GameComponent +{ + [SerializableProperty] + public int value { get; set; } + + [SerializableProperty] + public bool rejectAnswer { get; set; } + + [OnSerializableRestored] + private void Validate() + { + if (rejectAnswer && value == 42) + throw new InvalidOperationException("The current owner rejected this batch value."); + } +} diff --git a/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.cs b/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.cs index 2a69fda83..fa569eba9 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/SceneHistoryTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Extensibility.Modules; @@ -22,13 +21,9 @@ using Xunit; namespace Inno.Editor.Interactions.Tests; - -public sealed class SceneHistoryTests : IDisposable +public sealed partial class SceneHistoryTests : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoSceneHistoryTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoSceneHistoryTests", Guid.NewGuid().ToString("N")); private readonly EngineHost m_host; private readonly RuntimeSession m_session; private readonly IDisposable m_executionScope; @@ -36,26 +31,16 @@ public sealed class SceneHistoryTests : IDisposable private readonly EditorInteractionRuntime m_runtime; private readonly SceneEdits m_edits; private readonly EditorReloadCoordinator m_reloads = new(); - public SceneHistoryTests() { Directory.CreateDirectory(Path.Combine(m_projectRoot, "Assets")); SceneHistoryProbe.Reset(); - m_host = new EngineHostBuilder() - .UseMetadataSources(new DotNetAssemblyCatalogSource(typeof(SceneHistoryTests).Assembly), - new ReflectionTypeCatalogSource(), new ReflectionSerializationMetadataSource()) - .Build(); - m_session = m_host.CreateSession(new RuntimeSessionOptions - { - kind = RuntimeSessionKind.Edit, - applicationId = "inno.tests.scene-history", - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine( - m_projectRoot, - "Library", - "PersistentData", - "inno.tests.scene-history"), "Logs")), - jobExecutionMode = RuntimeJobExecutionMode.SingleThread - }); + m_host = new EngineHostBuilder().UseMetadataSources( + new DotNetAssemblyCatalogSource(typeof(SceneHistoryTests).Assembly), + new ReflectionTypeCatalogSource(), + new ReflectionSerializationMetadataSource() + ).Build(); + m_session = m_host.CreateSession(new RuntimeSessionOptions { kind = RuntimeSessionKind.Edit, applicationId = "inno.tests.scene-history", createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(m_projectRoot, "Library", "PersistentData", "inno.tests.scene-history"), "Logs")), jobExecutionMode = RuntimeJobExecutionMode.SingleThread }); m_executionScope = m_session.EnterExecutionScope(); m_assets = new AssetPipeline( m_host.modules, @@ -64,23 +49,14 @@ public SceneHistoryTests() new IdentityAllocator(), m_host.diagnostics, m_host.logs, - AssetPipelineOptions.Create( - Path.Combine(m_projectRoot, "Assets"), - Path.Combine(m_projectRoot, "Library")) with - { - enableFileSystemWatcher = false - }); + AssetPipelineOptions.Create(Path.Combine(m_projectRoot, "Assets"), Path.Combine(m_projectRoot, "Library"))with { enableFileSystemWatcher = false } + ); m_runtime = new EditorInteractionRuntime( new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_host.types, m_host.logs, - [ - m_host.types, - m_host.serialization, - m_session, - m_assets, - m_reloads - ]); + [m_host.types, m_host.serialization, m_session, m_assets, m_reloads] + ); m_runtime.Start(); _ = m_runtime.panelCount; m_edits = Assert.IsType(SceneHistoryProbe.edits); @@ -107,22 +83,14 @@ public void PropertyUndoRestoresOnlyThePropertyOnTheExistingSceneGraph() Guid sceneId = scene.identity.persistentId; Guid objectId = gameObject.identity.persistentId; Guid componentId = component.identity.persistentId; - - Assert.True(m_edits.ChangeProperty( - component, - nameof(HistoryComponent.value), - () => component.value = 25, - "Change Value", - "tests:component-value")); + Assert.True(m_edits.ChangeProperty(component, nameof(HistoryComponent.value), () => component.value = 25, "Change Value", "tests:component-value")); Assert.Equal(25, component.value); - EditorHistoryResult undo = m_runtime.interactions.history.Undo(); Assert.True(undo.succeeded, undo.message); Assert.Equal(10, component.value); Assert.Same(scene, IdentityAllocator.current.Get(sceneId)); Assert.Same(gameObject, IdentityAllocator.current.Get(objectId)); Assert.Same(component, IdentityAllocator.current.Get(componentId)); - Assert.True(m_runtime.interactions.history.Redo().succeeded); Assert.Equal(25, component.value); } @@ -133,20 +101,13 @@ public void PropertyUndoRestoresEverySiblingChangedByThePrimarySetter() var component = CreateScene().CreateObject("Coupled").AddComponent(); Assert.True(component.primary); Assert.Equal(CoupledHistoryMode.Base, component.mode); - - Assert.True(m_edits.ChangeProperty( - component, - nameof(CoupledHistoryComponent.mode), - () => component.mode = CoupledHistoryMode.Overlay, - "Change Coupled Mode")); + Assert.True(m_edits.ChangeProperty(component, nameof(CoupledHistoryComponent.mode), () => component.mode = CoupledHistoryMode.Overlay, "Change Coupled Mode")); Assert.Equal(CoupledHistoryMode.Overlay, component.mode); Assert.False(component.primary); - EditorHistoryResult undo = m_runtime.interactions.history.Undo(); Assert.True(undo.succeeded, undo.message); Assert.Equal(CoupledHistoryMode.Base, component.mode); Assert.True(component.primary); - EditorHistoryResult redo = m_runtime.interactions.history.Redo(); Assert.True(redo.succeeded, redo.message); Assert.Equal(CoupledHistoryMode.Overlay, component.mode); @@ -159,28 +120,13 @@ public void TransformManipulationTransactionUndoesAllLocalValuesAtomically() GameObject gameObject = CreateScene().CreateObject("Manipulated"); Transform transform = gameObject.transform; var position = new Inno.Core.Mathematics.Vector3(3f, -2f, 4f); - Inno.Core.Mathematics.Quaternion rotation = - Inno.Core.Mathematics.Quaternion.CreateFromYawPitchRoll(0.2f, 0.3f, 0.4f); + Inno.Core.Mathematics.Quaternion rotation = Inno.Core.Mathematics.Quaternion.CreateFromYawPitchRoll(0.2f, 0.3f, 0.4f); var scale = new Inno.Core.Mathematics.Vector3(2f, 3f, 4f); - - using (EditorHistoryTransaction transaction = - m_runtime.interactions.history.BeginTransaction("Move GameObject")) + using (EditorHistoryTransaction transaction = m_runtime.interactions.history.BeginTransaction("Move GameObject")) { - Assert.True(m_edits.ChangeProperty( - transform, - nameof(Transform.localPosition), - () => transform.localPosition = position, - "Move GameObject")); - Assert.True(m_edits.ChangeProperty( - transform, - nameof(Transform.localRotation), - () => transform.localRotation = rotation, - "Move GameObject")); - Assert.True(m_edits.ChangeProperty( - transform, - nameof(Transform.localScale), - () => transform.localScale = scale, - "Move GameObject")); + Assert.True(m_edits.ChangeProperty(transform, nameof(Transform.localPosition), () => transform.localPosition = position, "Move GameObject")); + Assert.True(m_edits.ChangeProperty(transform, nameof(Transform.localRotation), () => transform.localRotation = rotation, "Move GameObject")); + Assert.True(m_edits.ChangeProperty(transform, nameof(Transform.localScale), () => transform.localScale = scale, "Move GameObject")); transaction.Commit(); } @@ -207,9 +153,7 @@ public void WorldPositionEditRestoresLocalStorageAndWorldResultThroughHistory() var beforeLocal = child.localPosition; var beforeWorld = child.worldPosition; var requestedWorld = new Inno.Core.Mathematics.Vector3(11f, 7f, 20f); - - Assert.True(m_edits.ChangeProperty(child, nameof(Transform.localPosition), - () => child.worldPosition = requestedWorld, "Edit World Position")); + Assert.True(m_edits.ChangeProperty(child, nameof(Transform.localPosition), () => child.worldPosition = requestedWorld, "Edit World Position")); var afterLocal = child.localPosition; Assert.Equal(requestedWorld, child.worldPosition); Assert.NotEqual(beforeLocal, afterLocal); @@ -228,27 +172,23 @@ public void ComponentAddRemoveResetAndOrderPreserveLogicalIdentity() GameObject gameObject = scene.CreateObject("Object"); var first = (HistoryComponent)m_edits.AddComponent(gameObject, typeof(HistoryComponent)); Guid firstId = first.identity.persistentId; - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Null(IdentityAllocator.current.Get(firstId)); Assert.True(m_runtime.interactions.history.Redo().succeeded); var restoredFirst = Assert.IsType(IdentityAllocator.current.Get(firstId)); Assert.NotSame(first, restoredFirst); Assert.Equal(7, restoredFirst.value); - var second = (HistoryComponent)m_edits.AddComponent(gameObject, typeof(HistoryComponent)); m_edits.SetComponentIndex(second, 1); Assert.Equal(1, gameObject.GetComponentIndex(second)); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(2, gameObject.GetComponentIndex(second)); - restoredFirst.value = 99; m_edits.ResetComponent(restoredFirst); Assert.Equal(7, restoredFirst.value); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(99, restoredFirst.value); Assert.Same(restoredFirst, IdentityAllocator.current.Get(firstId)); - Assert.True(m_edits.RemoveComponent(restoredFirst)); Assert.Null(IdentityAllocator.current.Get(firstId)); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -265,18 +205,14 @@ public void RemovingComponentRestoresSerializedAssetReferences() AssetPath assetPath = AssetPath.Project("Text/shared.txt"); Assert.True(m_assets.Import(assetPath)); TextAsset asset = m_assets.Load(assetPath); - GameObject owner = CreateScene().CreateObject("Audio Source Owner"); var component = owner.AddComponent(); component.asset = asset; Guid componentId = component.identity.persistentId; - Assert.True(m_edits.RemoveComponent(component)); EditorHistoryResult undo = m_runtime.interactions.history.Undo(); - Assert.True(undo.succeeded, undo.message); - var restored = Assert.IsType( - IdentityAllocator.current.Get(componentId)); + var restored = Assert.IsType(IdentityAllocator.current.Get(componentId)); Assert.Same(asset, restored.asset); Assert.True(m_runtime.interactions.history.Redo().succeeded); Assert.Null(IdentityAllocator.current.Get(componentId)); @@ -299,13 +235,11 @@ public void UndoMissingPluginComponentRecoversItsAssetAndKeepsTheRedoBranch() m_assets.Rescan(); ReloadHistoryModule(install: false); m_host.modules.generations.Wait(); - EditorHistoryResult undo = m_runtime.interactions.history.Undo(); Assert.True(undo.succeeded, undo.message); MissingGameComponent missing = Assert.IsType(m_session.scenes.Find(componentId)); int missingRuntimeId = Assert.IsType(missing.identity.runtimeId); Assert.Equal(componentId, missing.identity.persistentId); - File.WriteAllText(sourcePath, "shared recovery content"); File.WriteAllBytes(sourcePath + ".imeta", sourceIdentity); m_assets.Rescan(); @@ -330,7 +264,6 @@ public void FailedMissingRecoveryPreservesPlaceholderAndHistoryForRetry() m_host.modules.generations.Wait(); Assert.True(m_runtime.interactions.history.Undo().succeeded); MissingGameComponent missing = Assert.IsType(m_session.scenes.Find(componentId)); - AssertRejectedHistoryModule(); m_host.modules.generations.Wait(); Assert.Same(missing, m_session.scenes.Find(componentId)); @@ -358,9 +291,7 @@ bool rejectRestore } [MethodImpl(MethodImplOptions.NoInlining)] - private void AssertRejectedHistoryModule() - => Assert.ThrowsAny(() => ReloadHistoryModule(install: true)); - + private void AssertRejectedHistoryModule() => Assert.ThrowsAny(() => ReloadHistoryModule(install: true)); [Fact] public void MissingComponentEditsPreserveOriginalStateAndRecoveryDoesNotDirtySavedScene() { @@ -410,7 +341,7 @@ public void MissingSystemUndoAndRemovalKeepItsLogicalTypeForRecovery() m_host.modules.generations.Wait(); Assert.True(m_runtime.interactions.history.Undo().succeeded); MissingGameSystem missing = Assert.IsType(m_session.scenes.Find(id)); - Assert.True(m_edits.RemoveSystem(scene, missing)); + Assert.True(m_edits.RemoveSystem(missing)); Assert.True(m_runtime.interactions.history.Undo().succeeded); ReloadHistoryModule(install: true); AssertRecoveredSystem(id); @@ -427,7 +358,7 @@ private Guid AddAndRemoveReloadableSystem(GameScene scene) GameSystem system = scene.AddSystem(type); type.GetProperty("value")!.SetValue(system, 92); Guid id = system.identity.persistentId; - Assert.True(m_edits.RemoveSystem(scene, system)); + Assert.True(m_edits.RemoveSystem(system)); return id; } @@ -458,22 +389,18 @@ int missingRuntimeId private void ReloadHistoryModule(bool install) { DotNetModuleSource request = CreateHistoryRequest(); - using AssemblyReloadSession reload = install - ? m_host.modules.BeginReload([request]) - : m_host.modules.BeginReload([], [request.moduleName]); + using AssemblyReloadSession reload = install ? m_host.modules.BeginReload([request]) : m_host.modules.BeginReload([], [request.moduleName]); _ = m_reloads.Execute(reload); } - private DotNetModuleSource CreateHistoryRequest() - => new() - { - moduleName = "SceneHistoryRecovery", - artifactRootDirectory = Path.Combine(m_projectRoot, "Library", "Modules"), - mainAssemblyPath = Path.Combine(AppContext.BaseDirectory, "Modules", "SceneReload", "Inno.Scene.Reload.TestModule.dll"), - domain = AssemblyDomain.InnoPlugin, - scope = AssemblyScope.Runtime - }; - + private DotNetModuleSource CreateHistoryRequest() => new() + { + moduleName = "SceneHistoryRecovery", + artifactRootDirectory = Path.Combine(m_projectRoot, "Library", "Modules"), + mainAssemblyPath = Path.Combine(AppContext.BaseDirectory, "Modules", "SceneReload", "Inno.Scene.Reload.TestModule.dll"), + domain = AssemblyDomain.InnoPlugin, + scope = AssemblyScope.Runtime + }; [Fact] public void ExternalResolversAreRestoredBeforeDomainPropertyCompensation() { @@ -481,8 +408,7 @@ public void ExternalResolversAreRestoredBeforeDomainPropertyCompensation() var participant = new ExternalRecoveryOrder(() => externalCandidateActive); using IDisposable registration = m_reloads.Register(participant); using AssemblyReloadSession reload = m_host.modules.BeginReload([CreateHistoryRequest()]); - Assert.Throws(() => m_reloads.Execute(reload, - new ExternalContentChange(() => externalCandidateActive = true, () => externalCandidateActive = false))); + Assert.Throws(() => m_reloads.Execute(reload, new ExternalContentChange(() => externalCandidateActive = true, () => externalCandidateActive = false))); Assert.True(participant.restored); Assert.False(externalCandidateActive); reload.Dispose(); @@ -495,30 +421,45 @@ private sealed class ExternalContentChange( Action restore ) : IGenerationChange { - public void PrepareForActivation() { } + public void PrepareForActivation() + { + } + public void Apply() => activate(); - public void Complete() { } - public void RollbackStructure() { } + public void Complete() + { + } + + public void RollbackStructure() + { + } + public void RestorePreviousState() => restore(); } private sealed class ExternalRecoveryOrder(Func externalCandidateActive) : IEditorReloadParticipant, IGenerationChange { internal bool restored { get; private set; } + public IGenerationChange Capture(AssemblyReloadContext context) => this; - public void RefreshDiagnostics() { } + public void RefreshDiagnostics() + { + } + public void PrepareForActivation() => Assert.False(externalCandidateActive()); public void Apply() { Assert.True(externalCandidateActive()); throw new InvalidOperationException("Rejected domain candidate."); } + public void RollbackStructure() => Assert.True(externalCandidateActive()); public void RestorePreviousState() { Assert.False(externalCandidateActive()); restored = true; } + public void Complete() => Assert.Fail("Failed candidate cannot commit."); } @@ -538,14 +479,12 @@ public void RemovingAnElementAndSubtreeRestoresExternalSceneReferences() Guid componentId = targetComponent.identity.persistentId; Guid rootId = targetObject.identity.persistentId; Guid childId = targetChild.identity.persistentId; - Assert.True(m_edits.RemoveComponent(targetComponent)); EditorHistoryResult restoreComponentResult = m_runtime.interactions.history.Undo(); Assert.True(restoreComponentResult.succeeded, restoreComponentResult.message); var restoredComponent = Assert.IsType(IdentityAllocator.current.Get(componentId)); Assert.Same(restoredComponent, references.targetComponent); Assert.Equal(41, restoredComponent.value); - Assert.True(m_edits.DeleteGameObject(targetObject)); Assert.True(m_runtime.interactions.history.Undo().succeeded); GameObject restoredRoot = Assert.IsType(IdentityAllocator.current.Get(rootId)); @@ -553,7 +492,6 @@ public void RemovingAnElementAndSubtreeRestoresExternalSceneReferences() Assert.Same(restoredRoot.transform, restoredChild.transform.parent); Assert.Same(restoredChild, references.targetObject); Assert.Equal(componentId, references.targetComponent?.identity.persistentId); - Assert.True(m_runtime.interactions.history.Redo().succeeded); Assert.Null(IdentityAllocator.current.Get(rootId)); } @@ -564,35 +502,14 @@ public void ElementRestoreRejectsAnotherLogicalTypesPayloadBeforeAttachingElemen GameScene scene = CreateScene(); GameObject sourceObject = scene.CreateObject("Source"); var source = sourceObject.AddComponent(); - byte[] incompatibleState = SceneElementSerialization.CaptureState( - source, - m_host.serialization, - m_assets); + byte[] incompatibleState = SceneElementSerialization.CaptureState(source, m_host.serialization, m_assets); GameObject owner = scene.CreateObject("Owner"); TypeRef componentType = m_host.types.GetTypeRef(typeof(HistoryComponent)); TypeRef systemType = m_host.types.GetTypeRef(typeof(HistorySystem)); Guid componentId = Guid.NewGuid(); Guid systemId = Guid.NewGuid(); - - InvalidDataException componentFailure = Assert.Throws(() => - SceneElementSerialization.RestoreComponent( - owner, - componentType, - componentId, - componentIndex: owner.GetComponents().Count, - incompatibleState, - m_host.serialization, - m_assets)); - InvalidDataException systemFailure = Assert.Throws(() => - SceneElementSerialization.RestoreSystem( - scene, - systemType, - systemId, - systemIndex: scene.GetSystems().Count, - incompatibleState, - m_host.serialization, - m_assets)); - + InvalidDataException componentFailure = Assert.Throws(() => SceneElementSerialization.RestoreComponent(owner, componentType, componentId, componentIndex: owner.GetComponents().Count, incompatibleState, m_host.serialization, m_assets)); + InvalidDataException systemFailure = Assert.Throws(() => SceneElementSerialization.RestoreSystem(scene, systemType, systemId, systemIndex: scene.GetSystems().Count, incompatibleState, m_host.serialization, m_assets)); Assert.Contains("type", componentFailure.Message, StringComparison.OrdinalIgnoreCase); Assert.Contains("type", systemFailure.Message, StringComparison.OrdinalIgnoreCase); Assert.Null(IdentityAllocator.current.Get(componentId)); @@ -605,24 +522,11 @@ public void ElementRestoreRemovesPartialElementWhenRestoreCallbackFails() GameScene scene = CreateScene(); GameObject sourceOwner = scene.CreateObject("Source"); var source = sourceOwner.AddComponent(); - byte[] state = SceneElementSerialization.CaptureState( - source, - m_host.serialization, - m_assets); + byte[] state = SceneElementSerialization.CaptureState(source, m_host.serialization, m_assets); GameObject targetOwner = scene.CreateObject("Target"); TypeRef typeRef = m_host.types.GetTypeRef(typeof(RestoreCleanupFailureComponent)); Guid persistentId = Guid.NewGuid(); - - _ = Assert.ThrowsAny(() => - SceneElementSerialization.RestoreComponent( - targetOwner, - typeRef, - persistentId, - componentIndex: targetOwner.GetComponents().Count, - state, - m_host.serialization, - m_assets)); - + _ = Assert.ThrowsAny(() => SceneElementSerialization.RestoreComponent(targetOwner, typeRef, persistentId, componentIndex: targetOwner.GetComponents().Count, state, m_host.serialization, m_assets)); Assert.Null(IdentityAllocator.current.Get(persistentId)); } @@ -635,25 +539,14 @@ public void FailedSubtreeRestoreRemovesEveryCreatedObjectDespiteDestructionCallb GameObject child = scene.CreateObject("Child"); child.transform.SetParent(root.transform); _ = child.AddComponent(); - byte[] state = SceneSubtreeSerialization.Capture( - root, - m_host.serialization, - m_assets); + byte[] state = SceneSubtreeSerialization.Capture(root, m_host.serialization, m_assets); Guid rootId = root.identity.persistentId; Guid childId = child.identity.persistentId; - Assert.True(scene.DestroyObject(root)); SubtreeRestoreFailureComponent.throwOnDestroy = true; try { - _ = Assert.ThrowsAny(() => - SceneSubtreeSerialization.Restore( - scene, - state, - m_host.serialization, - m_assets, - parent: null, - siblingIndex: 0)); + _ = Assert.ThrowsAny(() => SceneSubtreeSerialization.Restore(scene, state, m_host.serialization, m_assets, parent: null, siblingIndex: 0)); } finally { @@ -676,7 +569,6 @@ public void FailedSubtreeHistoryRestoreReportsPreservedOnlyAfterExactSceneRecove _ = child.AddComponent(); Guid rootId = root.identity.persistentId; Guid childId = child.identity.persistentId; - Assert.True(m_edits.DeleteGameObject(root)); SubtreeRestoreFailureComponent.throwOnDestroy = true; EditorHistoryResult result; @@ -705,12 +597,8 @@ public void HierarchyUndoRestoresOnlyAffectedPlacements() GameObject secondParent = scene.CreateObject("Second"); GameObject child = scene.CreateObject("Child"); child.transform.SetParent(firstParent.transform); - - Assert.True(m_edits.ChangeHierarchy( - child, - _ => child.transform.SetParent(secondParent.transform))); + Assert.True(m_edits.ChangeHierarchy(child, _ => child.transform.SetParent(secondParent.transform))); Assert.Same(secondParent.transform, child.transform.parent); - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Same(firstParent.transform, child.transform.parent); Assert.True(m_runtime.interactions.history.Redo().succeeded); @@ -725,18 +613,15 @@ public void SystemHistoryPreservesIdentityStateAndExplicitOrder() var second = (HistorySystem)m_edits.AddSystem(scene, typeof(HistorySystem)); first.value = 30; Guid firstId = first.identity.persistentId; - m_edits.SetSystemIndex(scene, second, 0); Assert.Equal(0, scene.GetSystemIndex(second)); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(1, scene.GetSystemIndex(second)); - - m_edits.ResetSystem(scene, first); + m_edits.ResetSystem(first); Assert.Equal(11, first.value); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(30, first.value); - - Assert.True(m_edits.RemoveSystem(scene, first)); + Assert.True(m_edits.RemoveSystem(first)); Assert.True(m_runtime.interactions.history.Undo().succeeded); var restored = Assert.IsType(IdentityAllocator.current.Get(firstId)); Assert.Equal(30, restored.value); @@ -748,13 +633,10 @@ public void SceneDocumentUndoRecreatesTheSameLogicalScene() GameScene scene = m_edits.CreateScene(); scene.CreateObject("Persistent"); Guid sceneId = scene.identity.persistentId; - Assert.True(m_edits.CloseScene(scene)); Assert.Empty(SceneManager.loadedScenes); Assert.Null(IdentityAllocator.current.Get(sceneId)); - Assert.True( - m_runtime.interactions.history.canUndo, - m_runtime.interactions.history.undoUnavailableReason); + Assert.True(m_runtime.interactions.history.canUndo, m_runtime.interactions.history.undoUnavailableReason); EditorHistoryResult undo = m_runtime.interactions.history.Undo(); Assert.True(undo.succeeded, undo.message); GameScene restored = Assert.IsType(IdentityAllocator.current.Get(sceneId)); @@ -771,10 +653,8 @@ public void SceneHistoryRemainsUsableAfterHostTypeCacheRefresh() GameScene scene = CreateScene(); GameObject gameObject = scene.CreateObject("Before"); m_edits.RenameGameObject(gameObject, "After"); - m_host.types.Rebuild(); _ = m_runtime.panelCount; - Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal("Before", gameObject.name); Assert.True(m_runtime.interactions.history.Redo().succeeded); @@ -786,10 +666,8 @@ public void SceneRenameUsesTheSameScalarHistoryContractAsGameObjectRename() { GameScene scene = CreateScene(); GameObject gameObject = scene.CreateObject("Object Before"); - m_edits.RenameScene(scene, "Scene After"); m_edits.RenameGameObject(gameObject, "Object After"); - Assert.Equal("Scene After", scene.name); Assert.Equal("Object After", gameObject.name); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -807,9 +685,7 @@ public void GameObjectTagUndoAndRedoRefreshSceneQueries() { GameScene scene = CreateScene(); GameObject gameObject = scene.CreateObject("Tagged"); - m_edits.SetGameObjectTag(gameObject, "Player"); - Assert.Equal("Player", gameObject.tag); Assert.Same(gameObject, scene.FindObjectWithTag("Player")); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -826,9 +702,7 @@ public void GameObjectLayerUndoAndRedoRefreshSceneQueries() GameScene scene = CreateScene(); GameObject gameObject = scene.CreateObject("Layered"); var gameplay = new GameLayer(6); - m_edits.SetGameObjectLayer(gameObject, gameplay); - Assert.Equal(gameplay, gameObject.layer); Assert.Same(gameObject, scene.FindObjectWithLayer(gameplay)); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -839,8 +713,7 @@ public void GameObjectLayerUndoAndRedoRefreshSceneQueries() Assert.Same(gameObject, scene.FindObjectWithLayer(gameplay)); } - private GameScene CreateScene() - => m_session.scenes.LoadNewSceneAdditive(); + private GameScene CreateScene() => m_session.scenes.LoadNewSceneAdditive(); } [EditorModule("tests.scene-history-probe", order: 220)] @@ -855,7 +728,6 @@ IEditorSceneWorkspace workspace } public static SceneEdits? edits { get; private set; } - public static IEditorSceneWorkspace? documents { get; private set; } public static void Reset() @@ -889,13 +761,8 @@ internal sealed class CoupledHistoryComponent : GameBehavior { private bool m_primary = true; private CoupledHistoryMode m_mode; - [SerializableProperty] - public bool primary - { - get => m_primary; - set => m_primary = value && mode == CoupledHistoryMode.Base; - } + public bool primary { get => m_primary; set => m_primary = value && mode == CoupledHistoryMode.Base; } [SerializableProperty] public CoupledHistoryMode mode @@ -949,8 +816,7 @@ internal sealed class RestoreCleanupFailureComponent : GameBehavior public int value { get; set; } = 5; [OnSerializableRestored] - private void OnRestored() - => throw new InvalidOperationException("Injected restore callback failure."); + private void OnRestored() => throw new InvalidOperationException("Injected restore callback failure."); } [StableTypeId("8503e97b-1845-4ab2-a116-1ece04934a4b")] @@ -958,14 +824,11 @@ private void OnRestored() internal sealed class SubtreeRestoreFailureComponent : GameBehavior { internal static bool throwOnDestroy; - [SerializableProperty] public int value { get; set; } = 5; [OnSerializableRestored] - private void OnRestored() - => throw new InvalidOperationException("Injected subtree restore callback failure."); - + private void OnRestored() => throw new InvalidOperationException("Injected subtree restore callback failure."); protected override void OnDestroy() { if (throwOnDestroy) diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs index 1370d8129..c3cceb9f3 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs @@ -11,6 +11,7 @@ using Inno.Core.Diagnostics; using Inno.Core.Logging; using Inno.Editor.Core; +using Inno.Editor.ImGui; using Inno.Editor.Diagnostics; using Inno.Editor.Interactions; using Inno.Extensibility.Modules; @@ -50,7 +51,13 @@ float scale using var console = new EditorConsole(logs, hub, new InactivePlayMode()); console.Start(); using var reporter = hub.CreateReporter(new("shader", "Rendering")); - using var runtime = new EditorInteractionRuntime(new EditorContext(root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), types, logs, [console]); + using var ui = new EditorUiSession(); + using var runtime = new EditorInteractionRuntime( + new EditorContext(root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), + types, + logs, + [console] + ); runtime.Start(); logs.Flush(); console.Clear(); @@ -61,6 +68,7 @@ float scale panel.isOpen = true; void Draw(float width) { + using IDisposable scope = ui.Enter(); ImGuiApi.NewFrame(); ImGuiApi.SetNextWindowPos(new(0, 0)); ImGuiApi.SetNextWindowSize(new(width, 700)); @@ -118,7 +126,9 @@ private sealed class InactivePlayMode : IEditorPlayMode public bool isPlaying => false; public string? lastFailure => null; public LogSessionId activeSessionId => LogSessionId.none; - public event Action? stateChanged { add { } remove { } } + private readonly Inno.Core.Events.EventDispatcher m_stateDispatcher = new(); + public IDisposable ListenStateChanges(Action listener) => m_stateDispatcher.CreateHub(); + public bool EnterPlayMode() => false; public bool ExitPlayMode() => false; } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/EditorPlayModeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/EditorPlayModeTests.cs index 280d6de42..51b079158 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/EditorPlayModeTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/EditorPlayModeTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Core.Identity; @@ -27,25 +26,21 @@ using Xunit; namespace Inno.Editor.PlayMode.Tests; - public sealed class EditorPlayModeTests : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoEditorPlayModeTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoEditorPlayModeTests", Guid.NewGuid().ToString("N")); private readonly EngineHost m_engineHost; private readonly RuntimeSession m_editSession; private readonly AssetPipeline m_authoringAssets; private readonly IDisposable m_editScope; - public EditorPlayModeTests() { Directory.CreateDirectory(Path.Combine(m_projectRoot, "Assets")); - m_engineHost = new EngineHostBuilder() - .UseMetadataSources(new DotNetAssemblyCatalogSource(typeof(EditorPlayModeTests).Assembly), - new ReflectionTypeCatalogSource(), new ReflectionSerializationMetadataSource()) - .Build(); + m_engineHost = new EngineHostBuilder().UseMetadataSources( + new DotNetAssemblyCatalogSource(typeof(EditorPlayModeTests).Assembly), + new ReflectionTypeCatalogSource(), + new ReflectionSerializationMetadataSource() + ).Build(); m_editSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Edit)); m_editScope = m_editSession.EnterExecutionScope(); m_authoringAssets = new AssetPipeline( @@ -55,12 +50,8 @@ public EditorPlayModeTests() new IdentityAllocator(), m_engineHost.diagnostics, m_engineHost.logs, - AssetPipelineOptions.Create( - Path.Combine(m_projectRoot, "Assets"), - Path.Combine(m_projectRoot, "Library")) with - { - enableFileSystemWatcher = false - }); + AssetPipelineOptions.Create(Path.Combine(m_projectRoot, "Assets"), Path.Combine(m_projectRoot, "Library"))with { enableFileSystemWatcher = false } + ); } public void Dispose() @@ -73,25 +64,38 @@ public void Dispose() Directory.Delete(m_projectRoot, recursive: true); } + [Fact] + public void StateSubscriptionsNotifyAllObserversAndReleaseIndependently() + { + using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, new FakeScriptCompilation(EditorScriptCompilationState.Ready), new FakeScenePlayMode()); + int observed = 0; + using IDisposable failing = harness.playMode.ListenStateChanges(_ => throw new InvalidOperationException("Rejected observer.")); + using IDisposable healthy = harness.playMode.ListenStateChanges(_ => observed++); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + AggregateException error = Assert.Throws(() => harness.playMode.EnterPlayMode()); + Assert.Single(error.InnerExceptions); + Assert.Equal(1, observed); + Assert.Equal(EditorPlayModeState.Compiling, harness.playMode.state); + failing.Dispose(); + healthy.Dispose(); + Assert.True(harness.playMode.ExitPlayMode()); + Assert.Equal(1, observed); + } + [Fact] public void PlayShortcutStopsTheSessionFromAFocusedGameView() { - using var harness = new PlayModeHarness( - m_projectRoot, - m_engineHost, - new FakeScriptCompilation(EditorScriptCompilationState.Ready), - new FakeScenePlayMode()); - using var modules = new Inno.Extensibility.Modules.ModuleHost( - new Inno.Extensibility.Modules.ModuleHostOptions - { - catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorPlayModeController).Assembly) - }); + using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, new FakeScriptCompilation(EditorScriptCompilationState.Ready), new FakeScenePlayMode()); + using var modules = new Inno.Extensibility.Modules.ModuleHost(new Inno.Extensibility.Modules.ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(EditorPlayModeController).Assembly) }); using var types = new TypeCatalog(modules, new ReflectionTypeCatalogSource()); using var interactions = new EditorInteractionRuntime( new EditorContext(m_projectRoot, new EditorKeyboardPolicy(KeyModifier.Control, "Super")), types, m_engineHost.logs, - [harness.playMode]); + [harness.playMode] + ); interactions.Start(); Assert.True(harness.playMode.EnterPlayMode()); harness.Update(); @@ -134,8 +138,7 @@ public void ReloadGuardPreservesStartupCompilationAndPinsThePlayingGeneration() [Fact] public void ReleasingGuardRegistrationDoesNotRetainItsBlockingState() { - using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, - new FakeScriptCompilation(EditorScriptCompilationState.Compiling), new FakeScenePlayMode()); + using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, new FakeScriptCompilation(EditorScriptCompilationState.Compiling), new FakeScenePlayMode()); var reloads = new EditorReloadCoordinator(); IDisposable registration = reloads.RegisterGuard((IEditorReloadGuard)harness.playMode); Assert.True(harness.playMode.EnterPlayMode()); @@ -155,18 +158,14 @@ public void EntryWaitsForCompilationAndExitRestoresEditingHistory() harness.Update(); Assert.Equal(EditorPlayModeState.Compiling, harness.playMode.state); Assert.Equal(0, scenes.beginCount); - scripting.state = EditorScriptCompilationState.Ready; harness.Update(); Assert.Equal(EditorPlayModeState.Preparing, harness.playMode.state); harness.Update(); - Assert.True( - harness.playMode.state == EditorPlayModeState.Playing, - harness.playMode.lastFailure); + Assert.True(harness.playMode.state == EditorPlayModeState.Playing, harness.playMode.lastFailure); Assert.Equal(1, scenes.beginCount); Assert.Equal(1, harness.history.beginCount); Assert.Equal(0, harness.history.disposeCount); - Assert.True(harness.playMode.ExitPlayMode()); harness.Update(); Assert.Equal(EditorPlayModeState.Editing, harness.playMode.state); @@ -197,6 +196,7 @@ public void PendingStopRetainsHistoryAndBlocksNewGenerationUntilWorkDrains(bool Assert.True(harness.playMode.ExitPlayMode()); harness.Update(); } + Assert.Equal(EditorPlayModeState.Stopping, harness.playMode.state); Assert.Equal(0, harness.history.disposeCount); Assert.Throws(() => m_engineHost.generations.EnsureReady("reload while stopping")); @@ -223,10 +223,8 @@ public void CompilationFailureReturnsToEditWithoutReplacingScenes() var scripting = new FakeScriptCompilation(EditorScriptCompilationState.Failed); var scenes = new FakeScenePlayMode(); using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, scripting, scenes); - Assert.True(harness.playMode.EnterPlayMode()); harness.Update(); - Assert.Equal(EditorPlayModeState.Failed, harness.playMode.state); Assert.Contains("valid script generation", harness.playMode.lastFailure, StringComparison.OrdinalIgnoreCase); Assert.Equal(0, scenes.beginCount); @@ -238,11 +236,9 @@ public void EnteringPlayCanBeCancelledBeforeScriptsBecomeReady() var scripting = new FakeScriptCompilation(EditorScriptCompilationState.Compiling); var scenes = new FakeScenePlayMode(); using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, scripting, scenes); - Assert.True(harness.playMode.EnterPlayMode()); Assert.True(harness.playMode.ExitPlayMode()); harness.Update(); - Assert.Equal(EditorPlayModeState.Editing, harness.playMode.state); Assert.Equal(0, scenes.beginCount); } @@ -259,15 +255,12 @@ public void HostLoopDispatchesGameLifecycleOnlyWhilePlaying() world.LoadScene(scene); }); using var harness = new PlayModeHarness(m_projectRoot, m_engineHost, scripting, scenes); - harness.Simulate(0.016f); Assert.Null(system); - Assert.True(harness.playMode.EnterPlayMode()); harness.Update(); harness.Update(); harness.Simulate(0.02f); - Assert.NotNull(system); Assert.Equal(1, system.fixedCount); Assert.Equal(1, system.updateCount); @@ -288,11 +281,9 @@ public void SimulationFailureRequestsAndCompletesSafeEditRestoration() Assert.True(harness.playMode.EnterPlayMode()); harness.Update(); harness.Update(); - harness.Simulate(0.016f); Assert.Equal(EditorPlayModeState.Stopping, harness.playMode.state); harness.Update(); - Assert.Equal(EditorPlayModeState.Editing, harness.playMode.state); Assert.Equal(1, scenes.restoreCount); Assert.Contains("update failed", harness.playMode.lastFailure, StringComparison.OrdinalIgnoreCase); @@ -308,7 +299,8 @@ public void SceneSessionRestoresGraphIdentitySelectionAndEditValues() m_engineHost.types, m_engineHost.serialization, m_engineHost.logs, - selection); + selection + ); IEditorSceneWorkspace workspace = workspaceHost.workspace; var editScene = new GameScene("Edit Scene"); GameObject editObject = editScene.CreateObject("Edit Object"); @@ -316,9 +308,7 @@ public void SceneSessionRestoresGraphIdentitySelectionAndEditValues() Guid objectId = editObject.identity.persistentId; SceneManager.LoadScene(editScene); selection.SetSelection(editObject); - - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); using IDisposable session = workspaceHost.playMode.BeginPlayMode(runtimeSession); GameScene runtimeScene; GameObject runtimeObject; @@ -327,6 +317,7 @@ public void SceneSessionRestoresGraphIdentitySelectionAndEditValues() runtimeScene = Assert.Single(SceneManager.loadedScenes); runtimeObject = Assert.Single(runtimeScene.GetObjects()); } + Assert.NotSame(editScene, runtimeScene); Assert.NotSame(editObject, runtimeObject); Assert.Equal(sceneId, runtimeScene.identity.persistentId); @@ -341,11 +332,10 @@ public void SceneSessionRestoresGraphIdentitySelectionAndEditValues() runtimeObject.name = "Runtime Object"; _ = runtimeScene.CreateObject("Runtime Only"); } + Assert.False(workspace.IsDirty(runtimeScene)); Assert.Throws(() => workspace.Save(runtimeScene, string.Empty)); - session.Dispose(); - GameScene restoredScene = Assert.Single(SceneManager.loadedScenes); GameObject restoredObject = Assert.Single(restoredScene.GetObjects()); Assert.Same(editScene, restoredScene); @@ -366,12 +356,11 @@ public void DeletingTheLastRuntimeSceneRestoresTheEditSceneOnExit() m_authoringAssets, m_engineHost.types, m_engineHost.serialization, - m_engineHost.logs); + m_engineHost.logs + ); var editScene = new GameScene("Edit Scene"); SceneManager.LoadScene(editScene); - - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); IDisposable playLease = workspaceHost.playMode.BeginPlayMode(runtimeSession); using (runtimeSession.EnterExecutionScope()) { @@ -382,7 +371,6 @@ public void DeletingTheLastRuntimeSceneRestoresTheEditSceneOnExit() } playLease.Dispose(); - Assert.Same(editScene, Assert.Single(SceneManager.loadedScenes)); Assert.Same(editScene, Assert.Single(workspaceHost.workspace.scenes)); Assert.True(workspaceHost.workspace.canPersist); @@ -397,29 +385,21 @@ public void SceneSessionMaterializesSerializedAssetReferences() AssetPath assetPath = AssetPath.Project("Text/shared.txt"); Assert.True(m_authoringAssets.Import(assetPath)); TextAsset asset = m_authoringAssets.Load(assetPath); - using EditorSceneWorkspaceHost workspaceHost = EditorSceneWorkspaceFactory.Create( m_editSession, m_authoringAssets, m_engineHost.types, m_engineHost.serialization, - m_engineHost.logs); + m_engineHost.logs + ); var editScene = new GameScene("Asset Reference Scene"); - editScene.CreateObject("Asset Reference") - .AddComponent() - .asset = asset; + editScene.CreateObject("Asset Reference").AddComponent().asset = asset; SceneManager.LoadScene(editScene); - - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); using IDisposable playLease = workspaceHost.playMode.BeginPlayMode(runtimeSession); using IDisposable runtimeScope = runtimeSession.EnterExecutionScope(); - GameObject runtimeObject = Assert.Single( - Assert.Single(SceneManager.loadedScenes).GetObjects()); - TextAsset? runtimeAsset = runtimeObject - .GetComponent() - .asset; - + GameObject runtimeObject = Assert.Single(Assert.Single(SceneManager.loadedScenes).GetObjects()); + TextAsset? runtimeAsset = runtimeObject.GetComponent().asset; Assert.Same(asset, runtimeAsset); } @@ -431,42 +411,30 @@ public void GamePresentationSwitchesFromEditToPlayAndBackWithoutSharingObjects() m_authoringAssets, m_engineHost.types, m_engineHost.serialization, - m_engineHost.logs); + m_engineHost.logs + ); IEditorGameScenePresentation presentation = workspaceHost.gamePresentation; var editScene = new GameScene("Edit Presentation"); GameObject editObject = editScene.CreateObject("Edit Object"); SceneManager.LoadScene(editScene); - using ContentReadScope editing = presentation.Capture(); Assert.Same(editScene, Assert.Single(editing.GetValues())); Assert.Equal(editScene.identity.persistentId, editing.activeContent); - - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); IDisposable playLease = workspaceHost.playMode.BeginPlayMode(runtimeSession); using ContentReadScope playing = presentation.Capture(); GameScene runtimeScene = Assert.Single(playing.GetValues()); GameObject runtimeObject = Assert.Single(runtimeScene.GetObjects()); - Assert.NotSame(editScene, runtimeScene); Assert.NotSame(editObject, runtimeObject); Assert.Equal(runtimeScene.identity.persistentId, playing.activeContent); runtimeObject.name = "Runtime Object"; - runtimeObject.transform.localRotation = Quaternion.FromEulerAnglesXYZDegrees( - new Vector3(0f, 0f, 90f)); - Assert.Equal( - "Runtime Object", - Assert.Single(Assert.Single(playing.GetValues()).GetObjects()).name); - Assert.Equal( - 90f, - Assert.Single(Assert.Single(playing.GetValues()).GetObjects()) - .transform.localRotation.ToEulerAnglesXYZDegrees().z, - precision: 3); + runtimeObject.transform.localRotation = Quaternion.FromEulerAnglesXYZDegrees(new Vector3(0f, 0f, 90f)); + Assert.Equal("Runtime Object", Assert.Single(Assert.Single(playing.GetValues()).GetObjects()).name); + Assert.Equal(90f, Assert.Single(Assert.Single(playing.GetValues()).GetObjects()).transform.localRotation.ToEulerAnglesXYZDegrees().z, precision: 3); Assert.Equal("Edit Object", editObject.name); Assert.Equal(Quaternion.identity, editObject.transform.localRotation); - playLease.Dispose(); - using ContentReadScope restored = presentation.Capture(); Assert.Same(editScene, Assert.Single(restored.GetValues())); Assert.Equal(editScene.identity.persistentId, restored.activeContent); @@ -481,19 +449,15 @@ public void RejectedPlayWorldDoesNotReplaceTheEditPresentation() m_authoringAssets, m_engineHost.types, m_engineHost.serialization, - m_engineHost.logs); + m_engineHost.logs + ); IEditorGameScenePresentation presentation = workspaceHost.gamePresentation; var editScene = new GameScene("Edit Presentation"); SceneManager.LoadScene(editScene); - - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); using (runtimeSession.EnterExecutionScope()) runtimeSession.scenes.LoadScene(new GameScene("Unexpected Existing Runtime Scene")); - - Assert.Throws( - () => workspaceHost.playMode.BeginPlayMode(runtimeSession)); - + Assert.Throws(() => workspaceHost.playMode.BeginPlayMode(runtimeSession)); using ContentReadScope snapshot = presentation.Capture(); Assert.Same(editScene, Assert.Single(snapshot.GetValues())); Assert.Equal(editScene.identity.persistentId, snapshot.activeContent); @@ -504,20 +468,21 @@ public void RejectedPlayWorldDoesNotReplaceTheEditPresentation() public void ScenePresentationScopeRejectsStalePlayRootsAfterStop() { using EditorSceneWorkspaceHost workspaceHost = EditorSceneWorkspaceFactory.Create( - m_editSession, m_authoringAssets, m_engineHost.types, - m_engineHost.serialization, m_engineHost.logs); + m_editSession, + m_authoringAssets, + m_engineHost.types, + m_engineHost.serialization, + m_engineHost.logs + ); var editScene = new GameScene("Identity Presentation"); SceneManager.LoadScene(editScene); - using RuntimeSession runtimeSession = m_engineHost.CreateSession( - CreateSessionOptions(RuntimeSessionKind.Play)); + using RuntimeSession runtimeSession = m_engineHost.CreateSession(CreateSessionOptions(RuntimeSessionKind.Play)); IDisposable playLease = workspaceHost.playMode.BeginPlayMode(runtimeSession); using ContentReadScope playing = workspaceHost.gamePresentation.Capture(); Identity captured = Assert.Single(playing.contents); Assert.Equal(editScene.identity.persistentId, captured.persistentId); Assert.NotEqual(editScene.identity.runtimeIdentity, captured.runtimeIdentity); - playLease.Dispose(); - runtimeSession.Dispose(); Assert.Empty(playing.GetValues()); Assert.False(playing.TryGetValue(captured.persistentId, out _)); @@ -540,60 +505,58 @@ public sealed class CountingSystem : GameSystem public sealed class ThrowingSystem : GameSystem { - protected override void OnUpdate() - => throw new InvalidOperationException("Injected simulation failure."); + protected override void OnUpdate() => throw new InvalidOperationException("Injected simulation failure."); } private sealed class PlayModeHarness : IDisposable { private readonly EditorPlayModeController m_controller; private readonly FakeHistoryIsolation m_history = new(); - internal PlayModeHarness( string projectRoot, EngineHost engineHost, IEditorScriptCompilation scripting, IEditorScenePlayMode scenes, - IRuntimeSubsystemFactory? factory = null) - { + IRuntimeSubsystemFactory? factory = null + ) { m_controller = new EditorPlayModeController( engineHost, - new RuntimeSessionOptions - { + new RuntimeSessionOptions { kind = RuntimeSessionKind.Play, applicationId = "inno.tests.play", - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine( - projectRoot, - "PersistentData", - "inno.tests.play"), "Logs")), + createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(projectRoot, "PersistentData", "inno.tests.play"), "Logs")), jobExecutionMode = RuntimeJobExecutionMode.SingleThread, createSubsystems = _ => factory is null ? [] : [factory] }, scenes, scripting, m_history, - engineHost.logs); + engineHost.logs + ); } internal IEditorPlayMode playMode => m_controller; internal FakeHistoryIsolation history => m_history; internal void Update() => m_controller.AdvanceTransition(); - internal void Simulate(float deltaTime) => m_controller.Tick(deltaTime); - public void Dispose() { m_controller.Dispose(); } } - private sealed class FakeScriptCompilation(EditorScriptCompilationState initialState) - : IEditorScriptCompilation + private sealed class FakeScriptCompilation(EditorScriptCompilationState initialState) : IEditorScriptCompilation { + public bool requiresExclusiveProgress => false; + public float progress => 0f; + + public void CancelCompilation() + { + } + private EditorScriptCompilationState m_state = initialState; private FakeCompilationTicket? m_ticket; - public IScriptCompilationTicket RequestCompilation() { m_ticket = new FakeCompilationTicket(); @@ -620,12 +583,14 @@ public EditorScriptCompilationState state private sealed class PendingPlayFactory(PendingPlaySubsystem subsystem) : IRuntimeSubsystemFactory { public RuntimeSubsystemDescriptor descriptor { get; } = new(new RuntimeSubsystemId("test.pending-play")); + public IRuntimeSubsystem Create(RuntimeSubsystemContext context) => subsystem; } private sealed class PendingPlaySubsystem : RuntimeSubsystem { internal TaskCompletionSource work { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + internal bool stopped; protected override void OnStart() => lifetime.Track(work.Task); protected override void OnStop() => stopped = true; @@ -634,18 +599,10 @@ private sealed class PendingPlaySubsystem : RuntimeSubsystem private sealed class FakeCompilationTicket : IScriptCompilationTicket { public long requestId => 1; - public ScriptCompilationTicketState state { get; private set; } - public string status => "Test compilation ticket."; - public ScriptCompilationResult? result => null; - - public bool isCompleted - => state is ScriptCompilationTicketState.Succeeded - or ScriptCompilationTicketState.Failed - or ScriptCompilationTicketState.Canceled - or ScriptCompilationTicketState.Superseded; + public bool isCompleted => state is ScriptCompilationTicketState.Succeeded or ScriptCompilationTicketState.Failed or ScriptCompilationTicketState.Canceled or ScriptCompilationTicketState.Superseded; internal void SetState(EditorScriptCompilationState value) { @@ -673,7 +630,6 @@ public IDisposable BeginPlayMode(RuntimeSession runtimeSession) private sealed class Session(FakeScenePlayMode owner) : IDisposable { private bool m_restored; - public void Dispose() { if (m_restored) @@ -686,6 +642,7 @@ public void Dispose() private sealed class FakeSelectionCoordinator : IEditorSelectionCoordinator { + public IDisposable RegisterIdentityDomain(Inno.Core.Identity.IdentityAllocator allocator) => new Inno.Core.Execution.LifetimeScope(); public object? selectedTarget { get; private set; } public void SetSelection(object? target) => selectedTarget = target; @@ -694,7 +651,6 @@ private sealed class FakeSelectionCoordinator : IEditorSelectionCoordinator private sealed class FakeHistoryIsolation : IEditorHistoryIsolation { internal int beginCount { get; private set; } - internal int disposeCount { get; private set; } public IDisposable BeginHistoryIsolation() @@ -706,7 +662,6 @@ public IDisposable BeginHistoryIsolation() private sealed class Lease(FakeHistoryIsolation owner) : IDisposable { private bool m_disposed; - public void Dispose() { if (m_disposed) @@ -719,17 +674,12 @@ public void Dispose() private RuntimeSessionOptions CreateSessionOptions(RuntimeSessionKind kind) { - string applicationId = kind == RuntimeSessionKind.Edit - ? "inno.tests.edit" - : "inno.tests.play"; + string applicationId = kind == RuntimeSessionKind.Edit ? "inno.tests.edit" : "inno.tests.play"; return new RuntimeSessionOptions { kind = kind, applicationId = applicationId, - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine( - m_projectRoot, - "PersistentData", - applicationId), "Logs")), + createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(m_projectRoot, "PersistentData", applicationId), "Logs")), jobExecutionMode = RuntimeJobExecutionMode.SingleThread }; } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/EditorPopupLayoutTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/EditorPopupLayoutTests.cs index 49ad0887b..26d174611 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/EditorPopupLayoutTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/EditorPopupLayoutTests.cs @@ -1,16 +1,15 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; using System; using System.Numerics; - using Inno.Native.ImGui; using EditorWidget = Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget; using NativeImGui = Inno.Native.ImGui.ImGui; using Xunit; namespace Inno.Editor.PlayMode.Tests; - public sealed unsafe class EditorPopupLayoutTests { [Fact] @@ -19,8 +18,10 @@ public void ContextMenuSearchUsesCompletePopupContentWidth() ImGuiNativeContext* context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ConfigureContext(); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.SetupStyle(); string query = string.Empty; for (int frame = 0; frame < 3; frame++) { @@ -30,19 +31,19 @@ public void ContextMenuSearchUsesCompletePopupContentWidth() _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText0.pointer, null, 0); _ = NativeImGui.Button("Target"); NativeImGui.OpenPopup("commands"); - - Assert.True(EditorWidget.BeginContextMenu("commands")); - _ = EditorWidget.SearchInput("commands", "Search commands...", ref query); + Assert.True(EditorUI.current.widgets.BeginContextMenu("commands")); + _ = EditorUI.current.widgets.SearchInput("commands", "Search commands...", ref query); if (frame == 2) { float inputWidth = NativeImGui.GetItemRectSize().X; float contentWidth = NativeImGui.GetContentRegionAvail().X; - Assert.True(inputWidth >= EditorWidget.style.searchPopupWidth - 1f); + Assert.True(inputWidth >= EditorUI.current.widgets.style.searchPopupWidth - 1f); Assert.InRange(MathF.Abs(inputWidth - contentWidth), 0f, 2f); Assert.InRange(NativeImGui.GetScrollMaxX(), 0f, 1f); } + NativeImGui.Selectable("Rename"); - EditorWidget.EndContextMenu(); + EditorUI.current.widgets.EndContextMenu(); NativeImGui.End(); NativeImGui.Render(); } @@ -59,9 +60,10 @@ public void TwoItemSelectorOpensBelowControlWithoutOverflow() ImGuiNativeContext* context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ConfigureContext(); - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); for (int frame = 0; frame < 3; frame++) { NativeImGui.NewFrame(); @@ -72,8 +74,7 @@ public void TwoItemSelectorOpensBelowControlWithoutOverflow() NativeImGui.SetNextItemWidth(140f); float triggerBottom = NativeImGui.GetCursorScreenPos().Y + NativeImGui.GetFrameHeight(); NativeImGui.OpenPopup("##menu_selector_popup_tag"); - - Assert.True(EditorWidget.BeginBoundedCombo("tag", "Untagged")); + Assert.True(EditorUI.current.widgets.BeginBoundedCombo("tag", "Untagged")); Vector2 popupPosition = NativeImGui.GetWindowPos(); Vector2 popupSize = NativeImGui.GetWindowSize(); _ = NativeImGui.Selectable("Untagged"); @@ -85,7 +86,8 @@ public void TwoItemSelectorOpensBelowControlWithoutOverflow() Assert.True(popupPosition.Y + popupSize.Y <= 270f + 1f); Assert.InRange(NativeImGui.GetScrollMaxY(), 0f, 1f); } - EditorWidget.EndBoundedCombo(); + + EditorUI.current.widgets.EndBoundedCombo(); NativeImGui.End(); NativeImGui.Render(); } @@ -102,8 +104,10 @@ public void LongSelectorNearWindowBottomScrollsWithoutOpeningUpward() ImGuiNativeContext* context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ConfigureContext(); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.SetupStyle(); for (int frame = 0; frame < 3; frame++) { NativeImGui.NewFrame(); @@ -115,8 +119,7 @@ public void LongSelectorNearWindowBottomScrollsWithoutOpeningUpward() float triggerBottom = NativeImGui.GetCursorScreenPos().Y + NativeImGui.GetFrameHeight(); NativeImGui.SetNextItemWidth(140f); NativeImGui.OpenPopup("##menu_selector_popup_long"); - - Assert.True(EditorWidget.BeginBoundedCombo("long", "Choose")); + Assert.True(EditorUI.current.widgets.BeginBoundedCombo("long", "Choose")); Vector2 popupPosition = NativeImGui.GetWindowPos(); Vector2 popupSize = NativeImGui.GetWindowSize(); for (int index = 0; index < 40; index++) @@ -127,7 +130,8 @@ public void LongSelectorNearWindowBottomScrollsWithoutOpeningUpward() Assert.True(popupPosition.Y + popupSize.Y <= 270f + 1f); Assert.True(NativeImGui.GetScrollMaxY() > 0f); } - EditorWidget.EndBoundedCombo(); + + EditorUI.current.widgets.EndBoundedCombo(); NativeImGui.End(); NativeImGui.Render(); } @@ -144,8 +148,10 @@ public void SelectorInsideLayoutChildUsesContainingPanelBounds() ImGuiNativeContext* context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ConfigureContext(); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.SetupStyle(); for (int frame = 0; frame < 3; frame++) { NativeImGui.NewFrame(); @@ -156,8 +162,7 @@ public void SelectorInsideLayoutChildUsesContainingPanelBounds() _ = NativeImGui.BeginChild("Fields", new Vector2(200f, 55f), 0, 0); NativeImGui.SetNextItemWidth(140f); NativeImGui.OpenPopup("##menu_selector_popup_nested"); - - Assert.True(EditorWidget.BeginBoundedCombo("nested", "Choose")); + Assert.True(EditorUI.current.widgets.BeginBoundedCombo("nested", "Choose")); Vector2 popupPosition = NativeImGui.GetWindowPos(); Vector2 popupSize = NativeImGui.GetWindowSize(); for (int index = 0; index < 40; index++) @@ -167,7 +172,8 @@ public void SelectorInsideLayoutChildUsesContainingPanelBounds() Assert.True(popupSize.Y > 55f); Assert.True(popupPosition.Y + popupSize.Y <= 270f + 1f); } - EditorWidget.EndBoundedCombo(); + + EditorUI.current.widgets.EndBoundedCombo(); NativeImGui.EndChild(); NativeImGui.End(); NativeImGui.Render(); diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj index e6108ef6e..4edaa28b8 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj +++ b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj @@ -15,6 +15,8 @@ true true Editor + editor + $(InnoNativeTarget) diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/PlayModeLogRetentionTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/PlayModeLogRetentionTests.cs index a4fde5537..5cb91fabc 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/PlayModeLogRetentionTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/PlayModeLogRetentionTests.cs @@ -196,7 +196,16 @@ private sealed class FakePlayMode : IEditorPlayMode public LogSessionId activeSessionId { get; private set; } - public event Action? stateChanged; + private readonly Inno.Core.Events.EventDispatcher m_stateDispatcher = new(); + private readonly Inno.Core.Events.EventHub m_stateEvents; + + internal FakePlayMode() => m_stateEvents = m_stateDispatcher.CreateHub(); + public IDisposable ListenStateChanges(Action listener) + => m_stateEvents.Listen(notification => listener(notification.state)); + private sealed class StateNotification(EditorPlayModeState state) : Inno.Core.Events.Event + { + internal EditorPlayModeState state { get; } = state; + } public bool EnterPlayMode() => false; @@ -218,7 +227,7 @@ internal void End() internal void SetState(EditorPlayModeState value) { state = value; - stateChanged?.Invoke(value); + m_stateDispatcher.Emit(new StateNotification(value)); } } } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs index 750aa00d4..28ae60a8e 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs @@ -1,3 +1,11 @@ +using ImGuiWindowFlags = Inno.Editor.ImGui.ImGuiWindowFlags; +using ImGuiTableRowFlags = Inno.Editor.ImGui.ImGuiTableRowFlags; +using ImGuiStyleVar = Inno.Editor.ImGui.ImGuiStyleVar; +using ImGuiMouseButton = Inno.Editor.ImGui.ImGuiMouseButton; +using ImGuiCond = Inno.Editor.ImGui.ImGuiCond; +using ImGuiCol = Inno.Editor.ImGui.ImGuiCol; +using ImGuiChildFlags = Inno.Editor.ImGui.ImGuiChildFlags; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; @@ -5,7 +13,6 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; - using Inno.Editor.ImGui; using Inno.Native.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; @@ -14,18 +21,15 @@ using TreeNodeOptions = Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions; using TreeNodeResult = Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult; using NativeImGui = Inno.Native.ImGui.ImGui; - using Xunit; namespace Inno.Editor.Scripting.Tests; - public sealed unsafe class EditorStyleMetricsTests { [Fact] public void ZoomScalesDimensionsAndFontWhilePreservingRatios() { var metrics = new EditorStyleMetrics(); - Assert.True(metrics.SetZoom(1.5f)); Assert.Equal(1.5f, metrics.zoom, 3); Assert.Equal(12f, metrics.windowPadding.X, 3); @@ -41,7 +45,6 @@ public void ZoomScalesDimensionsAndFontWhilePreservingRatios() public void ZoomCommandsClampAndResetToTheSupportedRange() { var metrics = new EditorStyleMetrics(); - Assert.True(metrics.SetZoom(100f)); Assert.Equal(EditorStyleMetrics.C_MAX_ZOOM, metrics.zoom); Assert.False(metrics.ZoomIn()); @@ -59,28 +62,25 @@ public void WrappedTextRendersThroughTheSafeLiteralTextPath() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(260f, 180f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated("Wrapped Text Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText0.pointer, null, 0); float lineHeight = NativeImGui.GetTextLineHeight(); - EditorWidget.WrappedText( - "Literal %s text remains safe and wraps. This deliberately long compilation status " + - "must stay inside the modal content width instead of extending beyond its right edge."); + EditorUI.current.widgets.WrappedText("Literal %s text remains safe and wraps. This deliberately long compilation status " + "must stay inside the modal content width instead of extending beyond its right edge."); Assert.True(NativeImGui.GetItemRectSize().Y > lineHeight); Inno.Native.ImGui.ImDrawList* windowDrawList = NativeImGui.Raw.GetWindowDrawList(); Inno.Native.ImGui.ImDrawList* foregroundDrawList = NativeImGui.Raw.GetForegroundDrawList(NativeImGui.Raw.GetMainViewport()); int windowVertexCount = windowDrawList->VtxBuffer.Size; int foregroundVertexCount = foregroundDrawList->VtxBuffer.Size; - EditorWidget.DropTargetHighlight( - new Vector2(20f, 20f), - new Vector2(120f, 80f)); + EditorUI.current.widgets.DropTargetHighlight(new Vector2(20f, 20f), new Vector2(120f, 80f)); Assert.Equal(windowVertexCount, windowDrawList->VtxBuffer.Size); Assert.True(foregroundDrawList->VtxBuffer.Size > foregroundVertexCount); NativeImGui.End(); @@ -98,12 +98,13 @@ public void CenteredWrappedTextStaysInsidePaddingAndUsesMultipleLines() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(420f, 260f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText1 = Utf8Codec.EncodeNullTerminated("Centered Wrapped Text Test", allowsNull: false); @@ -114,13 +115,11 @@ public void CenteredWrappedTextStaysInsidePaddingAndUsesMultipleLines() ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); int vertexStart = drawList->VtxBuffer.Size; uint textColor = NativeImGui.GetColorU32((int)(ImGuiCol.Text)); - - EditorWidget.CenteredWrappedText( - "A deliberately long viewport diagnostic wraps into several centered lines " + - "without touching either horizontal edge of the presentation area.", + EditorUI.current.widgets.CenteredWrappedText( + "A deliberately long viewport diagnostic wraps into several centered lines " + "without touching either horizontal edge of the presentation area.", area, - padding); - + padding + ); Assert.Equal(area, NativeImGui.GetItemRectSize()); Vector2 minimum = new(float.MaxValue); Vector2 maximum = new(float.MinValue); @@ -129,7 +128,6 @@ public void CenteredWrappedTextStaysInsidePaddingAndUsesMultipleLines() ImDrawVert vertex = drawList->VtxBuffer.Data[i]; if (vertex.Col != textColor) continue; - minimum = Vector2.Min(minimum, Unsafe.BitCast(vertex.Pos)); maximum = Vector2.Max(maximum, Unsafe.BitCast(vertex.Pos)); } @@ -155,6 +153,8 @@ public void InlineRenameIsCenteredAndSelectsTheCompleteValueWhenFocused() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; @@ -162,18 +162,12 @@ public void InlineRenameIsCenteredAndSelectsTheCompleteValueWhenFocused() io->Fonts->RendererHasTextures = 1; string value = "Scene"; bool requestFocus = true; - NativeImGui.NewFrame(); using NativeStringLease nativeTestText2 = Utf8Codec.EncodeNullTerminated("Inline Rename Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText2.pointer, null, 0); Vector2 rowMin = NativeImGui.GetCursorScreenPos(); float rowHeight = NativeImGui.GetFrameHeight() + 8f; - _ = EditorWidget.InlineRename( - "scene", - ref value, - ref requestFocus, - rowHeight, - width: 180f); + _ = EditorUI.current.widgets.InlineRename("scene", ref value, ref requestFocus, rowHeight, width: 180f); Vector2 fieldMin = NativeImGui.GetItemRectMin(); Vector2 fieldMax = NativeImGui.GetItemRectMax(); Assert.True(fieldMin.Y >= rowMin.Y); @@ -184,36 +178,27 @@ public void InlineRenameIsCenteredAndSelectsTheCompleteValueWhenFocused() Assert.NotEqual(0u, NativeImGui.Raw.GetCurrentWindow()->ID); NativeImGui.End(); NativeImGui.Render(); - NativeImGui.NewFrame(); using NativeStringLease nativeTestText3 = Utf8Codec.EncodeNullTerminated("Inline Rename Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText3.pointer, null, 0); - _ = EditorWidget.InlineRename( - "scene", - ref value, - ref requestFocus, - rowHeight, - width: 180f); + _ = EditorUI.current.widgets.InlineRename("scene", ref value, ref requestFocus, rowHeight, width: 180f); Vector2 focusedFieldMin = NativeImGui.GetItemRectMin(); Vector2 focusedFieldMax = NativeImGui.GetItemRectMax(); uint inputId = NativeImGui.GetItemID(); ImGuiInputTextState* inputState = NativeImGui.Raw.GetInputTextState(inputId); - Assert.False(requestFocus); Assert.False((inputState == null)); Assert.Equal(0, NativeImGui.Raw.GetSelectionStart(inputState)); Assert.Equal(value.Length, NativeImGui.Raw.GetSelectionEnd(inputState)); uint navCursorColor = NativeImGui.GetColorU32((int)(ImGuiCol.NavCursor)); - AssertDrawListDoesNotContainColor( - NativeImGui.Raw.GetWindowDrawList(), - navCursorColor, - "The inline rename focus outline was submitted to the window draw list."); + AssertDrawListDoesNotContainColor(NativeImGui.Raw.GetWindowDrawList(), navCursorColor, "The inline rename focus outline was submitted to the window draw list."); AssertColorBoundsStayCloseToItem( NativeImGui.Raw.GetForegroundDrawList(NativeImGui.Raw.GetMainViewport()), navCursorColor, focusedFieldMin, focusedFieldMax, - 1f + EditorWidget.style.interactionOverlayThickness); + 1f + EditorUI.current.widgets.style.interactionOverlayThickness + ); NativeImGui.End(); NativeImGui.Render(); } @@ -229,6 +214,8 @@ public void InlineRenameReleasesFocusBeforeAContextMenuCanOpen() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; @@ -238,7 +225,6 @@ public void InlineRenameReleasesFocusBeforeAContextMenuCanOpen() bool requestFocus = true; InlineRenameResult result = InlineRenameResult.None; Vector2 center = default; - void Frame() { NativeImGui.NewFrame(); @@ -246,12 +232,7 @@ void Frame() NativeImGui.SetNextWindowSize(new Vector2(320f, 180f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText4 = Utf8Codec.EncodeNullTerminated("Inline Rename Context Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText4.pointer, null, 0); - result = EditorWidget.InlineRename( - "created_asset", - ref value, - ref requestFocus, - NativeImGui.GetFrameHeight(), - width: 220f); + result = EditorUI.current.widgets.InlineRename("created_asset", ref value, ref requestFocus, NativeImGui.GetFrameHeight(), width: 220f); center = (NativeImGui.GetItemRectMin() + NativeImGui.GetItemRectMax()) * 0.5f; NativeImGui.End(); NativeImGui.Render(); @@ -261,13 +242,11 @@ void Frame() Frame(); Assert.False(requestFocus); Assert.NotEqual(0u, NativeImGui.Raw.GetActiveID()); - NativeImGui.Raw.AddMousePosEvent(io, center.X, center.Y); NativeImGui.Raw.AddMouseButtonEvent(io, (int)ImGuiMouseButton.Right, (true ? (byte)1 : (byte)0)); Frame(); NativeImGui.Raw.AddMouseButtonEvent(io, (int)ImGuiMouseButton.Right, (false ? (byte)1 : (byte)0)); Frame(); - Assert.Equal(InlineRenameResult.FocusLost, result); Assert.Equal(0u, NativeImGui.Raw.GetActiveID()); } @@ -283,6 +262,8 @@ public void InlineRenameCentersWithinTheNativeTableRowBounds() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; @@ -290,35 +271,23 @@ public void InlineRenameCentersWithinTheNativeTableRowBounds() io->Fonts->RendererHasTextures = 1; string value = "Scene"; bool requestFocus = false; - NativeImGui.NewFrame(); using NativeStringLease nativeTestText5 = Utf8Codec.EncodeNullTerminated("Inline Rename Table Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText5.pointer, null, 0); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.CellPadding), new Vector2(6f, 3f)); Assert.True(NativeImGui.BeginTable("##rename_table", 1, 0, default(Vector2), 0f)); - float rowHeight = NativeImGui.GetTextLineHeight() + - NativeImGui.Raw.GetStyle()->CellPadding.Y * 2f; + float rowHeight = NativeImGui.GetTextLineHeight() + NativeImGui.Raw.GetStyle()->CellPadding.Y * 2f; NativeImGui.TableNextRow((int)(ImGuiTableRowFlags.None), rowHeight); _ = NativeImGui.TableSetColumnIndex(0); ImGuiTable* table = NativeImGui.Raw.GetCurrentTable(); Vector2 cursor = NativeImGui.GetCursorScreenPos(); NativeImGui.SetCursorScreenPos(new Vector2(cursor.X, table->RowPosY1)); - - _ = EditorWidget.InlineRename( - "table_scene", - ref value, - ref requestFocus, - table->RowPosY2 - table->RowPosY1, - width: 180f); + _ = EditorUI.current.widgets.InlineRename("table_scene", ref value, ref requestFocus, table->RowPosY2 - table->RowPosY1, width: 180f); Vector2 fieldMin = NativeImGui.GetItemRectMin(); Vector2 fieldMax = NativeImGui.GetItemRectMax(); - Assert.True(fieldMin.Y >= table->RowPosY1); Assert.True(fieldMax.Y <= table->RowPosY2); - Assert.Equal( - (table->RowPosY1 + table->RowPosY2) * 0.5f, - (fieldMin.Y + fieldMax.Y) * 0.5f, - 3); + Assert.Equal((table->RowPosY1 + table->RowPosY2) * 0.5f, (fieldMin.Y + fieldMax.Y) * 0.5f, 3); NativeImGui.EndTable(); NativeImGui.PopStyleVar(); NativeImGui.End(); @@ -336,23 +305,30 @@ public void TreeGuideDepthRemainsBalancedAcrossSupportedZoomLevels() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; + foreach (float zoom in new[] + { + 0.75f, + 1f, + 1.25f, + 1.5f + } - foreach (float zoom in new[] { 0.75f, 1f, 1.25f, 1.5f }) + ) { - _ = EditorWidget.style.SetZoom(zoom); - EditorWidget.SetupStyle(); + _ = EditorUI.current.widgets.style.SetZoom(zoom); + EditorUI.current.widgets.SetupStyle(); DrawTreeFrame(); } } finally { - _ = EditorWidget.style.ResetZoom(); - EditorWidget.SetupStyle(); NativeImGui.Raw.DestroyContext(context); } } @@ -363,12 +339,13 @@ public void MenuSelectorPopupIsBoundedAndScrollsLongContent() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - for (int frame = 0; frame < 2; frame++) { NativeImGui.NewFrame(); @@ -378,7 +355,7 @@ public void MenuSelectorPopupIsBoundedAndScrollsLongContent() _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText6.pointer, null, 0); if (frame == 0) NativeImGui.OpenPopup("##menu_selector_popup_test"); - Assert.True(EditorWidget.BeginMenuSelector("test", "Untagged", 180f, 240f)); + Assert.True(EditorUI.current.widgets.BeginMenuSelector("test", "Untagged", 180f, 240f)); ImGuiWindowFlags flags = (ImGuiWindowFlags)NativeImGui.Raw.GetCurrentWindow()->Flags; Assert.True(flags.HasFlag(ImGuiWindowFlags.AlwaysAutoResize)); Assert.False(flags.HasFlag(ImGuiWindowFlags.NoScrollbar)); @@ -387,17 +364,10 @@ public void MenuSelectorPopupIsBoundedAndScrollsLongContent() Assert.True(NativeImGui.GetWindowSize().X >= 240f); string newTag = string.Empty; NativeImGui.SetNextItemWidth(160f); - _ = EditorImGui.InputTextWithHint( - "##new_tag", - "Add tag...", - ref newTag, - 128); + _ = EditorImGui.InputTextWithHint("##new_tag", "Add tag...", ref newTag, 128); float inputCenterY = (NativeImGui.GetItemRectMin().Y + NativeImGui.GetItemRectMax().Y) * 0.5f; NativeImGui.SameLine(0f, -1f); - _ = EditorWidget.ClickableText( - "add_tag", - "+", - new Vector2(EditorWidget.GetCompactIconSize().X, NativeImGui.GetFrameHeight())); + _ = EditorUI.current.widgets.ClickableText("add_tag", "+", new Vector2(EditorUI.current.widgets.GetCompactIconSize().X, NativeImGui.GetFrameHeight())); float actionCenterY = (NativeImGui.GetItemRectMin().Y + NativeImGui.GetItemRectMax().Y) * 0.5f; Assert.Equal(inputCenterY, actionCenterY, 3); for (int index = 0; index < 64; index++) @@ -407,7 +377,8 @@ public void MenuSelectorPopupIsBoundedAndScrollsLongContent() Assert.True(NativeImGui.GetWindowSize().Y <= io->DisplaySize.Y + 1f); Assert.True(NativeImGui.GetScrollMaxY() > 0f); } - EditorWidget.EndMenuSelector(); + + EditorUI.current.widgets.EndMenuSelector(); NativeImGui.End(); NativeImGui.Render(); } @@ -424,12 +395,13 @@ public void SmallMenuSelectorDoesNotReserveAScrollbar() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - for (int frame = 0; frame < 3; frame++) { NativeImGui.NewFrame(); @@ -439,7 +411,7 @@ public void SmallMenuSelectorDoesNotReserveAScrollbar() _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText7.pointer, null, 0); if (frame == 0) NativeImGui.OpenPopup("##menu_selector_popup_small"); - Assert.True(EditorWidget.BeginMenuSelector("small", "Current", 180f, 240f)); + Assert.True(EditorUI.current.widgets.BeginMenuSelector("small", "Current", 180f, 240f)); for (int index = 0; index < 3; index++) NativeImGui.Selectable($"Entry {index}"); if (frame == 2) @@ -447,7 +419,8 @@ public void SmallMenuSelectorDoesNotReserveAScrollbar() Assert.False((NativeImGui.Raw.GetCurrentWindow()->ScrollbarY != 0)); Assert.Equal(0f, NativeImGui.GetScrollMaxY(), 3); } - EditorWidget.EndMenuSelector(); + + EditorUI.current.widgets.EndMenuSelector(); NativeImGui.End(); NativeImGui.Render(); } @@ -464,12 +437,13 @@ public void SmallContextMenuDoesNotReserveScrollbars() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - for (int frame = 0; frame < 3; frame++) { NativeImGui.NewFrame(); @@ -478,7 +452,7 @@ public void SmallContextMenuDoesNotReserveScrollbars() NativeImGui.Selectable("Target"); if (frame == 0) NativeImGui.OpenPopup("context"); - Assert.True(EditorWidget.BeginContextMenu("context")); + Assert.True(EditorUI.current.widgets.BeginContextMenu("context")); NativeImGui.MenuItem("Rename", string.Empty, false, true); NativeImGui.MenuItem("Delete", string.Empty, false, true); if (frame == 2) @@ -486,7 +460,8 @@ public void SmallContextMenuDoesNotReserveScrollbars() Assert.False((NativeImGui.Raw.GetCurrentWindow()->ScrollbarX != 0)); Assert.False((NativeImGui.Raw.GetCurrentWindow()->ScrollbarY != 0)); } - EditorWidget.EndContextMenu(); + + EditorUI.current.widgets.EndContextMenu(); NativeImGui.End(); NativeImGui.Render(); } @@ -503,12 +478,13 @@ public void BoundedComboPopupUsesTheSharedScrollingContract() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - var popupWidths = new List(); int openFrames = 0; for (int frame = 0; frame < 5; frame++) @@ -523,14 +499,15 @@ public void BoundedComboPopupUsesTheSharedScrollingContract() { NativeImGui.OpenPopup("##menu_selector_popup_##asset"); } + NativeImGui.SetNextItemWidth(180f); - bool open = EditorWidget.BeginBoundedCombo("##asset", "project:Material"); + bool open = EditorUI.current.widgets.BeginBoundedCombo("##asset", "project:Material"); if (open) { openFrames++; Assert.Equal(parentViewportId, NativeImGui.Raw.GetWindowViewport()->ID); string search = string.Empty; - _ = EditorWidget.SearchInput("assets", "Search assets...", ref search); + _ = EditorUI.current.widgets.SearchInput("assets", "Search assets...", ref search); for (int index = 0; index < 64; index++) NativeImGui.Selectable($"project:Material{index:D2}"); popupWidths.Add(NativeImGui.GetWindowSize().X); @@ -547,11 +524,14 @@ public void BoundedComboPopupUsesTheSharedScrollingContract() Assert.True(popupMaximum.X <= popupViewport->WorkPos.X + popupViewport->WorkSize.X + 1f); Assert.True(popupMaximum.Y <= popupViewport->WorkPos.Y + popupViewport->WorkSize.Y + 1f); } - EditorWidget.EndBoundedCombo(); + + EditorUI.current.widgets.EndBoundedCombo(); } + NativeImGui.End(); NativeImGui.Render(); } + Assert.True(openFrames >= 2); Assert.All(popupWidths, width => Assert.InRange(width, 179f, 181f)); } @@ -567,12 +547,13 @@ public void TreeRegionScrollsHorizontallyOnlyForRealContentOverflow() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - DrawTreeOverflowFrame(assertRanges: false); DrawTreeOverflowFrame(assertRanges: true); DrawHorizontallyScrolledTreeFrame(0f, assertGeometry: false); @@ -591,12 +572,13 @@ public void TreeRegionReleasesViewportWidthAfterItShrinks() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - DrawResizableTreeFrame(420f, 0f, assertNoOverflow: false); DrawResizableTreeFrame(220f, 48f, assertNoOverflow: false); DrawResizableTreeFrame(220f, 48f, assertNoOverflow: true); @@ -613,13 +595,14 @@ public void TreeGuideSegmentsRemainVisibleAcrossCompactRows() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); DrawTreeFrame(); DrawTreeFrame(assertContinuousGuides: true); } @@ -635,30 +618,24 @@ public void ChildGuideRetainsAVisualGapBelowTheParentWithoutChangingCompactRowHe var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(480f, 320f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText10 = Utf8Codec.EncodeNullTerminated("Gapped Parent Guide Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText10.pointer, null, 0); float expectedHeight = NativeImGui.GetTextLineHeight(); - EditorWidget.SetNextTreeNodeOpen(true); - TreeNodeResult parent = EditorWidget.TreeNode( - "connected_parent", - static _ => NativeImGui.TextUnformatted("Parent"), - new TreeNodeOptions()); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult parent = EditorUI.current.widgets.TreeNode("connected_parent", static _ => NativeImGui.TextUnformatted("Parent"), new TreeNodeOptions()); Assert.True(parent.isOpen); - TreeNodeResult child = EditorWidget.TreeNode( - "connected_child", - static _ => NativeImGui.TextUnformatted("Child"), - new TreeNodeOptions { isLeaf = true }); + TreeNodeResult child = EditorUI.current.widgets.TreeNode("connected_child", static _ => NativeImGui.TextUnformatted("Child"), new TreeNodeOptions { isLeaf = true }); NativeImGui.TreePop(); - Assert.Equal(expectedHeight, parent.max.Y - parent.min.Y, 3); Assert.Equal(expectedHeight, child.max.Y - child.min.Y, 3); AssertCurrentTreeGuideDoesNotTouchY((parent.min.Y + parent.max.Y) * 0.5f); @@ -677,32 +654,28 @@ public void TreeGuidesAreDrawnFromTheCurrentFrameWhileDragging() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(480f, 320f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText11 = Utf8Codec.EncodeNullTerminated("Dragging Tree Guide Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText11.pointer, null, 0); ImGuiNativeContext* currentContext = NativeImGui.Raw.GetCurrentContext(); currentContext->DragDropActive = 1; - EditorWidget.SetNextTreeNodeOpen(true); - TreeNodeResult root = EditorWidget.TreeNode( - "drag_root", - static _ => NativeImGui.TextUnformatted("Assets"), - new TreeNodeOptions()); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult root = EditorUI.current.widgets.TreeNode("drag_root", static _ => NativeImGui.TextUnformatted("Assets"), new TreeNodeOptions()); if (root.isOpen) { - _ = EditorWidget.TreeNode( - "drag_child", - static _ => NativeImGui.TextUnformatted("Scene"), - new TreeNodeOptions { isLeaf = true }); + _ = EditorUI.current.widgets.TreeNode("drag_child", static _ => NativeImGui.TextUnformatted("Scene"), new TreeNodeOptions { isLeaf = true }); NativeImGui.TreePop(); } + AssertCurrentDrawListContainsTreeGuideColor(); currentContext->DragDropActive = 0; NativeImGui.End(); @@ -720,19 +693,16 @@ public void TreeRowBackgroundsUseCurrentGeometryWhileTheWindowChanges() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - - DrawCurrentTreeBackgroundFrame( - new Vector2(20f, 20f), - new Vector2(480f, 320f)); - DrawCurrentTreeBackgroundFrame( - new Vector2(80f, 60f), - new Vector2(300f, 220f)); + EditorUI.current.widgets.SetupStyle(); + DrawCurrentTreeBackgroundFrame(new Vector2(20f, 20f), new Vector2(480f, 320f)); + DrawCurrentTreeBackgroundFrame(new Vector2(80f, 60f), new Vector2(300f, 220f)); } finally { @@ -746,22 +716,20 @@ public void TreeRowsRetainTheirCompactContentHeight() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(320f, 240f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText12 = Utf8Codec.EncodeNullTerminated("Compact Tree Row Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText12.pointer, null, 0); float expectedHeight = NativeImGui.GetTextLineHeight(); - TreeNodeResult row = EditorWidget.TreeNode( - "compact_row", - static _ => NativeImGui.TextUnformatted("GameObject"), - new TreeNodeOptions { isLeaf = true }); + TreeNodeResult row = EditorUI.current.widgets.TreeNode("compact_row", static _ => NativeImGui.TextUnformatted("GameObject"), new TreeNodeOptions { isLeaf = true }); Assert.Equal(expectedHeight, row.max.Y - row.min.Y, 3); NativeImGui.End(); NativeImGui.Render(); @@ -778,17 +746,16 @@ public void DoubleClickingTreeContentTogglesNonLeafExpansion() var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); TreeNodeResult initial = DrawDoubleClickTreeFrame(); - Vector2 contentCenter = new( - initial.contentMin.X + 20f, - (initial.min.Y + initial.max.Y) * 0.5f); + Vector2 contentCenter = new(initial.contentMin.X + 20f, (initial.min.Y + initial.max.Y) * 0.5f); NativeImGui.Raw.AddMousePosEvent(io, contentCenter.X, contentCenter.Y); NativeImGui.Raw.AddMouseButtonEvent(io, 0, (true ? (byte)1 : (byte)0)); _ = DrawDoubleClickTreeFrame(); @@ -798,7 +765,6 @@ public void DoubleClickingTreeContentTogglesNonLeafExpansion() TreeNodeResult doubleClicked = DrawDoubleClickTreeFrame(); NativeImGui.Raw.AddMouseButtonEvent(io, 0, (false ? (byte)1 : (byte)0)); TreeNodeResult expanded = DrawDoubleClickTreeFrame(); - Assert.True(doubleClicked.isDoubleClicked); Assert.True(expanded.isOpen); } @@ -814,13 +780,14 @@ public void InspectorContentPreservesStandardPaddingWithoutArtificialHorizontalO var context = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.SetupStyle(); - + EditorUI.current.widgets.SetupStyle(); DrawInspectorCardFrame(assertHorizontalRange: false); DrawInspectorCardFrame(assertHorizontalRange: true); } @@ -836,33 +803,22 @@ private static void DrawTreeFrame(bool assertContinuousGuides = false) NativeImGui.SetNextWindowSize(new Vector2(480f, 320f), (int)(Inno.Native.ImGui.ImGuiCond.Always)); using NativeStringLease nativeTestText13 = Utf8Codec.EncodeNullTerminated("Tree Zoom Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText13.pointer, null, 0); - EditorWidget.SetNextTreeNodeOpen(true); - TreeNodeResult root = EditorWidget.TreeNode( - "root", - static _ => NativeImGui.TextUnformatted("Assets"), - new TreeNodeOptions()); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult root = EditorUI.current.widgets.TreeNode("root", static _ => NativeImGui.TextUnformatted("Assets"), new TreeNodeOptions()); if (root.isOpen) { - EditorWidget.SetNextTreeNodeOpen(true); - TreeNodeResult folder = EditorWidget.TreeNode( - "folder", - static _ => NativeImGui.TextUnformatted("Scene"), - new TreeNodeOptions()); + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult folder = EditorUI.current.widgets.TreeNode("folder", static _ => NativeImGui.TextUnformatted("Scene"), new TreeNodeOptions()); if (folder.isOpen) { - _ = EditorWidget.TreeNode( - "nested-file", - static _ => NativeImGui.TextUnformatted("TestScene1.iscene"), - new TreeNodeOptions { isLeaf = true }); + _ = EditorUI.current.widgets.TreeNode("nested-file", static _ => NativeImGui.TextUnformatted("TestScene1.iscene"), new TreeNodeOptions { isLeaf = true }); NativeImGui.TreePop(); } - _ = EditorWidget.TreeNode( - "sibling-folder", - static _ => NativeImGui.TextUnformatted("Settings"), - new TreeNodeOptions { isLeaf = true }); + _ = EditorUI.current.widgets.TreeNode("sibling-folder", static _ => NativeImGui.TextUnformatted("Settings"), new TreeNodeOptions { isLeaf = true }); NativeImGui.TreePop(); } + if (assertContinuousGuides) AssertCurrentDrawListContainsTreeGuideColor(); NativeImGui.End(); @@ -876,18 +832,13 @@ private static TreeNodeResult DrawDoubleClickTreeFrame() NativeImGui.SetNextWindowSize(new Vector2(320f, 240f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText14 = Utf8Codec.EncodeNullTerminated("Double Click Tree Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText14.pointer, null, 0); - TreeNodeResult result = EditorWidget.TreeNode( - "double_click_parent", - static _ => NativeImGui.TextUnformatted("Parent"), - new TreeNodeOptions()); + TreeNodeResult result = EditorUI.current.widgets.TreeNode("double_click_parent", static _ => NativeImGui.TextUnformatted("Parent"), new TreeNodeOptions()); if (result.isOpen) { - _ = EditorWidget.TreeNode( - "double_click_child", - static _ => NativeImGui.TextUnformatted("Child"), - new TreeNodeOptions { isLeaf = true }); + _ = EditorUI.current.widgets.TreeNode("double_click_child", static _ => NativeImGui.TextUnformatted("Child"), new TreeNodeOptions { isLeaf = true }); NativeImGui.TreePop(); } + NativeImGui.End(); NativeImGui.Render(); return result; @@ -899,16 +850,9 @@ private static void DrawTreeOverflowFrame(bool assertRanges) NativeImGui.SetNextWindowSize(new Vector2(560f, 320f), (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText15 = Utf8Codec.EncodeNullTerminated("Tree Overflow Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText15.pointer, null, 0); - if (NativeImGui.BeginChild( - "##short_tree", - new Vector2(220f, 100f), - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.HorizontalScrollbar))) + if (NativeImGui.BeginChild("##short_tree", new Vector2(220f, 100f), (int)(ImGuiChildFlags.None), (int)(ImGuiWindowFlags.HorizontalScrollbar))) { - _ = EditorWidget.TreeNode( - "short", - static _ => NativeImGui.TextUnformatted("Object"), - new TreeNodeOptions { isLeaf = true }); + _ = EditorUI.current.widgets.TreeNode("short", static _ => NativeImGui.TextUnformatted("Object"), new TreeNodeOptions { isLeaf = true }); if (assertRanges) { Assert.Equal(0f, NativeImGui.GetScrollMaxX(), 3); @@ -916,19 +860,15 @@ private static void DrawTreeOverflowFrame(bool assertRanges) Assert.True((NativeImGui.Raw.GetCurrentWindow()->Flags & (int)ImGuiWindowFlags.HorizontalScrollbar) != 0); } } - NativeImGui.EndChild(); - if (NativeImGui.BeginChild( - "##long_tree", - new Vector2(220f, 100f), - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.HorizontalScrollbar))) + NativeImGui.EndChild(); + if (NativeImGui.BeginChild("##long_tree", new Vector2(220f, 100f), (int)(ImGuiChildFlags.None), (int)(ImGuiWindowFlags.HorizontalScrollbar))) { - _ = EditorWidget.TreeNode( + _ = EditorUI.current.widgets.TreeNode( "long", - static _ => NativeImGui.TextUnformatted( - "Object_With_A_Name_That_Is_Intentionally_Wider_Than_The_Tree_Viewport"), - new TreeNodeOptions { isLeaf = true }); + static _ => NativeImGui.TextUnformatted("Object_With_A_Name_That_Is_Intentionally_Wider_Than_The_Tree_Viewport"), + new TreeNodeOptions { isLeaf = true } + ); if (assertRanges) { Assert.True(NativeImGui.GetScrollMaxX() > 0f); @@ -936,6 +876,7 @@ private static void DrawTreeOverflowFrame(bool assertRanges) Assert.True((NativeImGui.Raw.GetCurrentWindow()->Flags & (int)ImGuiWindowFlags.HorizontalScrollbar) != 0); } } + NativeImGui.EndChild(); NativeImGui.End(); NativeImGui.Render(); @@ -950,38 +891,24 @@ bool assertGeometry using NativeStringLease nativeTestText16 = Utf8Codec.EncodeNullTerminated("Scrolled Tree Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText16.pointer, null, 0); NativeImGui.SetNextWindowScroll(new Vector2(requestedScroll, 0f)); - if (NativeImGui.BeginChild( - "##scrolled_tree", - new Vector2(220f, 120f), - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.HorizontalScrollbar))) + if (NativeImGui.BeginChild("##scrolled_tree", new Vector2(220f, 120f), (int)(ImGuiChildFlags.None), (int)(ImGuiWindowFlags.HorizontalScrollbar))) { Vector2 contentCursor = default; - TreeNodeResult row = EditorWidget.TreeNode( - "scrolled", - _ => - { - contentCursor = NativeImGui.GetCursorScreenPos(); - NativeImGui.TextUnformatted( - "Scene_With_A_Name_That_Is_Intentionally_Wider_Than_The_Tree_Viewport"); - }, - new TreeNodeOptions - { - isLeaf = true, - showBackground = true, - backgroundColor = EditorPalette.hierarchySceneRow - }); + TreeNodeResult row = EditorUI.current.widgets.TreeNode("scrolled", _ => + { + contentCursor = NativeImGui.GetCursorScreenPos(); + NativeImGui.TextUnformatted("Scene_With_A_Name_That_Is_Intentionally_Wider_Than_The_Tree_Viewport"); + }, new TreeNodeOptions { isLeaf = true, showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow }); if (assertGeometry) { ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); Assert.True(NativeImGui.GetScrollX() > 0f); Assert.True((window->ScrollbarX != 0)); Assert.Equal(row.contentMin.X, contentCursor.X, 3); - AssertCurrentTreeBackgroundSpansViewport( - window, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow)); + AssertCurrentTreeBackgroundSpansViewport(window, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow)); } } + NativeImGui.EndChild(); NativeImGui.End(); NativeImGui.Render(); @@ -997,32 +924,17 @@ bool assertNoOverflow using NativeStringLease nativeTestText17 = Utf8Codec.EncodeNullTerminated("Resizable Tree Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText17.pointer, null, 0); NativeImGui.SetNextWindowScroll(new Vector2(requestedScroll, 0f)); - if (NativeImGui.BeginChild( - "##resizable_tree", - new Vector2(width, 120f), - (int)(ImGuiChildFlags.None), - (int)(ImGuiWindowFlags.HorizontalScrollbar))) + if (NativeImGui.BeginChild("##resizable_tree", new Vector2(width, 120f), (int)(ImGuiChildFlags.None), (int)(ImGuiWindowFlags.HorizontalScrollbar))) { - _ = EditorWidget.TreeNode( - "resizable_row", - static _ => NativeImGui.TextUnformatted("GameObject"), - new TreeNodeOptions - { - isLeaf = true, - drawViewportOverlay = static () => - { - ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); - Vector2 overlaySize = new( - NativeImGui.GetFontSize(), - NativeImGui.GetTextLineHeight()); - NativeImGui.SameLine(0f, -1f); - Vector2 cursor = NativeImGui.GetCursorScreenPos(); - NativeImGui.SetCursorScreenPos(new Vector2( - MathF.Max(cursor.X, window->WorkRect.Max.X - overlaySize.X), - cursor.Y)); - NativeImGui.Dummy(overlaySize); - } - }); + _ = EditorUI.current.widgets.TreeNode("resizable_row", static _ => NativeImGui.TextUnformatted("GameObject"), new TreeNodeOptions { isLeaf = true, drawViewportOverlay = static () => + { + ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); + Vector2 overlaySize = new(NativeImGui.GetFontSize(), NativeImGui.GetTextLineHeight()); + NativeImGui.SameLine(0f, -1f); + Vector2 cursor = NativeImGui.GetCursorScreenPos(); + NativeImGui.SetCursorScreenPos(new Vector2(MathF.Max(cursor.X, window->WorkRect.Max.X - overlaySize.X), cursor.Y)); + NativeImGui.Dummy(overlaySize); + } }); if (assertNoOverflow) { Assert.Equal(0f, NativeImGui.GetScrollX(), 3); @@ -1030,6 +942,7 @@ bool assertNoOverflow Assert.False((NativeImGui.Raw.GetCurrentWindow()->ScrollbarX != 0)); } } + NativeImGui.EndChild(); NativeImGui.End(); NativeImGui.Render(); @@ -1038,9 +951,7 @@ bool assertNoOverflow private static void AssertCurrentDrawListContainsTreeGuideColor() { uint guideColor = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.treeGuide); - AssertCurrentDrawListContainsColor( - guideColor, - "The current drag frame did not submit any tree-guide vertices."); + AssertCurrentDrawListContainsColor(guideColor, "The current drag frame did not submit any tree-guide vertices."); } private static void AssertCurrentTreeGuideDoesNotTouchY(float excludedY) @@ -1052,7 +963,8 @@ private static void AssertCurrentTreeGuideDoesNotTouchY(float excludedY) ImDrawVert vertex = drawList->VtxBuffer.Data[i]; Assert.False( vertex.Col == guideColor && MathF.Abs(vertex.Pos.Y - excludedY) <= 1f, - "The child guide overlapped the parent disclosure row instead of retaining its visual gap."); + "The child guide overlapped the parent disclosure row instead of retaining its visual gap." + ); } } @@ -1065,35 +977,24 @@ Vector2 size NativeImGui.SetNextWindowSize(size, (int)(ImGuiCond.Always)); using NativeStringLease nativeTestText18 = Utf8Codec.EncodeNullTerminated("Current Tree Background Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText18.pointer, null, 0); - EditorWidget.SetNextTreeNodeOpen(true); - TreeNodeResult root = EditorWidget.TreeNode( + EditorUI.current.widgets.SetNextTreeNodeOpen(true); + TreeNodeResult root = EditorUI.current.widgets.TreeNode( "current_background_root", static _ => NativeImGui.TextUnformatted("TestScene"), - new TreeNodeOptions - { - showBackground = true, - backgroundColor = EditorPalette.hierarchySceneRow, - suppressHoverHighlight = true - }); + new TreeNodeOptions { showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow, suppressHoverHighlight = true } + ); if (root.isOpen) { - _ = EditorWidget.TreeNode( + _ = EditorUI.current.widgets.TreeNode( "current_background_child", static _ => NativeImGui.TextUnformatted("GameObject"), - new TreeNodeOptions - { - isLeaf = true, - selected = true - }); + new TreeNodeOptions { isLeaf = true, selected = true } + ); NativeImGui.TreePop(); } - AssertCurrentDrawListContainsColor( - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow), - "The current frame did not submit the Scene row background."); - AssertCurrentDrawListContainsColor( - NativeImGui.GetColorU32((int)(ImGuiCol.Header)), - "The current frame did not submit the selected row highlight."); + AssertCurrentDrawListContainsColor(NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow), "The current frame did not submit the Scene row background."); + AssertCurrentDrawListContainsColor(NativeImGui.GetColorU32((int)(ImGuiCol.Header)), "The current frame did not submit the selected row highlight."); NativeImGui.End(); NativeImGui.Render(); } @@ -1138,7 +1039,6 @@ float maximumExpansion ImDrawVert vertex = drawList->VtxBuffer.Data[i]; if (vertex.Col != color) continue; - colorMin = Vector2.Min(colorMin, Unsafe.BitCast(vertex.Pos)); colorMax = Vector2.Max(colorMax, Unsafe.BitCast(vertex.Pos)); } @@ -1166,7 +1066,6 @@ uint color ImDrawVert vertex = drawList->VtxBuffer.Data[i]; if (vertex.Col != color) continue; - minimumX = MathF.Min(minimumX, vertex.Pos.X); maximumX = MathF.Max(maximumX, vertex.Pos.X); } @@ -1181,12 +1080,13 @@ private static void DrawInspectorCardFrame(bool assertHorizontalRange) NativeImGui.NewFrame(); NativeImGui.SetNextWindowSize(new Vector2(320f, 260f), (int)(ImGuiCond.Always)); bool isOpen = true; - EditorWidget.PanelWindow( + EditorUI.current.widgets.PanelWindow( "Inspector Width Test", ref isOpen, () => DrawInspectorCardBody(assertHorizontalRange), ImGuiWindowFlags.NoSavedSettings, - useWindowPadding: false); + useWindowPadding: false + ); NativeImGui.Render(); } @@ -1209,64 +1109,57 @@ private static void DrawInspectorCardBody(bool assertHorizontalRange) Assert.Equal(panelBodySize, NativeImGui.GetWindowSize()); } - EditorWidget.ConstrainedContent( - "##inspector_content", - () => + EditorUI.current.widgets.ConstrainedContent("##inspector_content", () => + { + if (assertHorizontalRange) { - if (assertHorizontalRange) - { - Assert.Equal( - EditorWidget.style.windowPadding, - Unsafe.BitCast(NativeImGui.Raw.GetCurrentWindow()->WindowPadding)); - Assert.Equal( - NativeImGui.GetWindowPos() + EditorWidget.style.windowPadding, - NativeImGui.GetCursorScreenPos()); - } - - EditorWidget.LabelChip("Tag", EditorPalette.inspectorTagLabel); - float tagMaximumX = NativeImGui.GetItemRectMax().X; - NativeImGui.SameLine(0f, 0f); - NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); - if (assertHorizontalRange) - Assert.Equal(tagMaximumX, NativeImGui.GetItemRectMin().X, 3); - NativeImGui.SameLine(0f, EditorWidget.style.inspectorHeaderSectionSpacing); - EditorWidget.LabelChip("Layer", EditorPalette.inspectorLayerLabel); - float layerMaximumX = NativeImGui.GetItemRectMax().X; - NativeImGui.SameLine(0f, 0f); - NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); - if (assertHorizontalRange) - Assert.Equal(layerMaximumX, NativeImGui.GetItemRectMin().X, 3); + Assert.Equal(EditorUI.current.widgets.style.windowPadding, Unsafe.BitCast(NativeImGui.Raw.GetCurrentWindow()->WindowPadding)); + Assert.Equal(NativeImGui.GetWindowPos() + EditorUI.current.widgets.style.windowPadding, NativeImGui.GetCursorScreenPos()); + } - bool open = EditorWidget.CollapsingCard( + EditorUI.current.widgets.LabelChip("Tag", EditorPalette.inspectorTagLabel); + float tagMaximumX = NativeImGui.GetItemRectMax().X; + NativeImGui.SameLine(0f, 0f); + NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); + if (assertHorizontalRange) + Assert.Equal(tagMaximumX, NativeImGui.GetItemRectMin().X, 3); + NativeImGui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); + EditorUI.current.widgets.LabelChip("Layer", EditorPalette.inspectorLayerLabel); + float layerMaximumX = NativeImGui.GetItemRectMax().X; + NativeImGui.SameLine(0f, 0f); + NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); + if (assertHorizontalRange) + Assert.Equal(layerMaximumX, NativeImGui.GetItemRectMin().X, 3); + bool open = EditorUI.current.widgets.CollapsingCard( + "long_behavior", + "BehaviorWithAnIntentionallyLongTypeNameThatMustBeClipped", + drawLeadingControl: static () => NativeImGui.Dummy(new Vector2(NativeImGui.GetFrameHeight())), + drawTrailingControl: static () => NativeImGui.Dummy(new Vector2(48f, NativeImGui.GetFrameHeight())), + trailingControlWidth: 48f + ); + if (open) + { + NativeImGui.Unindent(); + EditorUI.current.widgets.CardBody( "long_behavior", - "BehaviorWithAnIntentionallyLongTypeNameThatMustBeClipped", - drawLeadingControl: static () => NativeImGui.Dummy( - new Vector2(NativeImGui.GetFrameHeight())), - drawTrailingControl: static () => NativeImGui.Dummy( - new Vector2(48f, NativeImGui.GetFrameHeight())), - trailingControlWidth: 48f); - if (open) - { - NativeImGui.Unindent(); - EditorWidget.CardBody( - "long_behavior", - static () => EditorWidget.PropertyRow( - "long_property", - "Property With A Very Long Display Name", - static () => NativeImGui.Dummy(new Vector2( - MathF.Max(1f, NativeImGui.GetContentRegionAvail().X), - NativeImGui.GetFrameHeight())))); - NativeImGui.Indent(); - NativeImGui.TreePop(); - NativeImGui.Dummy(Vector2.Zero); - } - if (assertHorizontalRange) - Assert.Equal(0f, NativeImGui.GetScrollMaxX(), 3); - }); + static () => EditorUI.current.widgets.PropertyRow( + "long_property", + "Property With A Very Long Display Name", + static () => NativeImGui.Dummy(new Vector2(MathF.Max(1f, NativeImGui.GetContentRegionAvail().X), NativeImGui.GetFrameHeight())) + ) + ); + NativeImGui.Indent(); + NativeImGui.TreePop(); + NativeImGui.Dummy(Vector2.Zero); + } + + if (assertHorizontalRange) + Assert.Equal(0f, NativeImGui.GetScrollMaxX(), 3); + }); if (assertHorizontalRange) Assert.Equal(0f, NativeImGui.GetScrollMaxX(), 3); } + NativeImGui.EndChild(); } - } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj b/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj index b8519a4b0..9a7a1c7da 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj +++ b/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj @@ -8,10 +8,17 @@ true true Editor + editor + $(InnoNativeTarget) $(DefineConstants);INNO_EDITOR + + + + + diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/InspectionPipelineTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/InspectionPipelineTests.cs index 38cd1d4b5..20f8500c0 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/InspectionPipelineTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/InspectionPipelineTests.cs @@ -1,3 +1,4 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; @@ -9,7 +10,6 @@ using System.IO; using System.Linq; using System.Numerics; - using Inno.Extensibility.Modules; using Inno.Core.Logging; using Inno.Extensibility.Types; @@ -21,35 +21,29 @@ using Inno.Editor.Inspection; using Inno.Editor.Interactions; using NativeImGui = Inno.Native.ImGui.ImGui; - using Xunit; namespace Inno.Editor.Scripting.Tests; - public sealed class InspectionPipelineTests : IDisposable { - private readonly string m_projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoInspectionPipelineTests", - Guid.NewGuid().ToString("N")); + private readonly string m_projectRoot = Path.Combine(Path.GetTempPath(), "InnoInspectionPipelineTests", Guid.NewGuid().ToString("N")); private readonly ModuleHost m_modules; private readonly TypeCatalog m_types; private readonly SerializationRegistry m_serialization; private readonly LogRouter m_logs = new(); private readonly EditorInteractionRuntime m_runtime; - public InspectionPipelineTests() { Directory.CreateDirectory(m_projectRoot); - m_modules = new ModuleHost(new ModuleHostOptions - { catalogSource = new DotNetAssemblyCatalogSource(typeof(InspectionPipelineTests).Assembly) }); + m_modules = new ModuleHost(new ModuleHostOptions { catalogSource = new DotNetAssemblyCatalogSource(typeof(InspectionPipelineTests).Assembly) }); m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); m_serialization = new SerializationRegistry(m_types, new ReflectionSerializationMetadataSource()); m_runtime = new EditorInteractionRuntime( new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_types, m_logs, - [m_types, m_serialization]); + [m_types, m_serialization] + ); m_runtime.Start(); } @@ -68,22 +62,9 @@ public void Dispose() public unsafe void InlineChildFailureIsConsumedAndReadonlyDisabledScopeRemainsBalanced() { EditorContext editor = m_runtime.context; - using var drawers = new PropertyDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - using var attributes = new InspectorAttributeDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - var renderer = new SerializedPropertyRenderer( - drawers, - attributes, - m_runtime.interactions, - new NoopEditService(), - m_logs); + using var drawers = new PropertyDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + using var attributes = new InspectorAttributeDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + var renderer = new SerializedPropertyRenderer(drawers, attributes, m_runtime.interactions, new NoopEditService(), m_logs); Assert.IsType(drawers.Resolve(typeof(InlineParent))); var owner = new InlineOwner(); SerializedProperty property = Assert.Single(m_serialization.GetProperties(owner)); @@ -91,13 +72,14 @@ public unsafe void InlineChildFailureIsConsumedAndReadonlyDisabledScopeRemainsBa var nativeContext = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); Inno.Native.ImGui.ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640f, 480f)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; float originalAlpha = NativeImGui.Raw.GetStyle()->Alpha; - NativeImGui.NewFrame(); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated("Inline Property Test", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText0.pointer, null, 0); @@ -110,8 +92,8 @@ public unsafe void InlineChildFailureIsConsumedAndReadonlyDisabledScopeRemainsBa { NativeImGui.End(); } - NativeImGui.Render(); + NativeImGui.Render(); Assert.True(InlineParentDrawer.drewAfterFailure); Assert.Equal(originalAlpha, NativeImGui.Raw.GetStyle()->Alpha); } @@ -127,13 +109,11 @@ public unsafe void PanelWindowFailureStillEndsTheWindowAndAllowsLaterContent() var nativeContext = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); PrepareNativeFrame(); bool isOpen = true; - - Assert.Throws(() => EditorWidget.PanelWindow( - "Throwing Panel", - ref isOpen, - static () => throw new InvalidOperationException("panel"))); + Assert.Throws(() => EditorUI.current.widgets.PanelWindow("Throwing Panel", ref isOpen, static () => throw new InvalidOperationException("panel"))); using NativeStringLease nativeTestText1 = Utf8Codec.EncodeNullTerminated("Content After Panel Failure", allowsNull: false); _ = NativeImGui.Raw.Begin((sbyte*)nativeTestText1.pointer, null, 0); NativeImGui.TextUnformatted("Still drawing."); @@ -150,36 +130,26 @@ public unsafe void PanelWindowFailureStillEndsTheWindowAndAllowsLaterContent() public unsafe void TextEditStateIsScopedByRendererOwnerAndPropertyPath() { EditorContext editor = m_runtime.context; - using var drawers = new PropertyDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - using var attributes = new InspectorAttributeDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - var firstRenderer = new SerializedPropertyRenderer( - drawers, - attributes, - m_runtime.interactions, - new NoopEditService(), - m_logs); - var secondRenderer = new SerializedPropertyRenderer( - drawers, - attributes, - m_runtime.interactions, - new NoopEditService(), - m_logs); - var firstOwner = new TextStateOwner { value = new TextStateValue("First") }; - var secondOwner = new TextStateOwner { value = new TextStateValue("Second") }; + using var drawers = new PropertyDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + using var attributes = new InspectorAttributeDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + var firstRenderer = new SerializedPropertyRenderer(drawers, attributes, m_runtime.interactions, new NoopEditService(), m_logs); + var secondRenderer = new SerializedPropertyRenderer(drawers, attributes, m_runtime.interactions, new NoopEditService(), m_logs); + var firstOwner = new TextStateOwner + { + value = new TextStateValue("First") + }; + var secondOwner = new TextStateOwner + { + value = new TextStateValue("Second") + }; SerializedProperty firstProperty = Assert.Single(m_serialization.GetProperties(firstOwner)); SerializedProperty secondProperty = Assert.Single(m_serialization.GetProperties(secondOwner)); TextStateDrawer.observations.Clear(); var nativeContext = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); DrawPropertyFrame(firstRenderer, editor, firstOwner, firstProperty); DrawPropertyFrame(firstRenderer, editor, firstOwner, firstProperty); DrawPropertyFrame(firstRenderer, editor, secondOwner, secondProperty); @@ -190,46 +160,28 @@ public unsafe void TextEditStateIsScopedByRendererOwnerAndPropertyPath() NativeImGui.Raw.DestroyContext(nativeContext); } - Assert.Equal( - ["First:", "First:First", "Second:", "First:"], - TextStateDrawer.observations); + Assert.Equal(["First:", "First:First", "Second:", "First:"], TextStateDrawer.observations); } [Fact] public unsafe void PresentationAttributesComposeAndCustomDrawersRemainExtensible() { EditorContext editor = m_runtime.context; - using var drawers = new PropertyDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - using var attributes = new InspectorAttributeDrawerRegistry( - m_runtime.interactions, - m_types, - m_serialization, - []); - var renderer = new SerializedPropertyRenderer( - drawers, - attributes, - m_runtime.interactions, - new NoopEditService(), - m_logs); + using var drawers = new PropertyDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + using var attributes = new InspectorAttributeDrawerRegistry(m_runtime.interactions, m_types, m_serialization, []); + var renderer = new SerializedPropertyRenderer(drawers, attributes, m_runtime.interactions, new NoopEditService(), m_logs); var owner = new AttributeOwner(); - SerializedProperty property = Assert.Single( - m_serialization.GetProperties(owner), - static candidate => candidate.name == nameof(AttributeOwner.value)); + SerializedProperty property = Assert.Single(m_serialization.GetProperties(owner), static candidate => candidate.name == nameof(AttributeOwner.value)); AttributeValueDrawer.observations.Clear(); ProbeInspectorAttributeDrawer.observations.Clear(); var nativeContext = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); DrawPropertyFrame(renderer, editor, owner, property); Assert.Empty(AttributeValueDrawer.observations); - Assert.Equal( - ["update:Friendly Value:False:2:8"], - ProbeInspectorAttributeDrawer.observations); - + Assert.Equal(["update:Friendly Value:False:2:8"], ProbeInspectorAttributeDrawer.observations); owner.showValue = true; DrawPropertyFrame(renderer, editor, owner, property); } @@ -238,14 +190,7 @@ public unsafe void PresentationAttributesComposeAndCustomDrawersRemainExtensible NativeImGui.Raw.DestroyContext(nativeContext); } - Assert.Equal( - [ - "update:Friendly Value:False:2:8", - "update:Friendly Value:False:2:8", - "before", - "after" - ], - ProbeInspectorAttributeDrawer.observations); + Assert.Equal(["update:Friendly Value:False:2:8", "update:Friendly Value:False:2:8", "before", "after"], ProbeInspectorAttributeDrawer.observations); Assert.Equal(["Friendly Value:False:2:8"], AttributeValueDrawer.observations); } @@ -276,6 +221,7 @@ SerializedProperty property { NativeImGui.End(); } + NativeImGui.Render(); } @@ -285,7 +231,9 @@ public bool ChangeProperty( object owner, string propertyName, Action mutation, - string historyName + string historyName, + string? mergeKey, + string? componentName ) { mutation(); return true; @@ -305,7 +253,6 @@ internal sealed class InlineParent } internal sealed class InlineFailure; - internal sealed class TextStateOwner : ISerializable { [SerializableProperty] @@ -313,7 +260,6 @@ internal sealed class TextStateOwner : ISerializable } internal sealed record TextStateValue(string id); - internal sealed class AttributeOwner : ISerializable { internal bool showValue { get; set; } @@ -330,19 +276,14 @@ internal sealed class AttributeOwner : ISerializable } internal sealed class AttributeValue; - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] internal sealed class ProbeInspectorAttribute : InspectorPresentationAttribute; - [InspectorAttributeDrawer(typeof(ProbeInspectorAttribute), priority: 10_000)] internal sealed class ProbeInspectorAttributeDrawer : IInspectorAttributeDrawer { internal static List observations { get; } = []; - public void Update(InspectorAttributeDrawContext context) - => observations.Add( - $"update:{context.label}:{context.isReadOnly}:{context.minimum}:{context.maximum}"); - + public void Update(InspectorAttributeDrawContext context) => observations.Add($"update:{context.label}:{context.isReadOnly}:{context.minimum}:{context.maximum}"); public void DrawBefore(InspectorAttributeDrawContext context) { _ = context; @@ -361,9 +302,7 @@ internal sealed class AttributeValueDrawer : IPropertyDrawer { internal static List observations { get; } = []; - public void Draw(PropertyDrawContext context) - => observations.Add( - $"{context.label}:{context.isReadOnly}:{context.minimum}:{context.maximum}"); + public void Draw(PropertyDrawContext context) => observations.Add($"{context.label}:{context.isReadOnly}:{context.minimum}:{context.maximum}"); } [PropertyDrawer(typeof(TextStateValue), priority: 10_000)] @@ -384,16 +323,12 @@ public void Draw(PropertyDrawContext context) internal sealed class InlineParentDrawer : IPropertyDrawer { internal static bool drewAfterFailure; - public void Draw(PropertyDrawContext context) { var parent = (InlineParent)context.GetValue()!; - context.DrawInlineChild( - "child", - typeof(InlineFailure), - () => parent.child, - static _ => { }, - readOnly: true); + context.DrawInlineChild("child", typeof(InlineFailure), () => parent.child, static _ => + { + }, readOnly: true); drewAfterFailure = true; } } @@ -401,6 +336,5 @@ public void Draw(PropertyDrawContext context) [PropertyDrawer(typeof(InlineFailure), priority: 10_000)] internal sealed class InlineFailureDrawer : IPropertyDrawer { - public void Draw(PropertyDrawContext context) - => throw new InvalidOperationException("Inline drawer failure."); + public void Draw(PropertyDrawContext context) => throw new InvalidOperationException("Inline drawer failure."); } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs index fb909cae7..26696ff7b 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs @@ -1,3 +1,5 @@ +using ImGuiWindowFlags = Inno.Editor.ImGui.ImGuiWindowFlags; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using NativeVector4 = Inno.Native.ImGui.ImVec4C; @@ -11,23 +13,29 @@ using Xunit; namespace Inno.Editor.Scripting.Tests; - public sealed unsafe class InspectorPresentationLayoutTests : IDisposable { private readonly ImGuiNativeContext* m_context = NativeImGui.Raw.CreateContext(null); - + private readonly Inno.Editor.ImGui.EditorUiSession m_ui = new(); + private readonly IDisposable m_uiScope; public InspectorPresentationLayoutTests() { + m_uiScope = m_ui.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(640, 480)); io->DeltaTime = 1f / 60f; io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; io->Fonts->RendererHasTextures = 1; - EditorWidget.style.ResetZoom(); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.style.ResetZoom(); + EditorUI.current.widgets.SetupStyle(); } - public void Dispose() => NativeImGui.Raw.DestroyContext(m_context); + public void Dispose() + { + m_uiScope.Dispose(); + m_ui.Dispose(); + NativeImGui.Raw.DestroyContext(m_context); + } [Fact] public void FloatingPanelCloseButtonClosesOnMouseRelease() @@ -38,9 +46,10 @@ void Frame() NativeImGui.NewFrame(); NativeImGui.SetNextWindowPos(new Vector2(30, 30)); NativeImGui.SetNextWindowSize(new Vector2(320, 180)); - EditorWidget.PanelWindow("Floating acceptance", ref open, () => NativeImGui.TextUnformatted("Body")); + EditorUI.current.widgets.PanelWindow("Floating acceptance", ref open, () => NativeImGui.TextUnformatted("Body")); NativeImGui.Render(); } + Frame(); Frame(); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated("Floating acceptance", allowsNull: false); @@ -66,8 +75,8 @@ float zoom ) { ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = Unsafe.BitCast(new Vector2(width, height)); - EditorWidget.style.SetZoom(zoom); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.style.SetZoom(zoom); + EditorUI.current.widgets.SetupStyle(); Vector2 hover = default; const string text = "The profile is evaluated after this camera stack is composed. The final words must remain readable."; void Frame() @@ -79,14 +88,16 @@ void Frame() NativeImGui.Raw.Begin((sbyte*)nativeTestText1.pointer, null, 0); NativeImGui.Button("Hover near edge"); hover = (NativeImGui.GetItemRectMin() + NativeImGui.GetItemRectMax()) * 0.5f; - EditorWidget.DrawItemTooltip(text); + EditorUI.current.widgets.DrawItemTooltip(text); NativeImGui.End(); NativeImGui.Render(); } + Frame(); Frame(); NativeImGui.Raw.AddMousePosEvent(io, hover.X, hover.Y); - for (int index = 0; index < 4; index++) Frame(); + for (int index = 0; index < 4; index++) + Frame(); using NativeStringLease nativeTestText2 = Utf8Codec.EncodeNullTerminated("##Tooltip_00", allowsNull: false); ImGuiWindow* tooltip = NativeImGui.Raw.FindWindowByName((sbyte*)nativeTestText2.pointer); Assert.False((tooltip == null)); @@ -109,7 +120,7 @@ public void HelpBoxUsesWrappedCardHeightWithoutHorizontalOverflow() using NativeStringLease nativeTestText3 = Utf8Codec.EncodeNullTerminated("HelpBox acceptance", allowsNull: false); NativeImGui.Raw.Begin((sbyte*)nativeTestText3.pointer, null, 0); float width = NativeImGui.GetContentRegionAvail().X; - EditorWidget.HelpBox("Assign a Sprite to enable atlas geometry, borders, pixel density, and sampling controls.", "i", EditorPalette.warning); + EditorUI.current.widgets.HelpBox("Assign a Sprite to enable atlas geometry, borders, pixel density, and sampling controls.", "i", EditorPalette.warning); Vector2 size = NativeImGui.GetItemRectSize(); Assert.Equal(width, size.X, 2); Assert.True(size.Y > NativeImGui.GetTextLineHeight() * 2); @@ -130,8 +141,8 @@ public void RoundedWindowTitleAndBodyHaveContinuousOpaqueCoverage( float framebufferScale ) { NativeImGui.Raw.GetIO()->DisplayFramebufferScale = Unsafe.BitCast(new Vector2(framebufferScale)); - EditorWidget.style.SetZoom(zoom); - EditorWidget.SetupStyle(); + EditorUI.current.widgets.style.SetZoom(zoom); + EditorUI.current.widgets.SetupStyle(); NativeImGui.Raw.GetStyle()->WindowRounding = 6f * zoom; NativeImGui.Raw.GetStyle()->FrameBorderSize = 0; for (int frame = 0; frame < 2; frame++) @@ -156,13 +167,14 @@ float framebufferScale float coverage = CoverageAt(window->DrawList, point); Assert.True(coverage >= 0.999f, $"Transparent title/body join: zoom={zoom}, scale={framebufferScale}, y={point.Y}, alpha={coverage}"); } + for (int pixel = -2; pixel <= 2; pixel++) { float y = (MathF.Floor(seam * framebufferScale) + pixel + 0.5f) / framebufferScale; Vector2 point = new(window->Pos.X + window->Size.X * 0.47f, y); - Assert.True(CoverageAt(window->DrawList, point) >= 0.999f, - $"Transparent device pixel: zoom={zoom}, scale={framebufferScale}, y={y}"); + Assert.True(CoverageAt(window->DrawList, point) >= 0.999f, $"Transparent device pixel: zoom={zoom}, scale={framebufferScale}, y={y}"); } + Assert.True(NativeImGui.Raw.GetStyle()->AntiAliasedFill != 0); Assert.Equal(6f * zoom, window->WindowRounding); } @@ -187,15 +199,18 @@ Vector2 point Vector2 bPosition = Unsafe.BitCast(b.Pos); Vector2 cPosition = Unsafe.BitCast(c.Pos); float area = Cross(bPosition - aPosition, cPosition - aPosition); - if (MathF.Abs(area) < 0.000001f) continue; + if (MathF.Abs(area) < 0.000001f) + continue; float wa = Cross(bPosition - point, cPosition - point) / area; float wb = Cross(cPosition - point, aPosition - point) / area; float wc = 1 - wa - wb; - if (wa < 0 || wb < 0 || wc < 0) continue; + if (wa < 0 || wb < 0 || wc < 0) + continue; float alpha = (wa * (a.Col >> 24) + wb * (b.Col >> 24) + wc * (c.Col >> 24)) / 255f; result = alpha + result * (1 - alpha); } } + return result; } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs index 6a1277597..e8930ad37 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs @@ -17,7 +17,6 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; - using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Extensibility.Modules; @@ -40,13 +39,10 @@ using Xunit; namespace Inno.Editor.Scripting.Tests; - public sealed class ScriptingPipelineTests : IDisposable { private readonly ScriptingFixture m_fixture = new(); - public void Dispose() => m_fixture.Dispose(); - [Theory] [InlineData(false)] [InlineData(true)] @@ -74,31 +70,20 @@ public void SourceLocalAssetPathsFollowInstalledPluginOwnership(bool runtimeDepl using var fixture = new ScriptingFixture(( root, serialization - ) => WritePluginPackage(root, "local-path.iplugin", serialization, - new PluginManifest { pluginId = "tests.local-path", displayName = "Source Ownership Fixture" }, - new Dictionary - { - ["Assets/Nested.imeta"] = serialization.Serialize(new ScriptingAssetSourceMeta - { - persistentId = Guid.NewGuid(), - sourceKind = (int)AssetSourceKind.Directory - }), - ["Assets/Nested/SourceLocalPathProbe.cs"] = System.Text.Encoding.UTF8.GetBytes(""" + ) => WritePluginPackage(root, "local-path.iplugin", serialization, new PluginManifest { + pluginId = "tests.local-path", + displayName = "Source Ownership Fixture" + }, new Dictionary { ["Assets/Nested.imeta"] = serialization.Serialize(new ScriptingAssetSourceMeta { persistentId = Guid.NewGuid(), sourceKind = (int)AssetSourceKind.Directory }), ["Assets/Nested/SourceLocalPathProbe.cs"] = System.Text.Encoding.UTF8.GetBytes(""" using InnoEngine.Assets; public static class SourceLocalPathProbe { public static AssetPath Resolve() => Assets.LocalPath("Materials/Default.imaterial"); } - """), - ["Assets/Nested/SourceLocalPathProbe.cs.imeta"] = CreateScriptSourceMeta(serialization, Guid.NewGuid()) - })); + """), ["Assets/Nested/SourceLocalPathProbe.cs.imeta"] = CreateScriptSourceMeta(serialization, Guid.NewGuid()) })); ScriptCompilationResult result = runtimeDeployment ? fixture.CompileRuntimeDeployment() : fixture.Compile(); Assert.True(result.success, FormatDiagnostics(result)); - string assemblyPath = Assert.Single(result.runtimeAssemblyPaths.Where(static path => - Path.GetFileName(path).StartsWith("Inno.Plugin.", StringComparison.Ordinal))); - + string assemblyPath = Assert.Single(result.runtimeAssemblyPaths.Where(static path => Path.GetFileName(path).StartsWith("Inno.Plugin.", StringComparison.Ordinal))); AssetPath path = InvokePublicPathProbe(assemblyPath); - Assert.Equal(new AssetSourceId("tests.local-path"), path.source); Assert.Equal("Materials/Default.imaterial", path.localPath); } @@ -119,8 +104,7 @@ public static class SourceLocalPathProbe string map = Assert.Single(project.Descendants("PathMap")).Value; Assert.Contains("Nested,,==Folder", map, StringComparison.Ordinal); Assert.Contains("project::./Nested,,==Folder", map, StringComparison.Ordinal); - Assert.Contains(project.Descendants("HintPath"), reference => ContainsCustomAttribute( - Path.GetFullPath(reference.Value, m_fixture.projectRoot), "CallerFilePathAttribute")); + Assert.Contains(project.Descendants("HintPath"), reference => ContainsCustomAttribute(Path.GetFullPath(reference.Value, m_fixture.projectRoot), "CallerFilePathAttribute")); } [MethodImpl(MethodImplOptions.NoInlining)] @@ -131,9 +115,13 @@ private static AssetPath InvokePublicPathProbe(string assemblyPath) { using var stream = new MemoryStream(File.ReadAllBytes(assemblyPath)); var assembly = lifetime.LoadFromStream(stream); - Func resolve = assembly.GetType("SourceLocalPathProbe", throwOnError: true)! - .GetMethod("Resolve", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)! - .CreateDelegate>(); + Func resolve = assembly.GetType( + "SourceLocalPathProbe", + throwOnError: true + )!.GetMethod( + "Resolve", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static + )!.CreateDelegate>(); return resolve(); } finally @@ -160,8 +148,7 @@ public sealed class FrameworkReferenceProbe : GameBehavior using FileStream stream = File.OpenRead(Path.Combine(result.outputDirectory!, "Inno.GameScripts.dll")); using var executable = new PEReader(stream); MetadataReader metadata = executable.GetMetadataReader(); - Assert.DoesNotContain(metadata.AssemblyReferences, handle => - metadata.GetString(metadata.GetAssemblyReference(handle).Name) == "System.Private.CoreLib"); + Assert.DoesNotContain(metadata.AssemblyReferences, handle => metadata.GetString(metadata.GetAssemblyReference(handle).Name) == "System.Private.CoreLib"); } [Theory] @@ -178,8 +165,7 @@ public static class IgnoredApiProbe """); ScriptCompilationResult result = runtimeDeployment ? m_fixture.CompileRuntimeDeployment() : m_fixture.Compile(); Assert.False(result.success); - Assert.Contains(result.diagnostics, static diagnostic => diagnostic.id == "CS1061" - && diagnostic.message.Contains("CaptureFrom", StringComparison.Ordinal)); + Assert.Contains(result.diagnostics, static diagnostic => diagnostic.id == "CS1061" && diagnostic.message.Contains("CaptureFrom", StringComparison.Ordinal)); } [Fact] @@ -207,8 +193,13 @@ Action observer ScriptCompilationResult compilation = m_fixture.CompileRuntimeDeployment(); Assert.True(compilation.success, FormatDiagnostics(compilation)); m_fixture.compiler.GenerateProjectFiles(); + foreach (string project in new[] + { + "Inno.GameScripts.csproj", + "Inno.EditorScripts.csproj" + } - foreach (string project in new[] { "Inno.GameScripts.csproj", "Inno.EditorScripts.csproj" }) + ) { XDocument document = XDocument.Load(Path.Combine(m_fixture.projectRoot, project)); bool found = false; @@ -220,13 +211,12 @@ Action observer foreach (TypeDefinitionHandle handle in metadata.TypeDefinitions) { TypeDefinition type = metadata.GetTypeDefinition(handle); - if (metadata.GetString(type.Namespace) != "InnoEngine.Events" - || metadata.GetString(type.Name) != "EventDispatcher") + if (metadata.GetString(type.Namespace) != "InnoEngine.Events" || metadata.GetString(type.Name) != "EventDispatcher") continue; - found = type.GetEvents().Any(eventHandle => - metadata.GetString(metadata.GetEventDefinition(eventHandle).Name) == "dispatched"); + found = type.GetEvents().Any(eventHandle => metadata.GetString(metadata.GetEventDefinition(eventHandle).Name) == "dispatched"); } } + Assert.True(found, $"The IDE reference set in {project} must contain the dispatch observation event."); } } @@ -339,7 +329,6 @@ public override void Draw(ShaderNodeDrawContext context) m_fixture.compiler.GenerateProjectFiles(); Assert.True(ContainsShaderApi("Inno.EditorScripts.csproj")); Assert.False(ContainsShaderApi("Inno.GameScripts.csproj")); - bool ContainsShaderApi(string project) { XDocument document = XDocument.Load(Path.Combine(m_fixture.projectRoot, project)); @@ -353,8 +342,10 @@ bool ContainsShaderApi(string project) { TypeDefinition type = metadata.GetTypeDefinition(handle); return metadata.GetString(type.Namespace) == "InnoEditor.Rendering.Shaders" && metadata.GetString(type.Name) == "IShaderNodeCompiler"; - })) return true; + })) + return true; } + return false; } } @@ -416,9 +407,7 @@ public sealed class ProjectTools public EditorContext? context { get; set; } } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.True(result.success, FormatDiagnostics(result)); Assert.NotNull(result.outputDirectory); Assert.True(File.Exists(Path.Combine(result.outputDirectory!, "Inno.GameScripts.dll"))); @@ -443,9 +432,7 @@ internal static bool TryResolve(AssetPath path, out MaterialAsset? asset) => Assets.TryLoad(path, out asset); } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.True(result.success, FormatDiagnostics(result)); } @@ -469,9 +456,7 @@ public sealed class SettingsProbe : ISerializable { } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.True(result.success, FormatDiagnostics(result)); } @@ -480,19 +465,13 @@ public void RuntimeDeploymentCompilationDoesNotCompileOrValidateEditorSources() { m_fixture.Write("RuntimeOnly.cs", "public sealed class RuntimeOnly { }"); m_fixture.Write("BrokenTool.editor.cs", "this is deliberately invalid editor C#"); - ScriptCompilationResult result = m_fixture.CompileRuntimeDeployment(); - Assert.True(result.success, FormatDiagnostics(result)); Assert.NotNull(result.outputDirectory); Assert.True(File.Exists(Path.Combine(result.outputDirectory!, "Inno.GameScripts.dll"))); Assert.False(File.Exists(Path.Combine(result.outputDirectory!, "Inno.EditorScripts.dll"))); - Assert.DoesNotContain( - result.moduleDeployments, - static request => request.scope == AssemblyScope.Editor); - Assert.DoesNotContain( - result.compiledAssemblyNames.Concat(result.reusedAssemblyNames), - static assemblyName => assemblyName.Contains("Editor", StringComparison.Ordinal)); + Assert.DoesNotContain(result.moduleDeployments, static request => request.scope == AssemblyScope.Editor); + Assert.DoesNotContain(result.compiledAssemblyNames.Concat(result.reusedAssemblyNames), static assemblyName => assemblyName.Contains("Editor", StringComparison.Ordinal)); } [Fact] @@ -518,7 +497,6 @@ public sealed class AttributedBehavior : GameBehavior public int value { get; set; } } """); - using ScriptReloadHost reload = m_fixture.CreateReloadHost(); reload.Start(); ScriptCompilationResult authoring = m_fixture.CompilePending(reload); @@ -531,7 +509,6 @@ public sealed class AttributedBehavior : GameBehavior Assert.True(ContainsCustomAttribute(authoringAssembly, nameof(RangeAttribute))); Assert.True(ContainsCustomAttribute(authoringAssembly, "CustomPresentationAttribute")); Assert.True(ContainsCustomAttribute(authoringAssembly, nameof(SerializablePropertyAttribute))); - ScriptCompilationResult player = m_fixture.CompileRuntimeDeployment(); Assert.True(player.success, FormatDiagnostics(player)); string playerAssembly = Path.Combine(player.outputDirectory!, "Inno.GameScripts.dll"); @@ -544,10 +521,8 @@ public sealed class AttributedBehavior : GameBehavior using FileStream stream = File.OpenRead(playerAssembly); using var executable = new PEReader(stream); MetadataReader metadata = executable.GetMetadataReader(); - Assert.DoesNotContain(metadata.AssemblyReferences, handle => - metadata.GetString(metadata.GetAssemblyReference(handle).Name).Contains("Editor", StringComparison.Ordinal)); - Assert.DoesNotContain(metadata.TypeDefinitions, handle => - metadata.GetString(metadata.GetTypeDefinition(handle).Name) == "CustomPresentationAttribute"); + Assert.DoesNotContain(metadata.AssemblyReferences, handle => metadata.GetString(metadata.GetAssemblyReference(handle).Name).Contains("Editor", StringComparison.Ordinal)); + Assert.DoesNotContain(metadata.TypeDefinitions, handle => metadata.GetString(metadata.GetTypeDefinition(handle).Name) == "CustomPresentationAttribute"); } [Fact] @@ -561,17 +536,12 @@ public sealed class TargetBoundBehavior : GameBehavior } """); string targetRuntime = m_fixture.CreateDeploymentRuntime(); - ScriptCompilationResult first = m_fixture.CompileRuntimeDeployment(targetRuntime); - Assert.True(first.success, FormatDiagnostics(first)); File.Delete(Path.Combine(targetRuntime, "Inno.Scene.dll")); - ScriptCompilationResult second = m_fixture.CompileRuntimeDeployment(targetRuntime); - Assert.False(second.success); - Assert.Contains(second.diagnostics, static diagnostic => - diagnostic.severity == DiagnosticSeverity.Error); + Assert.Contains(second.diagnostics, static diagnostic => diagnostic.severity == DiagnosticSeverity.Error); } [Fact] @@ -584,9 +554,7 @@ public sealed class ForbiddenUsingBehavior : GameBehavior { } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.False(result.success); Assert.Contains(result.diagnostics, static diagnostic => diagnostic.id == "INNO2003"); } @@ -601,9 +569,7 @@ public sealed class RemovedBehaviorBase : Behavior { } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.False(result.success); Assert.Contains(result.diagnostics, static diagnostic => diagnostic.id == "CS0246"); } @@ -622,10 +588,8 @@ public sealed class EditorApiProbe m_fixture.Write("EditorApiProbe.cs", source); ScriptCompilationResult runtimeResult = m_fixture.Compile(); Assert.False(runtimeResult.success); - m_fixture.Move("EditorApiProbe.cs", "EditorApiProbe.editor.cs"); ScriptCompilationResult editorResult = m_fixture.Compile(); - Assert.True(editorResult.success, FormatDiagnostics(editorResult)); } @@ -643,31 +607,28 @@ public sealed class SecondaryProbe : GameBehavior { } """); - ScriptCompilationResult result = m_fixture.Compile(); - Assert.False(result.success); - Assert.Contains(result.diagnostics, static diagnostic => - diagnostic.id == "INNO2001" && - diagnostic.severity == DiagnosticSeverity.Error && - diagnostic.message.Contains("SecondaryProbe", StringComparison.Ordinal)); + Assert.Contains( + result.diagnostics, + static diagnostic => diagnostic.id == "INNO2001" + && diagnostic.severity == DiagnosticSeverity.Error + && diagnostic.message.Contains( + "SecondaryProbe", + StringComparison.Ordinal + ) + ); } [Fact] public void CachedCompilationReplaysWarningsAndReusesTheGeneration() { - m_fixture.Write( - "CachedWarningProbe.cs", - "public sealed class CachedWarningProbe { private int unused; }"); - + m_fixture.Write("CachedWarningProbe.cs", "public sealed class CachedWarningProbe { private int unused; }"); ScriptCompilationResult first = m_fixture.Compile(); ScriptCompilationResult second = m_fixture.Compile(); - Assert.True(first.success, FormatDiagnostics(first)); Assert.True(second.success, FormatDiagnostics(second)); - ScriptDiagnostic firstWarning = Assert.Single(first.diagnostics, static diagnostic => - diagnostic.id == "CS0169" && - diagnostic.severity == DiagnosticSeverity.Warning); + ScriptDiagnostic firstWarning = Assert.Single(first.diagnostics, static diagnostic => diagnostic.id == "CS0169" && diagnostic.severity == DiagnosticSeverity.Warning); Assert.Contains(second.diagnostics, diagnostic => diagnostic == firstWarning); Assert.Equal(first.outputDirectory, second.outputDirectory); Assert.NotEmpty(second.reusedAssemblyNames); @@ -678,17 +639,14 @@ public void CompilerProgressIsMonotonicAndIncludesStageTimings() { m_fixture.Write("ProgressProbe.cs", "public sealed class ProgressProbe { }"); var progress = new ProgressRecorder(); - ScriptCompilationResult result = m_fixture.Compile(progress); - Assert.True(result.success, FormatDiagnostics(result)); Assert.NotEmpty(progress.values); Assert.All(progress.values, static value => Assert.InRange(value.fraction, 0f, 1f)); Assert.True(progress.values.Zip(progress.values.Skip(1), static ( left, right - ) => - right.fraction >= left.fraction).All(static value => value)); + ) => right.fraction >= left.fraction).All(static value => value)); Assert.NotEmpty(result.stageTimings); Assert.All(result.stageTimings, static timing => Assert.True(timing.elapsed >= TimeSpan.Zero)); } @@ -699,18 +657,9 @@ public void PreCanceledCompilationDoesNotPublishAnArtifactGeneration() m_fixture.Write("CanceledProbe.cs", "public sealed class CanceledProbe { }"); using var cancellation = new CancellationTokenSource(); cancellation.Cancel(); - - Assert.Throws(() => - m_fixture.Compile(cancellationToken: cancellation.Token)); - - string artifactRoot = Path.Combine( - m_fixture.projectRoot, - "Library", - "Artifacts", - "ScriptAssemblies"); - Assert.True( - !Directory.Exists(artifactRoot) || - !Directory.EnumerateDirectories(artifactRoot).Any()); + Assert.Throws(() => m_fixture.Compile(cancellationToken: cancellation.Token)); + string artifactRoot = Path.Combine(m_fixture.projectRoot, "Library", "Artifacts", "ScriptAssemblies"); + Assert.True(!Directory.Exists(artifactRoot) || !Directory.EnumerateDirectories(artifactRoot).Any()); } [Fact] @@ -718,39 +667,30 @@ public void LogicalDocumentationProjectsNamespacesWithoutLosingMemberDescription { m_fixture.compiler.GenerateProjectFiles(); XDocument project = XDocument.Load(Path.Combine(m_fixture.projectRoot, "Inno.GameScripts.csproj")); - XElement[] members = project.Descendants("HintPath") - .Select(reference => Path.ChangeExtension(Path.GetFullPath(reference.Value, m_fixture.projectRoot), ".xml")) - .Where(File.Exists) - .SelectMany(path => XDocument.Load(path).Descendants("member")) - .ToArray(); - - XElement manager = Assert.Single(members.Where(member => - member.Attribute("name")?.Value == "T:InnoEngine.Scene.SceneManager")); + XElement[] members = project.Descendants("HintPath").Select(reference => Path.ChangeExtension(Path.GetFullPath(reference.Value, m_fixture.projectRoot), ".xml")).Where(File.Exists).SelectMany(path => XDocument.Load(path).Descendants("member")).ToArray(); + XElement manager = Assert.Single(members.Where(member => member.Attribute("name")?.Value == "T:InnoEngine.Scene.SceneManager")); Assert.Contains("scene operations", manager.Element("summary")!.Value, StringComparison.Ordinal); - Assert.Contains(manager.Descendants("see"), reference => - reference.Attribute("cref")?.Value == "T:InnoEngine.Scene.SceneWorld"); - Assert.Contains(members, member => - member.Attribute("name")?.Value == "P:InnoEngine.Scene.SceneManager.activeScene" && - member.Element("summary")?.Value.Contains("active scene", StringComparison.Ordinal) == true); - Assert.DoesNotContain(members, member => - member.Attribute("name")?.Value.StartsWith("T:Inno.Scene.", StringComparison.Ordinal) == true); + Assert.Contains(manager.Descendants("see"), reference => reference.Attribute("cref")?.Value == "T:InnoEngine.Scene.SceneWorld"); + Assert.Contains( + members, + member => member.Attribute("name")?.Value == "P:InnoEngine.Scene.SceneManager.activeScene" + && member.Element("summary")?.Value.Contains( + "active scene", + StringComparison.Ordinal + ) == true + ); + Assert.DoesNotContain(members, member => member.Attribute("name")?.Value.StartsWith("T:Inno.Scene.", StringComparison.Ordinal) == true); } [Fact] public void GenerateProjectFilesUsesTheSameRuntimeAndEditorClassification() { - m_fixture.Write( - "Runtime.cs", - "using InnoEngine.Scene; public sealed class RuntimeScript : GameBehavior { }"); + m_fixture.Write("Runtime.cs", "using InnoEngine.Scene; public sealed class RuntimeScript : GameBehavior { }"); m_fixture.Write("Tools.editor.cs", "public sealed class EditorScript { }"); m_fixture.Rescan(); - m_fixture.compiler.GenerateProjectFiles(); - - string gameProject = File.ReadAllText( - Path.Combine(m_fixture.projectRoot, "Inno.GameScripts.csproj")); - string editorProject = File.ReadAllText( - Path.Combine(m_fixture.projectRoot, "Inno.EditorScripts.csproj")); + string gameProject = File.ReadAllText(Path.Combine(m_fixture.projectRoot, "Inno.GameScripts.csproj")); + string editorProject = File.ReadAllText(Path.Combine(m_fixture.projectRoot, "Inno.EditorScripts.csproj")); Assert.Contains("Compile Include=\"Assets/Runtime.cs\"", gameProject); Assert.DoesNotContain("Tools.editor.cs", gameProject); Assert.Contains("Compile Include=\"Assets/Tools.editor.cs\"", editorProject); @@ -762,33 +702,19 @@ public void GenerateProjectFilesUsesTheSameRuntimeAndEditorClassification() [Fact] public void ProjectTildeDirectoriesRemainBrowsableAndParticipateInAuthoringCompilation() { - m_fixture.Write( - "Runtime.cs", - "using InnoEngine.Scene; public sealed class RuntimeScript : GameBehavior { }"); - m_fixture.Write( - "~Examples/Example.cs", - "using InnoEngine.Scene; public sealed class ExampleScript : GameBehavior { }"); + m_fixture.Write("Runtime.cs", "using InnoEngine.Scene; public sealed class RuntimeScript : GameBehavior { }"); + m_fixture.Write("~Examples/Example.cs", "using InnoEngine.Scene; public sealed class ExampleScript : GameBehavior { }"); m_fixture.Rescan(); - - Assert.True(m_fixture.assets.TryGetFileSystemEntry( - AssetPath.Project("~Examples"), - out AssetFileEntry tildeRoot)); + Assert.True(m_fixture.assets.TryGetFileSystemEntry(AssetPath.Project("~Examples"), out AssetFileEntry tildeRoot)); Assert.False(tildeRoot.isSample); Assert.False(tildeRoot.isSampleContent); - Assert.True(m_fixture.assets.TryGetFileSystemEntry( - AssetPath.Project("~Examples/Example.cs"), - out AssetFileEntry tildeSource)); + Assert.True(m_fixture.assets.TryGetFileSystemEntry(AssetPath.Project("~Examples/Example.cs"), out AssetFileEntry tildeSource)); Assert.False(tildeSource.isSampleContent); - Assert.True(m_fixture.assets.TryGetInfo( - AssetPath.Project("~Examples/Example.cs"), - out _)); - + Assert.True(m_fixture.assets.TryGetInfo(AssetPath.Project("~Examples/Example.cs"), out _)); ScriptCompilationResult compilation = m_fixture.Compile(); Assert.True(compilation.success, FormatDiagnostics(compilation)); - m_fixture.compiler.GenerateProjectFiles(); - string gameProject = File.ReadAllText( - Path.Combine(m_fixture.projectRoot, "Inno.GameScripts.csproj")); + string gameProject = File.ReadAllText(Path.Combine(m_fixture.projectRoot, "Inno.GameScripts.csproj")); Assert.Contains("Compile Include=\"Assets/Runtime.cs\"", gameProject); Assert.Contains("Compile Include=\"Assets/~Examples/Example.cs\"", gameProject); } @@ -806,48 +732,35 @@ public void InstalledPluginSampleImportsAsAnImmediatelyCompilableWritableCopy() Assert.True(fixture.assets.TryGetFileSystemEntry(sourceFile, out AssetFileEntry sampleContent)); Assert.True(sampleContent.isReadOnly); Assert.True(sampleContent.isSampleContent); - Assert.True(fixture.assets.TryGetInfo( - sourceFile, - out AssetInfo? templateInfo)); + Assert.True(fixture.assets.TryGetInfo(sourceFile, out AssetInfo? templateInfo)); Guid sourcePersistentId = Assert.IsType(templateInfo).persistentId; - ScriptCompilationResult authoring = fixture.Compile(); Assert.True(authoring.success, FormatDiagnostics(authoring)); - Assert.Contains(authoring.compiledAssemblyNames, - name => name.EndsWith(".Samples", StringComparison.Ordinal)); - Assert.DoesNotContain(authoring.runtimeAssemblyPaths, - path => Path.GetFileNameWithoutExtension(path).EndsWith(".Samples", StringComparison.Ordinal)); + Assert.Contains(authoring.compiledAssemblyNames, name => name.EndsWith(".Samples", StringComparison.Ordinal)); + Assert.DoesNotContain(authoring.runtimeAssemblyPaths, path => Path.GetFileNameWithoutExtension(path).EndsWith(".Samples", StringComparison.Ordinal)); ScriptCompilationResult player = fixture.CompileRuntimeDeployment(); Assert.True(player.success, FormatDiagnostics(player)); - Assert.DoesNotContain(player.compiledAssemblyNames, - name => name.EndsWith(".Samples", StringComparison.Ordinal)); - + Assert.DoesNotContain(player.compiledAssemblyNames, name => name.EndsWith(".Samples", StringComparison.Ordinal)); using AssetSampleImportTransaction import = PrepareSample(fixture, source); import.BeginValidation(async ( sources, cancellationToken ) => { - ScriptCompilationResult candidate = await fixture.compiler.CompileAuthoringGenerationAsync( - cancellationToken: cancellationToken, sourceSnapshot: sources); + ScriptCompilationResult candidate = await fixture.compiler.CompileAuthoringGenerationAsync(cancellationToken: cancellationToken, sourceSnapshot: sources); Assert.True(candidate.success, FormatDiagnostics(candidate)); }); Assert.True(SpinWait.SpinUntil(() => import.isValidationComplete, 60000)); import.Commit(); AssetPath imported = import.target; - Assert.Equal(AssetPath.Project("~Starter"), imported); - Assert.True(File.Exists(Path.Combine( - fixture.projectRoot, - "Assets", - "~Starter", - "StarterBehavior.cs"))); - Assert.DoesNotContain("ce3b52c6-2a07-42ea-b632-a307a0ef7407", + Assert.True(File.Exists(Path.Combine(fixture.projectRoot, "Assets", "~Starter", "StarterBehavior.cs"))); + Assert.DoesNotContain( + "ce3b52c6-2a07-42ea-b632-a307a0ef7407", File.ReadAllText(Path.Combine(fixture.projectRoot, "Assets", "~Starter", "StarterBehavior.cs")), - StringComparison.OrdinalIgnoreCase); - Assert.True(fixture.assets.TryGetInfo( - AssetPath.Project("~Starter/StarterBehavior.cs"), - out AssetInfo? importedInfo)); + StringComparison.OrdinalIgnoreCase + ); + Assert.True(fixture.assets.TryGetInfo(AssetPath.Project("~Starter/StarterBehavior.cs"), out AssetInfo? importedInfo)); Assert.NotEqual(sourcePersistentId, Assert.IsType(importedInfo).persistentId); Assert.True(fixture.assets.TryGetFileSystemEntry(imported, out AssetFileEntry importedDirectory)); Assert.False(importedDirectory.isReadOnly); @@ -856,8 +769,7 @@ public void InstalledPluginSampleImportsAsAnImmediatelyCompilableWritableCopy() ScriptCompilationResult compilation = fixture.Compile(); Assert.True(compilation.success, FormatDiagnostics(compilation)); fixture.compiler.GenerateProjectFiles(); - string gameProject = File.ReadAllText( - Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj")); + string gameProject = File.ReadAllText(Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj")); Assert.Contains("Compile Include=\"Assets/~Starter/StarterBehavior.cs\"", gameProject); } @@ -867,8 +779,7 @@ public void InvalidSampleScriptPreflightLeavesNoProjectCopy() using var fixture = new ScriptingFixture(( root, serialization - ) => - WriteSamplePlugin(root, serialization, "public sealed class BrokenSample { this is invalid; }")); + ) => WriteSamplePlugin(root, serialization, "public sealed class BrokenSample { this is invalid; }")); AssetPath source = new(new AssetSourceId("tests.samples"), "~Starter"); using AssetSampleImportTransaction import = PrepareSample(fixture, source); import.BeginValidation(async ( @@ -876,8 +787,7 @@ public void InvalidSampleScriptPreflightLeavesNoProjectCopy() cancellationToken ) => { - ScriptCompilationResult candidate = await fixture.compiler.CompileAuthoringGenerationAsync( - cancellationToken: cancellationToken, sourceSnapshot: sources); + ScriptCompilationResult candidate = await fixture.compiler.CompileAuthoringGenerationAsync(cancellationToken: cancellationToken, sourceSnapshot: sources); if (!candidate.success) throw new InvalidOperationException(FormatDiagnostics(candidate)); }); @@ -885,8 +795,7 @@ public void InvalidSampleScriptPreflightLeavesNoProjectCopy() Assert.Throws(() => import.Commit()); new Inno.Core.Execution.RetirementBarrier("Test sample import").Wait(import.Rollback); Assert.False(Directory.Exists(Path.Combine(fixture.projectRoot, "Assets", "~Starter"))); - Assert.False(fixture.assets.TryGetFileSystemEntry( - AssetPath.Project("~Starter"), out _)); + Assert.False(fixture.assets.TryGetFileSystemEntry(AssetPath.Project("~Starter"), out _)); } [Fact] @@ -898,8 +807,7 @@ public void SampleImportPreservesEveryLeadingTildeAndRejectsDestinationCollision Assert.False(AssetSample.Contains(AssetPath.Project("~~~Starter/File.cs"), false)); using var fixture = new ScriptingFixture(WriteSamplePlugin); fixture.assets.CreateDirectory(AssetPath.Project("~Starter")); - Assert.Throws(() => fixture.assets.PrepareSampleImport( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter"))); + Assert.Throws(() => fixture.assets.PrepareSampleImport(new AssetPath(new AssetSourceId("tests.samples"), "~Starter"))); fixture.host.modules.generations.EnsureReady("retry after rejected import"); } @@ -909,8 +817,7 @@ public void SampleValidationReturnsToOwnerWhileWorkIsPendingAndPublishesExactlyO using var fixture = new ScriptingFixture(WriteSamplePlugin); int notifications = 0; fixture.assets.Changed += _ => notifications++; - using AssetSampleImportTransaction import = PrepareSample( - fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = PrepareSample(fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); long revision = fixture.assets.revision; Assert.False(fixture.assets.TryGetFileSystemEntry(import.target, out _)); var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -925,6 +832,7 @@ public void SampleValidationReturnsToOwnerWhileWorkIsPendingAndPublishesExactlyO Assert.Equal(0, notifications); Assert.Equal(revision, fixture.assets.revision); } + Assert.Throws(() => import.Commit()); Assert.Throws(() => fixture.assets.CreateDirectory(AssetPath.Project("Concurrent"))); Assert.Throws(() => fixture.host.modules.generations.EnsureReady("reload while importing")); @@ -943,8 +851,7 @@ public void SampleCancellationRetainsDependenciesUntilUncooperativeValidationDra using var fixture = new ScriptingFixture(WriteSamplePlugin); int notifications = 0; fixture.assets.Changed += _ => notifications++; - using AssetSampleImportTransaction import = PrepareSample( - fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = PrepareSample(fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); import.BeginValidation(( _, @@ -970,8 +877,7 @@ public void SampleHistoryFinalizationFailureRollsBackWithoutPublication() using var fixture = new ScriptingFixture(WriteSamplePlugin); int notifications = 0; fixture.assets.Changed += _ => notifications++; - using AssetSampleImportTransaction import = PrepareSample( - fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = PrepareSample(fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); import.BeginValidation(( _, _ @@ -988,13 +894,19 @@ public void SampleHistoryFinalizationFailureRollsBackWithoutPublication() public void SampleCopyCancellationLeavesNoProjectOrTransactionDirectory() { using var fixture = new ScriptingFixture(WriteSamplePlugin); - using AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport(new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); import.Cancel(); Assert.True(SpinWait.SpinUntil(() => { - try { import.Rollback(); return true; } - catch (Inno.Core.Execution.RetirementPendingException) { return false; } + try + { + import.Rollback(); + return true; + } + catch (Inno.Core.Execution.RetirementPendingException) + { + return false; + } }, 15000)); Assert.False(Directory.Exists(Path.Combine(fixture.projectRoot, "Assets", "~Starter"))); string transactions = Path.Combine(fixture.assets.libraryRoot, "AssetDatabase", "Transactions"); @@ -1008,16 +920,22 @@ public void PendingAutomaticCompilationDefersUntilSampleRollback() using var fixture = new ScriptingFixture(WriteSamplePlugin); using ScriptReloadHost reload = fixture.CreateReloadHost(autoCompile: true); reload.Start(); - using AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport(new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); Assert.False(reload.TryCompilePending(out Task? pending)); Assert.Null(pending); Assert.True(reload.isCompilationPending); import.Cancel(); Assert.True(SpinWait.SpinUntil(() => { - try { import.Rollback(); return true; } - catch (Inno.Core.Execution.RetirementPendingException) { return false; } + try + { + import.Rollback(); + return true; + } + catch (Inno.Core.Execution.RetirementPendingException) + { + return false; + } }, 15000)); Assert.True(reload.TryCompilePending(out Task? compilation)); Assert.True(SpinWait.SpinUntil(() => compilation!.IsCompleted, 15000)); @@ -1033,8 +951,7 @@ public void EditorSampleImportUsesSharedModalAndUndoRedo() runtime.Start(); IEditorScriptCompilation scripting = Assert.IsAssignableFrom(ScriptingCompilationProbe.compilation); PumpEditorUntil(fixture, runtime, () => scripting.state == EditorScriptCompilationState.Ready, () => scripting.status); - Assert.True(fixture.assets.TryGetFileSystemEntry( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter"), out AssetFileEntry sample)); + Assert.True(fixture.assets.TryGetFileSystemEntry(new AssetPath(new AssetSourceId("tests.samples"), "~Starter"), out AssetFileEntry sample)); Assert.True(runtime.interactions.For("panel/asset.file-browser", sample).Execute("file-browser/import-sample")); EditorModalExtension modal = runtime.modals.Single(value => value.id == "asset-sample-import.progress"); Assert.True(modal.TryGetPresentation(out EditorModalExtension.Presentation presentation)); @@ -1044,9 +961,7 @@ public void EditorSampleImportUsesSharedModalAndUndoRedo() EditorModalExtension compilationModal = runtime.modals.Single(value => value.id == "scripting.compilation"); Assert.True(compilationModal.TryGetPresentation(out EditorModalExtension.Presentation compilationPolicy)); Assert.Equal(compilationPolicy.allowScrolling, presentation.allowScrolling); - PumpEditorUntil(fixture, runtime, - () => modal.TryGetPresentation(out EditorModalExtension.Presentation state) && !state.isVisible, - () => scripting.status); + PumpEditorUntil(fixture, runtime, () => modal.TryGetPresentation(out EditorModalExtension.Presentation state) && !state.isVisible, () => scripting.status); Assert.True(fixture.assets.TryGetFileSystemEntry(AssetPath.Project("~Starter"), out _)); Assert.True(runtime.interactions.history.Undo().succeeded); Assert.False(fixture.assets.TryGetFileSystemEntry(AssetPath.Project("~Starter"), out _)); @@ -1066,8 +981,7 @@ public void SampleTransformRunsOnWorkerAndEditorStopRetainsPendingImport() ControlledSampleSourceRewriter.current = control; try { - Assert.True(fixture.assets.TryGetFileSystemEntry( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter"), out AssetFileEntry sample)); + Assert.True(fixture.assets.TryGetFileSystemEntry(new AssetPath(new AssetSourceId("tests.samples"), "~Starter"), out AssetFileEntry sample)); Assert.True(runtime.interactions.For("panel/asset.file-browser", sample).Execute("file-browser/import-sample")); Assert.True(control.started.Wait(TimeSpan.FromSeconds(5))); Assert.NotEqual(Environment.CurrentManagedThreadId, control.workerThread); @@ -1097,10 +1011,18 @@ public void SampleTransformRunsOnWorkerAndEditorStopRetainsPendingImport() ControlledSampleSourceRewriter.current = null; Assert.True(SpinWait.SpinUntil(() => { - try { runtime.Dispose(); return true; } - catch (Inno.Core.Execution.RetirementPendingException) { return false; } + try + { + runtime.Dispose(); + return true; + } + catch (Inno.Core.Execution.RetirementPendingException) + { + return false; + } }, 15000)); } + Assert.False(Directory.Exists(Path.Combine(fixture.projectRoot, "Assets", "~Starter"))); fixture.host.modules.generations.Wait(); fixture.host.modules.generations.EnsureReady("continue after editor sample retirement"); @@ -1119,23 +1041,20 @@ public void SampleDirectoryMetadataRemapsNestedImportSettingsIdentities() string package = Path.Combine(root, "Plugins", "samples.iplugin"); using var archive = ZipFile.Open(package, ZipArchiveMode.Update); archive.GetEntry("Assets/~Starter.imeta")!.Delete(); - WritePluginPackageEntry(archive, "Assets/~Starter.imeta", serialization.Serialize(new ScriptingAssetSourceMeta - { - persistentId = Guid.Parse("7726b1d2-9aee-4d2c-a865-2fd53155095f"), - sourceKind = (int)AssetSourceKind.Directory, - importerSettingsBytes = serialization.Serialize(new SampleIdentitySettings { referenceId = sourceId }) - })); + WritePluginPackageEntry( + archive, + "Assets/~Starter.imeta", + serialization.Serialize(new ScriptingAssetSourceMeta { persistentId = Guid.Parse("7726b1d2-9aee-4d2c-a865-2fd53155095f"), sourceKind = (int)AssetSourceKind.Directory, importerSettingsBytes = serialization.Serialize(new SampleIdentitySettings { referenceId = sourceId }) }) + ); }); - using AssetSampleImportTransaction import = PrepareSample( - fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = PrepareSample(fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); import.BeginValidation(( sources, _ ) => { Assert.True(sources.TryGetInfo(AssetPath.Project("~Starter/StarterBehavior.cs"), out AssetInfo? info)); - ScriptingAssetSourceMeta metadata = fixture.host.serialization.Deserialize( - File.ReadAllBytes(Path.Combine(fixture.projectRoot, "Assets", "~Starter.imeta"))); + ScriptingAssetSourceMeta metadata = fixture.host.serialization.Deserialize(File.ReadAllBytes(Path.Combine(fixture.projectRoot, "Assets", "~Starter.imeta"))); SampleIdentitySettings settings = fixture.host.serialization.Deserialize(metadata.importerSettingsBytes); Assert.Equal(info!.persistentId, settings.referenceId); Assert.NotEqual(sourceId, settings.referenceId); @@ -1156,17 +1075,15 @@ public void SampleAssetPreimportRunsOnWorkerAndReceivesCancellation() WriteSamplePlugin(root, serialization); using var package = ZipFile.Open(Path.Combine(root, "Plugins", "samples.iplugin"), ZipArchiveMode.Update); WritePluginPackageEntry(package, "Assets/~Starter/Slow.samplebusy", System.Text.Encoding.UTF8.GetBytes("sample")); - WritePluginPackageEntry(package, "Assets/~Starter/Slow.samplebusy.imeta", serialization.Serialize(new ScriptingAssetSourceMeta - { - persistentId = Guid.Parse("587849fc-777c-4501-a1b2-ec042ca40314"), - sourceKind = (int)AssetSourceKind.File, - importerId = "tests.scripting.controlled-sample-asset" - })); + WritePluginPackageEntry( + package, + "Assets/~Starter/Slow.samplebusy.imeta", + serialization.Serialize(new ScriptingAssetSourceMeta { persistentId = Guid.Parse("587849fc-777c-4501-a1b2-ec042ca40314"), sourceKind = (int)AssetSourceKind.File, importerId = "tests.scripting.controlled-sample-asset" }) + ); }); using var control = new ControlledSampleAssetImporter.Control(); ControlledSampleAssetImporter.current = control; - AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport(new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); try { Assert.True(SpinWait.SpinUntil(() => @@ -1183,6 +1100,7 @@ public void SampleAssetPreimportRunsOnWorkerAndReceivesCancellation() Assert.False(fixture.assets.TryGetFileSystemEntry(import.target, out _)); Assert.Equal(revision, fixture.assets.revision); } + import.Cancel(); Assert.True(control.canceled.Wait(TimeSpan.FromSeconds(5))); } @@ -1191,6 +1109,7 @@ public void SampleAssetPreimportRunsOnWorkerAndReceivesCancellation() new Inno.Core.Execution.RetirementBarrier("Test sample preimport").Wait(import.Dispose); ControlledSampleAssetImporter.current = null; } + Assert.False(Directory.Exists(Path.Combine(fixture.projectRoot, "Assets", "~Starter"))); Assert.False(fixture.assets.TryGetFileSystemEntry(import.target, out _)); fixture.host.modules.generations.EnsureReady("continue after asset preimport cancellation"); @@ -1200,8 +1119,7 @@ public void SampleAssetPreimportRunsOnWorkerAndReceivesCancellation() public void SampleCompilerReferencePreparationRunsOnWorker() { using var fixture = new ScriptingFixture(WriteSamplePlugin); - using AssetSampleImportTransaction import = PrepareSample( - fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using AssetSampleImportTransaction import = PrepareSample(fixture, new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); using var progress = new CompilationGate(); try { @@ -1210,8 +1128,7 @@ public void SampleCompilerReferencePreparationRunsOnWorker() cancellationToken ) => { - ScriptCompilationResult result = await fixture.compiler.CompileAuthoringGenerationAsync( - progress, cancellationToken, sources).ConfigureAwait(false); + ScriptCompilationResult result = await fixture.compiler.CompileAuthoringGenerationAsync(progress, cancellationToken, sources).ConfigureAwait(false); Assert.True(result.success, FormatDiagnostics(result)); }); Assert.True(progress.started.Wait(TimeSpan.FromSeconds(5))); @@ -1228,6 +1145,7 @@ public void SampleCompilerReferencePreparationRunsOnWorker() progress.release.Set(); Assert.True(SpinWait.SpinUntil(() => import.isValidationComplete, 60000)); } + import.Commit(); Assert.True(fixture.assets.TryGetFileSystemEntry(import.target, out _)); } @@ -1235,7 +1153,7 @@ public void SampleCompilerReferencePreparationRunsOnWorker() /// /// Verifies nonblocking watcher suspension and reconciliation after successful or canceled imports. /// - /// + /// /// Whether the candidate is committed rather than rolled back. /// [Theory] @@ -1243,10 +1161,8 @@ public void SampleCompilerReferencePreparationRunsOnWorker() [InlineData(true)] public void SampleIndexingDoesNotWaitForWatcherQuietPeriod(bool commit) { - using var fixture = new ScriptingFixture( - WriteSamplePlugin, enableFileSystemWatcher: true, fileWatcherFlushDelayMs: 3000); - AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport( - new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); + using var fixture = new ScriptingFixture(WriteSamplePlugin, enableFileSystemWatcher: true, fileWatcherFlushDelayMs: 3000); + AssetSampleImportTransaction import = fixture.assets.PrepareSampleImport(new AssetPath(new AssetSourceId("tests.samples"), "~Starter")); try { TimeSpan maximumAdvance = TimeSpan.Zero; @@ -1259,8 +1175,7 @@ public void SampleIndexingDoesNotWaitForWatcherQuietPeriod(bool commit) return ready; }, 15000); Assert.True(indexed); - Assert.True(maximumAdvance < TimeSpan.FromSeconds(2), - $"Owner-thread Advance waited {maximumAdvance} with a three-second watcher quiet period."); + Assert.True(maximumAdvance < TimeSpan.FromSeconds(2), $"Owner-thread Advance waited {maximumAdvance} with a three-second watcher quiet period."); fixture.Write("External.txt", "Changed while watching was paused."); Assert.False(fixture.assets.TryGetFileSystemEntry(AssetPath.Project("External.txt"), out _)); if (commit) @@ -1307,18 +1222,14 @@ public sealed class UsesProjectionPlugin """); string staleProject = Path.Combine(fixture.projectRoot, "Inno.Plugin.Stale.csproj"); File.WriteAllText(staleProject, "stale"); - ScriptCompilationResult result = fixture.Compile(); fixture.compiler.GenerateProjectFiles(); - Assert.True(result.success, FormatDiagnostics(result)); Assert.False(File.Exists(staleProject)); - Assert.False(File.Exists(Path.Combine( - fixture.projectRoot, "Inno.Plugin.TestsProjection.csproj"))); + Assert.False(File.Exists(Path.Combine(fixture.projectRoot, "Inno.Plugin.TestsProjection.csproj"))); string solution = File.ReadAllText(Path.Combine(fixture.projectRoot, "InnoProject.sln")); Assert.DoesNotContain("Inno.Plugin.TestsProjection", solution, StringComparison.Ordinal); - string gameProject = File.ReadAllText( - Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj")); + string gameProject = File.ReadAllText(Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj")); Assert.Contains("Inno.Plugin.TestsProjection.dll", gameProject, StringComparison.Ordinal); Assert.DoesNotContain("ProjectReference Include=\"Inno.Plugin.TestsProjection.csproj\"", gameProject); } @@ -1326,31 +1237,20 @@ public sealed class UsesProjectionPlugin [Fact] public void SourceArtifactsAreCatalogedButRuntimeExportContainsNoSourceFiles() { - m_fixture.Write( - "Scripts/Tracked.cs", - "using InnoEngine.Scene; public sealed class Tracked : GameBehavior { }"); - + m_fixture.Write("Scripts/Tracked.cs", "using InnoEngine.Scene; public sealed class Tracked : GameBehavior { }"); ScriptCompilationResult result = m_fixture.Compile(); string contentRoot = Path.Combine(m_fixture.projectRoot, "RuntimeContent"); AssetRuntimeContentInfo exported = m_fixture.assets.ExportRuntimeArtifacts(contentRoot); - Assert.True(result.success, FormatDiagnostics(result)); - Assert.True(m_fixture.assets.TryGetInfo( - AssetPath.Project("Scripts/Tracked.cs"), - out AssetInfo? info)); + Assert.True(m_fixture.assets.TryGetInfo(AssetPath.Project("Scripts/Tracked.cs"), out AssetInfo? info)); Assert.NotNull(info); Assert.Equal("inno.editor.csharp-script", info.importerId); - Assert.True(m_fixture.assets.TryGetArtifact( - info.persistentId, - "source", - out AssetArtifactInfo? source)); + Assert.True(m_fixture.assets.TryGetArtifact(info.persistentId, "source", out AssetArtifactInfo? source)); Assert.NotNull(source); using (ArtifactLease encoded = m_fixture.assets.AcquireArtifact(info.persistentId, "source")) Assert.NotEmpty(encoded.ReadAllBytes()); Assert.Equal(0, exported.assetCount); - Assert.DoesNotContain( - Directory.EnumerateFiles(contentRoot, "*", SearchOption.AllDirectories), - static path => path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)); + Assert.DoesNotContain(Directory.EnumerateFiles(contentRoot, "*", SearchOption.AllDirectories), static path => path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)); } [Fact] @@ -1359,12 +1259,10 @@ public void SuccessfulReloadActivatesCandidateAndFailedCompilationRetainsIt() m_fixture.WriteVersionedBehavior(1); using ScriptReloadHost reload = m_fixture.CreateReloadHost(); reload.Start(); - ScriptCompilationResult firstResult = m_fixture.CompilePending(reload); Assert.True(firstResult.success, FormatDiagnostics(firstResult)); Assert.True(reload.ApplyPendingReload()); WeakReference first = CaptureActiveType(m_fixture, "VersionedBehavior", expectedVersion: 1); - m_fixture.WriteVersionedBehavior(2); ScriptCompilationResult secondResult = m_fixture.CompilePending(reload); Assert.True(secondResult.success, FormatDiagnostics(secondResult)); @@ -1373,12 +1271,8 @@ public void SuccessfulReloadActivatesCandidateAndFailedCompilationRetainsIt() Assert.False(first.IsAlive, "The first script generation remained reachable after its reload barrier."); Type second = m_fixture.ResolveActiveType("VersionedBehavior"); Assert.Equal(2, ReadVersion(second)); - - m_fixture.Write( - "VersionedBehavior.cs", - "using InnoEngine.Scene; public sealed class VersionedBehavior : GameBehavior {"); + m_fixture.Write("VersionedBehavior.cs", "using InnoEngine.Scene; public sealed class VersionedBehavior : GameBehavior {"); ScriptCompilationResult failed = m_fixture.CompilePending(reload); - Assert.False(failed.success); Assert.False(reload.ApplyPendingReload()); Assert.Same(second, m_fixture.ResolveActiveType("VersionedBehavior")); @@ -1398,7 +1292,6 @@ public sealed class ProjectPresentation : GenericPresentation Assert.True(reload.ApplyPendingReload()); WeakReference runtime = CaptureActiveType(m_fixture, "VersionedBehavior", 1); WeakReference editor = CaptureActiveType(m_fixture, "ProjectPresentation", 1); - m_fixture.Write("Editor/GenericPresentation.editor.cs", """ public class GenericPresentation { public int version => 2; } public sealed class ProjectPresentation : GenericPresentation { } @@ -1432,7 +1325,6 @@ public sealed class ReloadableBehavior : GameBehavior ScriptCompilationResult firstResult = m_fixture.CompilePending(reload); Assert.True(firstResult.success, FormatDiagnostics(firstResult)); Assert.True(reload.ApplyPendingReload()); - using EditorInteractionRuntime runtime = m_fixture.CreateEditorRuntime(reloads); runtime.Start(); _ = Assert.IsAssignableFrom(SceneReloadProbe.workspace); @@ -1440,7 +1332,6 @@ public sealed class ReloadableBehavior : GameBehavior GameScene scene = m_fixture.editorSession.scenes.LoadNewSceneAdditive("Reload Test"); GameObject gameObject = scene.CreateObject("Reload Target"); RetiredScriptObject retired = AttachReloadableBehavior(m_fixture, gameObject); - ForceFullCollection(); m_fixture.Write("ReloadableBehavior.cs", """ using InnoEngine.Scene; @@ -1457,12 +1348,9 @@ public sealed class ReloadableBehavior : GameBehavior """); ScriptCompilationResult secondResult = m_fixture.CompilePending(reload); Assert.True(secondResult.success, FormatDiagnostics(secondResult)); - Assert.True(reload.ApplyPendingReload()); - Type currentType = m_fixture.ResolveActiveType("ReloadableBehavior"); - GameComponent current = Assert.Single(gameObject.GetComponents().Where(component => - string.Equals(component.GetType().Name, "ReloadableBehavior", StringComparison.Ordinal))); + GameComponent current = Assert.Single(gameObject.GetComponents().Where(component => string.Equals(component.GetType().Name, "ReloadableBehavior", StringComparison.Ordinal))); Assert.Same(currentType, current.GetType()); Assert.Equal(41, currentType.GetProperty("retained")!.GetValue(current)); Assert.Equal(2, currentType.GetProperty("added")!.GetValue(current)); @@ -1474,8 +1362,7 @@ public sealed class ReloadableBehavior : GameBehavior [Theory] [InlineData(false)] [InlineData(true)] - public void RemovedOrBrokenUpdatedPluginCommitsUnavailableGenerationAndRecovers( - bool updateInPlace) + public void RemovedOrBrokenUpdatedPluginCommitsUnavailableGenerationAndRecovers(bool updateInPlace) { using var fixture = new ScriptingFixture(WriteUnavailableGenerationPlugin); fixture.Write("DependentBehavior.cs", """ @@ -1499,52 +1386,38 @@ public sealed class DependentBehavior : GameBehavior ScriptCompilationResult initial = fixture.CompilePending(reload); Assert.True(initial.success, FormatDiagnostics(initial)); Assert.True(reload.ApplyPendingReload()); - RuntimeSession session = fixture.CreateEditorSession(); using IDisposable executionScope = session.EnterExecutionScope(); - var sceneReload = new SceneReloadService( - session.scenes, - fixture.host.serialization, - fixture.assets); + var sceneReload = new SceneReloadService(session.scenes, fixture.host.serialization, fixture.assets); using IDisposable registration = reloads.Register(new TestSceneReloadParticipant(sceneReload)); GameScene scene = session.scenes.LoadNewScene("Plugin Availability"); AvailabilitySceneState state = CreateAvailabilitySceneState(fixture, scene); - string installedPlugin = Path.Combine(fixture.projectRoot, "Plugins", "unavailability.iplugin"); string detachedPlugin = Path.Combine(fixture.projectRoot, "UnavailabilityPlugin.detached"); byte[] validPluginPackage = File.ReadAllBytes(installedPlugin); if (updateInPlace) { - WriteUnavailableGenerationPlugin( - fixture.projectRoot, - fixture.host.serialization, - "this Plugin update is deliberately invalid C#"); + WriteUnavailableGenerationPlugin(fixture.projectRoot, fixture.host.serialization, "this Plugin update is deliberately invalid C#"); } else { File.Move(installedPlugin, detachedPlugin); } - Assert.True(fixture.RefreshPlugins()); + Assert.True(fixture.RefreshPlugins()); ScriptCompilationResult unavailable = fixture.CompilePendingPluginReload(reload); Assert.False(unavailable.success); - Assert.Contains(unavailable.diagnostics, static diagnostic => - diagnostic.severity == DiagnosticSeverity.Error); + Assert.Contains(unavailable.diagnostics, static diagnostic => diagnostic.severity == DiagnosticSeverity.Error); Assert.True(reload.ApplyPendingReload()); - Assert.DoesNotContain(fixture.host.modules.modules, static module => - module.domain is AssemblyDomain.InnoPlugin or AssemblyDomain.InnoScripting); + Assert.DoesNotContain(fixture.host.modules.modules, static module => module.domain is AssemblyDomain.InnoPlugin or AssemblyDomain.InnoScripting); Assert.Equal(updateInPlace ? 1 : 0, fixture.activePlugins.Count); - - MissingGameComponent missingPlugin = Assert.IsType( - state.pluginOwner.GetComponents().Single(component => component is not Transform)); - MissingGameComponent missingScript = Assert.IsType( - state.scriptOwner.GetComponents().Single(component => component is not Transform)); + MissingGameComponent missingPlugin = Assert.IsType(state.pluginOwner.GetComponents().Single(component => component is not Transform)); + MissingGameComponent missingScript = Assert.IsType(state.scriptOwner.GetComponents().Single(component => component is not Transform)); Assert.Equal(state.pluginComponentId, missingPlugin.identity.persistentId); Assert.Equal(state.scriptComponentId, missingScript.identity.persistentId); Assert.Equal("UnavailabilityPlugin.PluginBehavior", missingPlugin.missingTypeName); Assert.Equal("DependentBehavior", missingScript.missingTypeName); CompleteUnloadVerification(reload); - if (updateInPlace) File.WriteAllBytes(installedPlugin, validPluginPackage); else @@ -1553,7 +1426,6 @@ public sealed class DependentBehavior : GameBehavior ScriptCompilationResult recoveredCompilation = fixture.CompilePendingPluginReload(reload); Assert.True(recoveredCompilation.success, FormatDiagnostics(recoveredCompilation)); Assert.True(reload.ApplyPendingReload()); - AssertRecoveredAvailability(state); } @@ -1581,11 +1453,13 @@ public void NeutralSettingsCacheDoesNotPinRetiredScriptTypesWithoutAnExplicitReb Assert.True(initial.success, FormatDiagnostics(initial)); Assert.True(reload.ApplyPendingReload()); using var detachedSettings = new ProjectSettingsStore( - new FileByteDocumentStore(Path.GetFullPath(Path.Combine(fixture.projectRoot, "DetachedSettings.inno"))), fixture.host.types, - fixture.host.serialization, new ProjectId("tests.detached.settings"), - AssetSerializationContext.Create(fixture.assets)); + new FileByteDocumentStore(Path.GetFullPath(Path.Combine(fixture.projectRoot, "DetachedSettings.inno"))), + fixture.host.types, + fixture.host.serialization, + new ProjectId("tests.detached.settings"), + AssetSerializationContext.Create(fixture.assets) + ); WeakReference previousType = CaptureCachedSettingType(detachedSettings); - WriteSetting(21); ScriptCompilationResult replacement = fixture.CompilePending(reload); Assert.True(replacement.success, FormatDiagnostics(replacement)); @@ -1593,7 +1467,6 @@ public void NeutralSettingsCacheDoesNotPinRetiredScriptTypesWithoutAnExplicitReb CompleteUnloadVerification(reload); Assert.False(previousType.IsAlive, "A neutral settings cache must not retain the old script type."); Assert.True(CaptureCachedSettingType(detachedSettings).IsAlive); - void WriteSetting(int value) { fixture.Write("DetachedSetting.cs", $$""" @@ -1628,12 +1501,10 @@ public void AuthoringRuntimeSessionDoesNotPinRetiredPluginAndScriptContexts() ScriptCompilationResult initial = fixture.CompilePending(reload); Assert.True(initial.success, FormatDiagnostics(initial)); Assert.True(reload.ApplyPendingReload()); - _ = fixture.CreateEditorSession(); ScriptCompilationResult replacement = fixture.CompilePendingPluginReload(reload); Assert.True(replacement.success, FormatDiagnostics(replacement)); Assert.True(reload.ApplyPendingReload()); - CompleteUnloadVerification(reload); } @@ -1647,23 +1518,16 @@ public void PluginRemovalUnloadsTheCommittedMissingGenerationWithoutASecondReloa ScriptCompilationResult initial = fixture.CompilePending(reload); Assert.True(initial.success, FormatDiagnostics(initial)); Assert.True(reload.ApplyPendingReload()); - RuntimeSession session = fixture.CreateEditorSession(); using IDisposable executionScope = session.EnterExecutionScope(); - var sceneReload = new SceneReloadService( - session.scenes, - fixture.host.serialization, - fixture.assets); + var sceneReload = new SceneReloadService(session.scenes, fixture.host.serialization, fixture.assets); using IDisposable registration = reloads.Register(new TestSceneReloadParticipant(sceneReload)); MissingGenerationExpectation expectation = CommitPluginRemoval(fixture, reload, session); - CompleteUnloadVerification(reload); Assert.False(expectation.retiredComponent.IsAlive, "The retired Plugin component remained strongly reachable."); Assert.False(expectation.retiredType.IsAlive, "The retired Plugin runtime Type remained strongly reachable."); - Assert.DoesNotContain(fixture.host.modules.modules, static module => - module.domain == AssemblyDomain.InnoPlugin); - MissingGameComponent missing = Assert.IsType( - expectation.owner.GetComponents().Single(component => component is not Transform)); + Assert.DoesNotContain(fixture.host.modules.modules, static module => module.domain == AssemblyDomain.InnoPlugin); + MissingGameComponent missing = Assert.IsType(expectation.owner.GetComponents().Single(component => component is not Transform)); Assert.Equal(expectation.componentId, missing.identity.persistentId); Assert.Equal("UnavailabilityPlugin.PluginBehavior", missing.missingTypeName); } @@ -1678,33 +1542,28 @@ public void PluginReloadWaitsForPlaySessionRetirementBeforeActivatingItsAssembly ScriptCompilationResult initial = fixture.CompilePending(reload); Assert.True(initial.success, FormatDiagnostics(initial)); Assert.True(reload.ApplyPendingReload()); - CompleteUnloadVerification(reload); var scenes = new PluginPlayScene(fixture); var history = new CountingHistoryIsolation(); using var playMode = new EditorPlayModeController( fixture.host, - new RuntimeSessionOptions - { + new RuntimeSessionOptions { kind = RuntimeSessionKind.Play, applicationId = "tests.scripting.play-reload", - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine( - fixture.projectRoot, - "Persistent", - "tests.scripting.play-reload"), "Logs")), + createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(fixture.projectRoot, "Persistent", "tests.scripting.play-reload"), "Logs")), jobExecutionMode = RuntimeJobExecutionMode.SingleThread, referenceResolvers = [fixture.assets] }, scenes, new ReadyScriptCompilation(), history, - fixture.host.logs); + fixture.host.logs + ); using IDisposable registration = reloads.RegisterGuard(playMode); Assert.True(playMode.EnterPlayMode()); playMode.AdvanceTransition(); playMode.AdvanceTransition(); Assert.Equal(EditorPlayModeState.Playing, playMode.state); - reload.ReloadPlugins(); Assert.True(reload.isCompilationPending); Assert.False(reloads.CanRequestReload(out _)); @@ -1716,7 +1575,6 @@ public void PluginReloadWaitsForPlaySessionRetirementBeforeActivatingItsAssembly Assert.False(reloads.CanActivateReload(out _)); playMode.AdvanceTransition(); playMode.AdvanceTransition(); - Assert.Equal(EditorPlayModeState.Editing, playMode.state); Assert.Equal(1, scenes.restoreCount); Assert.Equal(1, history.disposeCount); @@ -1782,24 +1640,17 @@ protected override async ValueTask ImportAsync( """); fixture.Write("Content/value.shutdownasset", "last-good"); (Guid persistentId, Guid stableTypeId) = LoadScriptAssetAndUnloadScripts(fixture); - Assert.Empty(fixture.host.modules.modules); Assert.True(fixture.assets.TryGetInfo(persistentId, out AssetInfo? preserved)); Assert.Equal(AssetImportStatus.Imported, preserved!.status); Assert.Equal(stableTypeId, preserved.stableAssetTypeId); - AssetObject missing = ((IAssetReferenceResolver)fixture.assets).Resolve( - persistentId, - stableTypeId, - "Content/value.shutdownasset", - typeof(AssetObject), - "$test"); + AssetObject missing = ((IAssetReferenceResolver)fixture.assets).Resolve(persistentId, stableTypeId, "Content/value.shutdownasset", typeof(AssetObject), "$test"); Assert.True(missing.isMissing); Assert.Equal(persistentId, missing.identity.persistentId); } [MethodImpl(MethodImplOptions.NoInlining)] - private static (Guid persistentId, Guid stableTypeId) LoadScriptAssetAndUnloadScripts( - ScriptingFixture fixture) + private static (Guid persistentId, Guid stableTypeId) LoadScriptAssetAndUnloadScripts(ScriptingFixture fixture) { using ScriptReloadHost reload = fixture.CreateReloadHost(new EditorReloadCoordinator()); reload.Start(); @@ -1807,9 +1658,7 @@ private static (Guid persistentId, Guid stableTypeId) LoadScriptAssetAndUnloadSc Assert.True(compilation.success, FormatDiagnostics(compilation)); Assert.True(reload.ApplyPendingReload()); Type assetType = fixture.ResolveActiveType("ShutdownAsset"); - AssetObject loaded = fixture.assets.Load( - AssetPath.Project("Content/value.shutdownasset"), - assetType); + AssetObject loaded = fixture.assets.Load(AssetPath.Project("Content/value.shutdownasset"), assetType); Assert.True(fixture.assets.TryGetInfo(loaded.identity.persistentId, out AssetInfo? imported)); Assert.Equal(AssetImportStatus.Imported, imported!.status); Guid persistentId = loaded.identity.persistentId; @@ -1830,21 +1679,32 @@ public void ContentPlayStopReloadPlaySoakRetiresEverySessionBeforeTheNextGenerat CompleteUnloadVerification(reload); var scenes = new PluginPlayScene(fixture); var history = new CountingHistoryIsolation(); - using var play = new EditorPlayModeController(fixture.host, new RuntimeSessionOptions - { - kind = RuntimeSessionKind.Play, applicationId = "tests.content-soak", - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(fixture.projectRoot, "Persistent", "tests.content-soak"), "Logs")), - jobExecutionMode = RuntimeJobExecutionMode.SingleThread, referenceResolvers = [fixture.assets] - }, scenes, new ReadyScriptCompilation(), history, fixture.host.logs); + using var play = new EditorPlayModeController( + fixture.host, + new RuntimeSessionOptions { + kind = RuntimeSessionKind.Play, + applicationId = "tests.content-soak", + createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(fixture.projectRoot, "Persistent", "tests.content-soak"), "Logs")), + jobExecutionMode = RuntimeJobExecutionMode.SingleThread, + referenceResolvers = [fixture.assets] + }, + scenes, + new ReadyScriptCompilation(), + history, + fixture.host.logs + ); using IDisposable registration = reloads.RegisterGuard(play); for (int iteration = 0; iteration < 8; iteration++) { Assert.True(play.EnterPlayMode()); - play.AdvanceTransition(); play.AdvanceTransition(); + play.AdvanceTransition(); + play.AdvanceTransition(); Assert.True(play.state == EditorPlayModeState.Playing, play.lastFailure); - for (int frame = 0; frame < 60; frame++) play.Tick(1f / 60f); + for (int frame = 0; frame < 60; frame++) + play.Tick(1f / 60f); Assert.True(play.ExitPlayMode()); - play.AdvanceTransition(); play.AdvanceTransition(); + play.AdvanceTransition(); + play.AdvanceTransition(); Assert.Equal(EditorPlayModeState.Editing, play.state); Assert.True(fixture.CompilePendingPluginReload(reload).success); Assert.True(reload.ApplyPendingReload()); @@ -1853,8 +1713,10 @@ public void ContentPlayStopReloadPlaySoakRetiresEverySessionBeforeTheNextGenerat Assert.Equal(iteration + 1, scenes.restoreCount); Assert.Equal(iteration + 1, history.disposeCount); } + Assert.True(play.EnterPlayMode()); - play.AdvanceTransition(); play.AdvanceTransition(); + play.AdvanceTransition(); + play.AdvanceTransition(); Assert.Equal(EditorPlayModeState.Playing, play.state); } @@ -1866,12 +1728,10 @@ public void AutomaticPublicationDoesNotQueueItselfButExternalMountChangesStillDo reload.Start(); Assert.True(m_fixture.CompilePending(reload).success); Assert.True(reload.ApplyPendingReload()); - Assert.False(reload.isCompilationPending, - "Publishing this script generation must not queue another generation from its own catalog notification."); + Assert.False(reload.isCompilationPending, "Publishing this script generation must not queue another generation from its own catalog notification."); CompleteUnloadVerification(reload); m_fixture.assets.ReplaceSourceMounts(m_fixture.assets.sourceMounts); - Assert.True(reload.isCompilationPending, - "An external source-mount publication must still invalidate scripting inputs."); + Assert.True(reload.isCompilationPending, "An external source-mount publication must still invalidate scripting inputs."); } [Fact] @@ -1881,11 +1741,8 @@ public void EditorAutomaticCompilationClosesModalAndTicketWaitsForRetirement() m_fixture.Rescan(); using EditorInteractionRuntime runtime = m_fixture.CreateEditorRuntime(); runtime.Start(); - IEditorScriptCompilation scripting = Assert.IsAssignableFrom( - ScriptingCompilationProbe.compilation); - PumpEditorUntil(m_fixture, runtime, - () => scripting.state == EditorScriptCompilationState.Ready, - () => scripting.status); + IEditorScriptCompilation scripting = Assert.IsAssignableFrom(ScriptingCompilationProbe.compilation); + PumpEditorUntil(m_fixture, runtime, () => scripting.state == EditorScriptCompilationState.Ready, () => scripting.status); AssertCompilationModalHidden(runtime); ScriptCompilationResult initial = Assert.IsType(scripting.lastCompilation); for (int frame = 0; frame < 150; frame++) @@ -1895,24 +1752,20 @@ public void EditorAutomaticCompilationClosesModalAndTicketWaitsForRetirement() Assert.Same(initial, scripting.lastCompilation); Thread.Sleep(5); } + List retained = RetainActiveScriptType(m_fixture); try { m_fixture.WriteVersionedBehavior(2); m_fixture.Rescan(); IScriptCompilationTicket ticket = scripting.RequestCompilation(); - PumpEditorUntil(m_fixture, runtime, - () => m_fixture.host.modules.generations.state == GenerationState.AwaitingCollection, - () => scripting.status); + PumpEditorUntil(m_fixture, runtime, () => m_fixture.host.modules.generations.state == GenerationState.AwaitingCollection, () => scripting.status); Assert.Equal(ScriptCompilationTicketState.Compiling, ticket.state); Assert.False(ticket.isCompleted); retained.Clear(); // Publication readiness may be advanced by another host boundary before the next editor update. m_fixture.host.modules.generations.Wait(); - PumpEditorUntil(m_fixture, runtime, - () => scripting.state == EditorScriptCompilationState.Ready && ticket.isCompleted, - () => scripting.status, - focused: false); + PumpEditorUntil(m_fixture, runtime, () => scripting.state == EditorScriptCompilationState.Ready && ticket.isCompleted, () => scripting.status, focused: false); Assert.Equal(ScriptCompilationTicketState.Succeeded, ticket.state); AssertCompilationModalHidden(runtime); Assert.Equal(2, ReadVersion(m_fixture.ResolveActiveType("VersionedBehavior"))); @@ -1933,8 +1786,13 @@ public void EditorQueuesLatestAutomaticChangeWithoutReplacingAcceptedTicketWhile using EditorInteractionRuntime runtime = fixture.CreateEditorRuntime(reloads); runtime.Start(); IEditorScriptCompilation scripting = Assert.IsAssignableFrom(ScriptingCompilationProbe.compilation); - PumpEditorUntil(fixture, runtime, () => scripting.state == EditorScriptCompilationState.Ready, - () => $"{scripting.state}: {scripting.status}; ticket={scripting.currentTicket?.state}", timeoutMilliseconds: 120000); + PumpEditorUntil( + fixture, + runtime, + () => scripting.state == EditorScriptCompilationState.Ready, + () => $"{scripting.state}: {scripting.status}; ticket={scripting.currentTicket?.state}", + timeoutMilliseconds: 120000 + ); ScriptCompilationResult initial = Assert.IsType(scripting.lastCompilation); IScriptCompilationTicket? accepted = scripting.currentTicket; var guard = new DeferredReloadGuard(); @@ -1954,23 +1812,27 @@ public void EditorQueuesLatestAutomaticChangeWithoutReplacingAcceptedTicketWhile runtime.Update(new EditorFrame(1f / 60f, frame / 60f, true)); Thread.Sleep(5); } + Assert.Same(initial, scripting.lastCompilation); Assert.Same(accepted, scripting.currentTicket); Assert.Contains("fixture active Play", scripting.status); AssertCompilationModalHidden(runtime); } + guard.isBlocked = false; - PumpEditorUntil(fixture, runtime, + PumpEditorUntil( + fixture, + runtime, () => scripting.state == EditorScriptCompilationState.Ready && !ReferenceEquals(initial, scripting.lastCompilation), - () => $"{scripting.state}: {scripting.status}; ticket={scripting.currentTicket?.state}", timeoutMilliseconds: 120000); + () => $"{scripting.state}: {scripting.status}; ticket={scripting.currentTicket?.state}", + timeoutMilliseconds: 120000 + ); Assert.Equal(GenerationState.Ready, fixture.host.generations.state); Assert.Equal(3, ReadLatestVersion(fixture)); } [MethodImpl(MethodImplOptions.NoInlining)] - private static int ReadLatestVersion(ScriptingFixture fixture) - => ReadVersion(fixture.ResolveActiveType("VersionedBehavior")); - + private static int ReadLatestVersion(ScriptingFixture fixture) => ReadVersion(fixture.ResolveActiveType("VersionedBehavior")); private sealed class DeferredReloadGuard : IEditorReloadGuard { internal bool isBlocked { get; set; } = true; @@ -1980,9 +1842,7 @@ private sealed class DeferredReloadGuard : IEditorReloadGuard } [MethodImpl(MethodImplOptions.NoInlining)] - private static List RetainActiveScriptType(ScriptingFixture fixture) - => [fixture.ResolveActiveType("VersionedBehavior")]; - + private static List RetainActiveScriptType(ScriptingFixture fixture) => [fixture.ResolveActiveType("VersionedBehavior")]; private static void AssertCompilationModalHidden(EditorInteractionRuntime runtime) { EditorModalExtension modal = runtime.modals.Single(value => value.id == "scripting.compilation"); @@ -2016,12 +1876,9 @@ public void NewCompilationTicketSupersedesTheExactPreviousRequest() using EditorInteractionRuntime runtime = m_fixture.CreateEditorRuntime(); runtime.Start(); _ = runtime.panelCount; - IEditorScriptCompilation scripting = Assert.IsAssignableFrom( - ScriptingCompilationProbe.compilation); - + IEditorScriptCompilation scripting = Assert.IsAssignableFrom(ScriptingCompilationProbe.compilation); IScriptCompilationTicket first = scripting.RequestCompilation(); IScriptCompilationTicket second = scripting.RequestCompilation(); - Assert.Equal(ScriptCompilationTicketState.Superseded, first.state); Assert.True(first.isCompleted); Assert.True(second.requestId > first.requestId); @@ -2032,14 +1889,7 @@ public void NewCompilationTicketSupersedesTheExactPreviousRequest() [Fact] public void FailedResultHasNoActivationOrRuntimeArtifacts() { - ScriptCompilationResult result = ScriptCompilationResult.Failure(new ScriptDiagnostic( - "INNO-TEST", - DiagnosticSeverity.Error, - "Injected compilation failure.", - filePath: null, - line: 0, - column: 0)); - + ScriptCompilationResult result = ScriptCompilationResult.Failure(new ScriptDiagnostic("INNO-TEST", DiagnosticSeverity.Error, "Injected compilation failure.", filePath: null, line: 0, column: 0)); Assert.False(result.success); Assert.Null(result.outputDirectory); Assert.Empty(result.moduleDeployments); @@ -2048,9 +1898,7 @@ public void FailedResultHasNoActivationOrRuntimeArtifacts() Assert.Empty(result.reusedAssemblyNames); } - private static int ReadVersion(Type type) - => (int)type.GetProperty("version")!.GetValue(Activator.CreateInstance(type))!; - + private static int ReadVersion(Type type) => (int)type.GetProperty("version")!.GetValue(Activator.CreateInstance(type))!; [MethodImpl(MethodImplOptions.NoInlining)] private static WeakReference CaptureActiveType( ScriptingFixture fixture, @@ -2086,11 +1934,7 @@ GameScene scene GameComponent scriptComponent = scriptOwner.AddComponent(scriptType); pluginType.GetProperty("retained")!.SetValue(pluginComponent, 47); scriptType.GetProperty("retained")!.SetValue(scriptComponent, 73); - return new AvailabilitySceneState( - pluginOwner, - scriptOwner, - pluginComponent.identity.persistentId, - scriptComponent.identity.persistentId); + return new AvailabilitySceneState(pluginOwner, scriptOwner, pluginComponent.identity.persistentId, scriptComponent.identity.persistentId); } private static void CompleteUnloadVerification(ScriptReloadHost reload) @@ -2118,22 +1962,17 @@ RuntimeSession session Assert.True(fixture.RefreshPlugins()); ScriptCompilationResult unavailable = fixture.CompilePendingPluginReload(reload); Assert.True(unavailable.success, FormatDiagnostics(unavailable)); - Assert.DoesNotContain(unavailable.moduleDeployments, static request => - request.domain == AssemblyDomain.InnoPlugin); - Assert.DoesNotContain(unavailable.moduleDeployments.SelectMany(static request => request.upstreamModuleNames), - static moduleName => string.Equals( - moduleName, - "Plugin.tests.unavailability", - StringComparison.Ordinal)); + Assert.DoesNotContain(unavailable.moduleDeployments, static request => request.domain == AssemblyDomain.InnoPlugin); + Assert.DoesNotContain( + unavailable.moduleDeployments.SelectMany(static request => request.upstreamModuleNames), + static moduleName => string.Equals(moduleName, "Plugin.tests.unavailability", StringComparison.Ordinal) + ); Assert.True(reload.ApplyPendingReload()); - Assert.DoesNotContain(fixture.host.modules.modules, static module => - module.domain == AssemblyDomain.InnoPlugin || - module.upstreamModuleNames.Contains("Plugin.tests.unavailability", StringComparer.Ordinal)); - return new MissingGenerationExpectation( - owner, - componentId, - new WeakReference(component), - new WeakReference(pluginType)); + Assert.DoesNotContain( + fixture.host.modules.modules, + static module => module.domain == AssemblyDomain.InnoPlugin || module.upstreamModuleNames.Contains("Plugin.tests.unavailability", StringComparer.Ordinal) + ); + return new MissingGenerationExpectation(owner, componentId, new WeakReference(component), new WeakReference(pluginType)); } private static void ForceFullCollection() @@ -2149,19 +1988,16 @@ private readonly record struct MissingGenerationExpectation( WeakReference retiredComponent, WeakReference retiredType ); - private readonly record struct RetiredScriptObject( WeakReference type, WeakReference component ); - private readonly record struct AvailabilitySceneState( GameObject pluginOwner, GameObject scriptOwner, Guid pluginComponentId, Guid scriptComponentId ); - private static void WriteProjectionPlugin( string projectRoot, SerializationRegistry serialization @@ -2179,23 +2015,18 @@ public sealed class ProjectionRuntime : GameBehavior projectRoot, "projection.iplugin", serialization, - new PluginManifest - { - pluginId = "tests.projection", - displayName = "Projection Plugin" - }, - new Dictionary - { + new PluginManifest { pluginId = "tests.projection", displayName = "Projection Plugin" }, + new Dictionary { ["Assets/ProjectionRuntime.cs"] = System.Text.Encoding.UTF8.GetBytes(c_source), ["Assets/ProjectionRuntime.cs.imeta"] = CreateScriptSourceMeta(serialization, Guid.NewGuid()) - }); + } + ); } private static void WriteUnavailableGenerationPlugin( string projectRoot, SerializationRegistry serialization - ) - => WriteUnavailableGenerationPlugin(projectRoot, serialization, """ + ) => WriteUnavailableGenerationPlugin(projectRoot, serialization, """ using InnoEngine.Reflection; using InnoEngine.Scene; using InnoEngine.Serialization; @@ -2209,7 +2040,6 @@ public sealed class PluginBehavior : GameBehavior public int retained { get; set; } = 5; } """); - private static void WriteUnavailableGenerationPlugin( string projectRoot, SerializationRegistry serialization, @@ -2219,28 +2049,19 @@ string source projectRoot, "unavailability.iplugin", serialization, - new PluginManifest - { - pluginId = "tests.unavailability", - displayName = "Unavailability Test Plugin" - }, - new Dictionary - { + new PluginManifest { pluginId = "tests.unavailability", displayName = "Unavailability Test Plugin" }, + new Dictionary { ["Assets/PluginBehavior.cs"] = System.Text.Encoding.UTF8.GetBytes(source), - ["Assets/PluginBehavior.cs.imeta"] = CreateScriptSourceMeta( - serialization, - Guid.Parse("37f9751f-a7de-43c7-94ac-c8e854762d49")) - }); + ["Assets/PluginBehavior.cs.imeta"] = CreateScriptSourceMeta(serialization, Guid.Parse("37f9751f-a7de-43c7-94ac-c8e854762d49")) + } + ); } private static void WriteSamplePlugin( string projectRoot, SerializationRegistry serialization ) { - const string c_source = - "using InnoEngine.Reflection; using InnoEngine.Scene; " + - "[StableTypeId(\"ce3b52c6-2a07-42ea-b632-a307a0ef7407\")] " + - "public sealed class StarterBehavior : GameBehavior { }"; + const string c_source = "using InnoEngine.Reflection; using InnoEngine.Scene; " + "[StableTypeId(\"ce3b52c6-2a07-42ea-b632-a307a0ef7407\")] " + "public sealed class StarterBehavior : GameBehavior { }"; WriteSamplePlugin(projectRoot, serialization, c_source); } @@ -2253,38 +2074,21 @@ string source projectRoot, "samples.iplugin", serialization, - new PluginManifest - { - pluginId = "tests.samples", - displayName = "Sample Test Plugin" - }, - new Dictionary - { + new PluginManifest { pluginId = "tests.samples", displayName = "Sample Test Plugin" }, + new Dictionary { ["Assets/"] = [], ["Assets/~Starter/"] = [], - ["Assets/~Starter.imeta"] = serialization.Serialize(new ScriptingAssetSourceMeta - { - persistentId = Guid.Parse("7726b1d2-9aee-4d2c-a865-2fd53155095f"), - sourceKind = (int)AssetSourceKind.Directory - }), + ["Assets/~Starter.imeta"] = serialization.Serialize(new ScriptingAssetSourceMeta { persistentId = Guid.Parse("7726b1d2-9aee-4d2c-a865-2fd53155095f"), sourceKind = (int)AssetSourceKind.Directory }), ["Assets/~Starter/StarterBehavior.cs"] = System.Text.Encoding.UTF8.GetBytes(source), - ["Assets/~Starter/StarterBehavior.cs.imeta"] = CreateScriptSourceMeta( - serialization, - Guid.Parse("75f6a70b-93b2-47f0-8747-cc359474b7a3")) - }); + ["Assets/~Starter/StarterBehavior.cs.imeta"] = CreateScriptSourceMeta(serialization, Guid.Parse("75f6a70b-93b2-47f0-8747-cc359474b7a3")) + } + ); } private static byte[] CreateScriptSourceMeta( SerializationRegistry serialization, Guid persistentId - ) - => serialization.Serialize(new ScriptingAssetSourceMeta - { - persistentId = persistentId, - sourceKind = (int)AssetSourceKind.File, - importerId = "inno.editor.csharp-script" - }); - + ) => serialization.Serialize(new ScriptingAssetSourceMeta { persistentId = persistentId, sourceKind = (int)AssetSourceKind.File, importerId = "inno.editor.csharp-script" }); private static void WritePluginPackage( string projectRoot, string fileName, @@ -2296,7 +2100,7 @@ IReadOnlyDictionary entries using FileStream stream = File.Create(path); using var archive = new ZipArchive(stream, ZipArchiveMode.Create); WritePluginPackageEntry(archive, "Plugin.inno", serialization.Serialize(manifest)); - foreach ((string entryPath, byte[] bytes) in entries) + foreach ((string entryPath, byte[] bytes)in entries) WritePluginPackageEntry(archive, entryPath, bytes); } @@ -2312,10 +2116,10 @@ byte[] bytes output.Write(bytes); } - private static string FormatDiagnostics(ScriptCompilationResult result) - => string.Join(Environment.NewLine, result.diagnostics.Select(static diagnostic => - $"{diagnostic.id}: {diagnostic.message}")); - + private static string FormatDiagnostics(ScriptCompilationResult result) => string.Join( + Environment.NewLine, + result.diagnostics.Select(static diagnostic => $"{diagnostic.id}: {diagnostic.message}") + ); private static bool ContainsCustomAttribute( string assemblyPath, string attributeTypeName @@ -2328,17 +2132,14 @@ string attributeTypeName CustomAttribute attribute = metadata.GetCustomAttribute(handle); string? typeName = attribute.Constructor.Kind switch { - HandleKind.MemberReference => GetMemberReferenceDeclaringTypeName( - metadata, - (MemberReferenceHandle)attribute.Constructor), - HandleKind.MethodDefinition => metadata.GetString(metadata.GetTypeDefinition( - metadata.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor) - .GetDeclaringType()).Name), + HandleKind.MemberReference => GetMemberReferenceDeclaringTypeName(metadata, (MemberReferenceHandle)attribute.Constructor), + HandleKind.MethodDefinition => metadata.GetString(metadata.GetTypeDefinition(metadata.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor).GetDeclaringType()).Name), _ => null }; if (string.Equals(typeName, attributeTypeName, StringComparison.Ordinal)) return true; } + return false; } @@ -2349,10 +2150,8 @@ MemberReferenceHandle constructorHandle MemberReference constructor = metadata.GetMemberReference(constructorHandle); return constructor.Parent.Kind switch { - HandleKind.TypeReference => metadata.GetString( - metadata.GetTypeReference((TypeReferenceHandle)constructor.Parent).Name), - HandleKind.TypeDefinition => metadata.GetString( - metadata.GetTypeDefinition((TypeDefinitionHandle)constructor.Parent).Name), + HandleKind.TypeReference => metadata.GetString(metadata.GetTypeReference((TypeReferenceHandle)constructor.Parent).Name), + HandleKind.TypeDefinition => metadata.GetString(metadata.GetTypeDefinition((TypeDefinitionHandle)constructor.Parent).Name), _ => null }; } @@ -2366,7 +2165,7 @@ private sealed class CompilationGate : IProgress, IDi /// /// Suspends reference preparation to verify that no Editor frame waits for the compiler. /// - /// + /// /// The real compiler progress callback. /// public void Report(ScriptCompilationProgress value) @@ -2412,7 +2211,6 @@ public IDisposable BeginPlayMode(RuntimeSession runtimeSession) private sealed class RestoreLease(PluginPlayScene owner) : IDisposable { private bool m_disposed; - public void Dispose() { if (m_disposed) @@ -2427,13 +2225,10 @@ private sealed class CountingHistoryIsolation : IEditorHistoryIsolation { internal int disposeCount { get; private set; } - public IDisposable BeginHistoryIsolation() - => new HistoryLease(this); - + public IDisposable BeginHistoryIsolation() => new HistoryLease(this); private sealed class HistoryLease(CountingHistoryIsolation owner) : IDisposable { private bool m_disposed; - public void Dispose() { if (m_disposed) @@ -2446,61 +2241,45 @@ public void Dispose() private sealed class ReadyScriptCompilation : IEditorScriptCompilation { - private readonly ReadyCompilationTicket m_ticket = new(); + public bool requiresExclusiveProgress => false; + public float progress => 0f; - public IScriptCompilationTicket RequestCompilation() - => m_ticket; + public void CancelCompilation() + { + } + private readonly ReadyCompilationTicket m_ticket = new(); + public IScriptCompilationTicket RequestCompilation() => m_ticket; public IScriptCompilationTicket? currentTicket => m_ticket; - public EditorScriptCompilationState state => EditorScriptCompilationState.Ready; - public string status => "The test script generation is ready."; - public ScriptCompilationResult? lastCompilation => null; private sealed class ReadyCompilationTicket : IScriptCompilationTicket { public long requestId => 1; - public ScriptCompilationTicketState state => ScriptCompilationTicketState.Succeeded; - public string status => "The test compilation ticket succeeded."; - public ScriptCompilationResult? result => null; - public bool isCompleted => true; } } private sealed class TestSceneReloadParticipant(SceneReloadService reload) : IEditorReloadParticipant { - public IGenerationChange Capture(AssemblyReloadContext context) - => new TestSceneReloadTransaction(reload.Capture( - context.GetContext())); - + public IGenerationChange Capture(AssemblyReloadContext context) => new TestSceneReloadTransaction(reload.Capture(context.GetContext())); public void RefreshDiagnostics() { } } - private sealed class TestSceneReloadTransaction(ISceneReloadStateTransfer transfer) - : IGenerationChange + private sealed class TestSceneReloadTransaction(ISceneReloadStateTransfer transfer) : IGenerationChange { - public void PrepareForActivation() - => transfer.PrepareForActivation(); - - public void Apply() - => transfer.Apply(); - - public void Complete() - => transfer.Complete(); - - public void RollbackStructure() - => transfer.RollbackStructure(); - - public void RestorePreviousState() - => transfer.RestorePreviousState(); + public void PrepareForActivation() => transfer.PrepareForActivation(); + public void Apply() => transfer.Apply(); + public void Complete() => transfer.Complete(); + public void RollbackStructure() => transfer.RollbackStructure(); + public void RestorePreviousState() => transfer.RestorePreviousState(); } } @@ -2513,16 +2292,12 @@ internal sealed class ScriptingFixture : IDisposable private readonly PluginEnvironment m_plugins; private RuntimeSession? m_editorSession; private bool m_disposed; - internal ScriptingFixture( Action? configureProject = null, bool enableFileSystemWatcher = false, int fileWatcherFlushDelayMs = 100 ) { - projectRoot = Path.Combine( - Path.GetTempPath(), - "InnoScriptingPipelineTests", - Guid.NewGuid().ToString("N")); + projectRoot = Path.Combine(Path.GetTempPath(), "InnoScriptingPipelineTests", Guid.NewGuid().ToString("N")); string assetRoot = Path.Combine(projectRoot, "Assets"); string pluginRoot = Path.Combine(projectRoot, "Plugins"); string libraryRoot = Path.Combine(projectRoot, "Library"); @@ -2530,10 +2305,11 @@ internal ScriptingFixture( Directory.CreateDirectory(pluginRoot); Directory.CreateDirectory(libraryRoot); m_identityScope = m_identities.EnterScope(); - host = new EngineHostBuilder() - .UseMetadataSources(new DotNetAssemblyCatalogSource(typeof(ScriptingPipelineTests).Assembly), - new ReflectionTypeCatalogSource(), new ReflectionSerializationMetadataSource()) - .Build(); + host = new EngineHostBuilder().UseMetadataSources( + new DotNetAssemblyCatalogSource(typeof(ScriptingPipelineTests).Assembly), + new ReflectionTypeCatalogSource(), + new ReflectionSerializationMetadataSource() + ).Build(); m_diagnosticScope = host.diagnostics.EnterScope(); configureProject?.Invoke(projectRoot, host.serialization); var pluginSources = new PluginSourceService(host.serialization, pluginRoot, libraryRoot); @@ -2546,48 +2322,29 @@ internal ScriptingFixture( m_identities, host.diagnostics, host.logs, - options with - { + options with { enableFileSystemWatcher = enableFileSystemWatcher, fileWatcherFlushDelayMs = fileWatcherFlushDelayMs, - sourceMounts = - [ - .. options.sourceMounts!, - .. PluginSourceService.GetActivatableMounts(scan) - ] - }); + sourceMounts = [..options.sourceMounts!, ..PluginSourceService.GetActivatableMounts(scan)] + } + ); m_settings = new ProjectSettingsStore( new FileByteDocumentStore(Path.GetFullPath(Path.Combine(projectRoot, "Settings.Project.inno"))), host.types, host.serialization, new ProjectId("tests.scripting"), - AssetSerializationContext.Create(assets)); - m_plugins = new PluginEnvironment( - assets, - m_settings, - host.serialization, - pluginRoot, - libraryRoot, - scan, - host.modules.generations); - compiler = new ScriptCompiler( - new ScriptCompilerOptions { projectRootDirectory = projectRoot }, - assets, - m_plugins); + AssetSerializationContext.Create(assets) + ); + m_plugins = new PluginEnvironment(assets, m_settings, host.serialization, pluginRoot, libraryRoot, scan, host.modules.generations); + compiler = new ScriptCompiler(new ScriptCompilerOptions { projectRootDirectory = projectRoot }, assets, m_plugins); } internal string projectRoot { get; } - internal EngineHost host { get; } - internal AssetPipeline assets { get; } - internal ScriptCompiler compiler { get; } - internal IReadOnlyList activePlugins => m_plugins.activePlugins; - - internal RuntimeSession editorSession - => m_editorSession ?? throw new InvalidOperationException("The Editor runtime session has not been created."); + internal RuntimeSession editorSession => m_editorSession ?? throw new InvalidOperationException("The Editor runtime session has not been created."); internal void Write( string relativePath, @@ -2611,8 +2368,7 @@ string destinationRelativePath File.Move(sourceMetadata, destination + ".imeta"); } - internal void WriteVersionedBehavior(int version) - => Write("VersionedBehavior.cs", $$""" + internal void WriteVersionedBehavior(int version) => Write("VersionedBehavior.cs", $$""" using InnoEngine.Scene; public sealed class VersionedBehavior : GameBehavior @@ -2620,9 +2376,7 @@ public sealed class VersionedBehavior : GameBehavior public int version => {{version}}; } """); - internal void Rescan() => assets.Rescan(); - internal ScriptCompilationResult Compile( IProgress? progress = null, CancellationToken cancellationToken = default @@ -2637,89 +2391,66 @@ internal ScriptCompilationResult CompileRuntimeDeployment( CancellationToken cancellationToken = default ) { Rescan(); - return compiler.CompileRuntimeDeploymentAsync( - targetRuntimeDirectory ?? AppContext.BaseDirectory, - progress, - cancellationToken) - .GetAwaiter() - .GetResult(); + return compiler.CompileRuntimeDeploymentAsync(targetRuntimeDirectory ?? AppContext.BaseDirectory, progress, cancellationToken).GetAwaiter().GetResult(); } internal string CreateDeploymentRuntime() { string directory = Path.Combine(projectRoot, "TargetRuntime"); Directory.CreateDirectory(directory); - foreach (string source in Directory.EnumerateFiles( - AppContext.BaseDirectory, - "Inno.*.dll", - SearchOption.TopDirectoryOnly)) + foreach (string source in Directory.EnumerateFiles(AppContext.BaseDirectory, "Inno.*.dll", SearchOption.TopDirectoryOnly)) { File.Copy(source, Path.Combine(directory, Path.GetFileName(source)), overwrite: true); } + return directory; } - private IModuleSource CreateScriptModuleSource(ScriptModuleDeployment deployment) - => new DotNetModuleSource - { - artifactRootDirectory = Path.Combine(projectRoot, "Library", "Assemblies"), - moduleName = deployment.moduleName, - mainAssemblyPath = deployment.mainAssemblyPath, - domain = deployment.domain, - scope = deployment.scope, - preloadAssemblyPaths = deployment.preloadAssemblyPaths, - upstreamModuleNames = deployment.upstreamModuleNames, - assemblyScopes = deployment.assemblyScopes, - collectible = true - }; - + private IModuleSource CreateScriptModuleSource(ScriptModuleDeployment deployment) => new DotNetModuleSource + { + artifactRootDirectory = Path.Combine(projectRoot, "Library", "Assemblies"), + moduleName = deployment.moduleName, + mainAssemblyPath = deployment.mainAssemblyPath, + domain = deployment.domain, + scope = deployment.scope, + preloadAssemblyPaths = deployment.preloadAssemblyPaths, + upstreamModuleNames = deployment.upstreamModuleNames, + assemblyScopes = deployment.assemblyScopes, + collectible = true + }; internal ScriptReloadHost CreateReloadHost( EditorReloadCoordinator? reloads = null, bool autoCompile = false - ) - => new( - new ScriptReloadOptions - { - autoCompile = autoCompile, - debounceMilliseconds = 0, - compilationWarningTimeout = Timeout.InfiniteTimeSpan - }, - compiler, - assets, - m_plugins, - host.modules, - m_settings, - reloads ?? new EditorReloadCoordinator(), - CreateScriptModuleSource); - + ) => new( + new ScriptReloadOptions { autoCompile = autoCompile, debounceMilliseconds = 0, compilationWarningTimeout = Timeout.InfiniteTimeSpan }, + compiler, + assets, + m_plugins, + host.modules, + m_settings, + reloads ?? new EditorReloadCoordinator(), + CreateScriptModuleSource + ); internal ScriptCompilationResult CompilePending(ScriptReloadHost reload) { Rescan(); reload.RecompileScripting(); Assert.True(reload.TryCompilePending(out Task? compilation)); - return Assert.IsAssignableFrom>(compilation) - .GetAwaiter() - .GetResult(); + return Assert.IsAssignableFrom>(compilation).GetAwaiter().GetResult(); } internal ScriptCompilationResult CompilePendingPluginReload(ScriptReloadHost reload) { reload.ReloadPlugins(); Assert.True(reload.TryCompilePending(out Task? compilation)); - return Assert.IsAssignableFrom>(compilation) - .GetAwaiter() - .GetResult(); + return Assert.IsAssignableFrom>(compilation).GetAwaiter().GetResult(); } - internal bool RefreshPlugins() - => m_plugins.Refresh(); - + internal bool RefreshPlugins() => m_plugins.Refresh(); internal Type ResolveActiveType(string name) { TypeCacheSnapshot snapshot = host.types.current; - return snapshot.types - .Select(typeRef => typeRef.Resolve(snapshot)) - .Single(type => string.Equals(type.Name, name, StringComparison.Ordinal)); + return snapshot.types.Select(typeRef => typeRef.Resolve(snapshot)).Single(type => string.Equals(type.Name, name, StringComparison.Ordinal)); } internal EditorInteractionRuntime CreateEditorRuntime(EditorReloadCoordinator? reloads = null) @@ -2735,6 +2466,8 @@ internal EditorInteractionRuntime CreateEditorRuntime(EditorReloadCoordinator? r [ host.types, host.serialization, + m_identities, + editorSession.identities, host.modules, assets, m_plugins, @@ -2743,18 +2476,13 @@ internal EditorInteractionRuntime CreateEditorRuntime(EditorReloadCoordinator? r new Func(CreateScriptModuleSource), editorSession, reloads - ]); + ] + ); } internal RuntimeSession CreateEditorSession() { - m_editorSession ??= host.CreateSession(new RuntimeSessionOptions - { - kind = RuntimeSessionKind.Edit, - applicationId = "tests.editor", - createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(projectRoot, "Persistent", "tests.editor"), "Logs")), - referenceResolvers = [assets] - }); + m_editorSession ??= host.CreateSession(new RuntimeSessionOptions { kind = RuntimeSessionKind.Edit, applicationId = "tests.editor", createLogSink = _ => new FileLogSink(Path.Combine(Path.Combine(projectRoot, "Persistent", "tests.editor"), "Logs")), referenceResolvers = [assets] }); return m_editorSession; } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs index e1db0e757..5c822cb17 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs @@ -1,3 +1,4 @@ +using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System.Runtime.CompilerServices; using NativeVector2 = Inno.Native.ImGui.ImVec2C; using BGCS.Runtime; @@ -24,8 +25,10 @@ using Inno.Editor.Core; using Inno.Editor.Graph; using Inno.Editor.Interactions; -using Inno.Editor.Panel.FileBrowser; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; using Inno.Editor.Shaders; +using Inno.Editor.Shaders.ImGui; using Inno.Editor.Rendering; using Inno.Extensibility.Modules; using Inno.Extensibility.Reload; @@ -39,9 +42,9 @@ using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Scripting.Tests; - public sealed class ShaderEditorWorkflowTests : IDisposable { + private readonly EditorUI m_ui = new(); private readonly string m_root = Path.Combine(Path.GetTempPath(), "InnoShaderEditorWorkflow", Guid.NewGuid().ToString("N")); private readonly IdentityAllocator m_identities = new(); private readonly IDisposable m_identityScope; @@ -62,7 +65,6 @@ public sealed class ShaderEditorWorkflowTests : IDisposable private Vector2 m_canvasMinimum; private Vector2 m_canvasMaximum; private Vector2 m_probeOffset; - public unsafe ShaderEditorWorkflowTests() { Directory.CreateDirectory(Path.Combine(m_root, "Assets")); @@ -71,14 +73,21 @@ public unsafe ShaderEditorWorkflowTests() _ = typeof(ShaderNodeDrawer); _ = typeof(ShaderGraphSourceStore); _ = typeof(GraphEditorModule); - m_modules = new(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(ShaderEditorWorkflowTests).Assembly)}); + m_modules = new(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(ShaderEditorWorkflowTests).Assembly) }); m_types = new(m_modules, new ReflectionTypeCatalogSource()); m_serialization = new(m_types, new ReflectionSerializationMetadataSource()); m_diagnostics = new DiagnosticHub(); m_diagnosticScope = m_diagnostics.EnterScope(); m_reporter = m_diagnostics.CreateReporter(new("tests.shader-editor", "Shader Editor workflow")); - m_assets = new(m_modules, m_types, m_serialization, m_identities, m_diagnostics, m_logs, - AssetPipelineOptions.Create(Path.Combine(m_root, "Assets"), Path.Combine(m_root, "Library")) with { enableFileSystemWatcher = false }); + m_assets = new( + m_modules, + m_types, + m_serialization, + m_identities, + m_diagnostics, + m_logs, + AssetPipelineOptions.Create(Path.Combine(m_root, "Assets"), Path.Combine(m_root, "Library"))with { enableFileSystemWatcher = false } + ); m_source = new(m_assets, m_serialization); m_artifacts = new(m_assets, m_serialization, m_types, new ShaderCompiler(new WorkflowCompiler()), new BgfxTextureTargetCompiler(), m_reporter); m_runtime = CreateRuntime(); @@ -100,10 +109,13 @@ public void ParameterPresentationRoundTripsWithoutChangingTheRuntimeDefinitionOr string beforeHash = ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(graph, new Dictionary(), m_serialization), m_serialization); ShaderPropertyId id = new("stable.tint"); Assert.True(ShaderParameterPresentation.Read(graph, id, m_serialization, context).visible); - ShaderParameterPresentation.Write(graph, id, new() - { - group = "Surface", description = "Linear tint", visible = false, hasRange = true, minimum = -2, maximum = 8 - }, m_serialization, context); + ShaderParameterPresentation.Write( + graph, + id, + new() { group = "Surface", description = "Linear tint", visible = false, hasRange = true, minimum = -2, maximum = 8 }, + m_serialization, + context + ); GraphDocument restored = GraphDocumentCodec.Decode(GraphDocumentCodec.Encode(graph, m_serialization), m_serialization); ShaderParameterPresentation presentation = ShaderParameterPresentation.Read(restored, id, m_serialization, context); Assert.Equal("Surface", presentation.group); @@ -115,7 +127,10 @@ public void ParameterPresentationRoundTripsWithoutChangingTheRuntimeDefinitionOr presentation.group = "Detached"; Assert.Equal("Surface", ShaderParameterPresentation.Read(restored, id, m_serialization, context).group); Assert.Equal(beforeDefinition, m_serialization.Serialize(ShaderGraphDocument.ReadDefinition(restored, m_serialization, context), context)); - Assert.Equal(beforeHash, ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(restored, new Dictionary(), m_serialization), m_serialization)); + Assert.Equal( + beforeHash, + ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(restored, new Dictionary(), m_serialization), m_serialization) + ); } [Fact] @@ -124,9 +139,17 @@ public void ParameterPresentationRejectsInvalidBoundsBeforeMutatingTheGraph() GraphDocument graph = m_source.Read(Create("Range.ishader").assetPath).document; byte[] before = GraphDocumentCodec.Encode(graph, m_serialization); SerializationContext context = AssetSerializationContext.Create(m_assets); - foreach (var range in new[] { (double.NaN, 1d), (0d, double.PositiveInfinity), (2d, 1d), (-1e100, 0d), (0d, 1e100) }) - Assert.Throws(() => ShaderParameterPresentation.Write(graph, new("range"), - new() { hasRange = true, minimum = range.Item1, maximum = range.Item2 }, m_serialization, context)); + foreach (var range in new[] + { + (double.NaN, 1d), + (0d, double.PositiveInfinity), + (2d, 1d), + (-1e100, 0d), + (0d, 1e100) + } + + ) + Assert.Throws(() => ShaderParameterPresentation.Write(graph, new("range"), new() { hasRange = true, minimum = range.Item1, maximum = range.Item2 }, m_serialization, context)); Assert.Equal(before, GraphDocumentCodec.Encode(graph, m_serialization)); } @@ -144,16 +167,26 @@ public unsafe void DiagnosticCheckUsesTheStandardTransientModalWithoutChangingFi Assert.False(presentation.canResize); for (int frame = 0; frame < 120 && presentation.isVisible; frame++) { + using IDisposable uiScope = m_ui.Enter(); ImGuiApi.NewFrame(); ImGuiApi.SetNextWindowPos(new(0, 0)); ImGuiApi.SetNextWindowSize(new(1200, 800)); using NativeStringLease nativeTestText0 = Utf8Codec.EncodeNullTerminated("Shader Check Workflow", allowsNull: false); _ = ImGuiApi.Raw.Begin((sbyte*)nativeTestText0.pointer, null, 0); - try { Assert.True(modal.Draw(m_runtime.context)); } - finally { ImGuiApi.End(); ImGuiApi.Render(); } + try + { + Assert.True(modal.Draw(m_runtime.context)); + } + finally + { + ImGuiApi.End(); + ImGuiApi.Render(); + } + Thread.Sleep(5); Assert.True(modal.TryGetPresentation(out presentation)); } + Assert.False(presentation.isVisible); Assert.Equal(before, File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath))); Assert.Single(m_runtime.interactions.documents.documents); @@ -196,12 +229,20 @@ public void SourceSettingsUndoRefusesToOverwriteAnExternalSidecarEdit() private AssetPath CreateFunction(params string[] exports) { - if (exports.Length == 0) exports = ["Evaluate"]; + if (exports.Length == 0) + exports = ["Evaluate"]; AssetPath path = AssetPath.Project("Function.ishadersource"); - File.WriteAllText(Path.Combine(m_root, "Assets", path.localPath), string.Join("\n", - exports.Select(static function => $"float {function}(float input) {{ return input; }}"))); + File.WriteAllText( + Path.Combine(m_root, "Assets", path.localPath), + string.Join("\n", exports.Select(static function => $"float {function}(float input) {{ return input; }}")) + ); _ = m_assets.Import(path); - var settings = new ShaderSourceImportSettings { languageId = "inno.shader-language.bgfx-sc", implementationId = "bgfx", exports = exports }; + var settings = new ShaderSourceImportSettings + { + languageId = "inno.shader-language.bgfx-sc", + implementationId = "bgfx", + exports = exports + }; Assert.True(m_assets.SaveImportSettings(path, settings, m_assets.GetImportSettings(path).fingerprint)); return path; } @@ -240,15 +281,14 @@ bool emptyGraph AssetFileEntry entry = Create("Resize.ishader"); SelectAndDraw(entry); GraphDocumentController controller = Controller(entry); - if (emptyGraph) controller.ReplaceDocument(new GraphDocument(), "Prepare Empty Canvas"); + if (emptyGraph) + controller.ReplaceDocument(new GraphDocument(), "Prepare Empty Canvas"); ulong revision = controller.revision; byte[] disk = File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath)); ImGuiApi.Raw.GetStyle()->ItemSpacing = Unsafe.BitCast(new Vector2(6, 4) * scale); ImGuiApi.Raw.GetStyle()->WindowPadding = Unsafe.BitCast(new Vector2(8, 7) * scale); ImGuiApi.Raw.GetIO()->DisplayFramebufferScale = Unsafe.BitCast(new Vector2(scale)); - - Vector2[] sizes = [new(1200, 800), new(640, 400), new(280, 220), new(96, 64), new(32, 32), - new(1200, 32), new(32, 800), new(1200, 800)]; + Vector2[] sizes = [new(1200, 800), new(640, 400), new(280, 220), new(96, 64), new(32, 32), new(1200, 32), new(32, 800), new(1200, 800)]; for (int cycle = 0; cycle < 3; cycle++) foreach (Vector2 size in sizes) { @@ -320,7 +360,10 @@ public void NativeMouseGesturesMoveNodesAndEachReleaseHasItsOwnUndo() SelectAndDraw(entry); GraphDocumentController controller = Controller(entry); var graph = new GraphDocument(); - var node = new GraphNodeRecord(new("probe"), "tests.shader-ui-probe") { position = new(0, 0) }; + var node = new GraphNodeRecord(new("probe"), "tests.shader-ui-probe") + { + position = new(0, 0) + }; graph.AddNode(node); controller.ReplaceDocument(graph, "Prepare Canvas Probe"); Assert.True(m_runtime.interactions.For("panel/rendering.shader-editor", entry).Execute("shader/focus")); @@ -353,15 +396,20 @@ public void FormatPlacesOutputsOnTheRightWithoutChangingShaderSemanticsAndIsUndo node.position = node.definitionId == ShaderGraphDocument.outputDefinitionId ? new(0, node.position.y) : new(900, node.position.y); controller.ReplaceDocument(reversed, "Reverse Shader Layout"); byte[] before = GraphDocumentCodec.Encode(controller.document, m_serialization); - string semantic = ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(controller.document, - new Dictionary(), m_serialization), m_serialization); + string semantic = ShaderGraphArtifact.GetSemanticHash( + ShaderGraphArtifact.Encode(controller.document, new Dictionary(), m_serialization), + m_serialization + ); Assert.True(m_runtime.interactions.For("panel/rendering.shader-editor", entry).Execute("shader/format")); GraphNodeRecord[] outputs = controller.document.nodes.Where(static node => node.definitionId == ShaderGraphDocument.outputDefinitionId).ToArray(); - Assert.All(outputs, output => Assert.True(output.position.x > controller.document.nodes - .Where(node => node.id != output.id && ShaderGraphDocument.Read(node, "stage", "", m_serialization, - AssetSerializationContext.Create(m_assets)) == output.id.value).Max(static node => node.position.x))); - Assert.Equal(semantic, ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(controller.document, - new Dictionary(), m_serialization), m_serialization)); + Assert.All( + outputs, + output => Assert.True(output.position.x > controller.document.nodes.Where(node => node.id != output.id && ShaderGraphDocument.Read(node, "stage", "", m_serialization, AssetSerializationContext.Create(m_assets)) == output.id.value).Max(static node => node.position.x)) + ); + Assert.Equal( + semantic, + ShaderGraphArtifact.GetSemanticHash(ShaderGraphArtifact.Encode(controller.document, new Dictionary(), m_serialization), m_serialization) + ); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(before, GraphDocumentCodec.Encode(controller.document, m_serialization)); } @@ -403,8 +451,10 @@ public void ExternalSourceConflictKeepsBothDiskAndUnsavedRecovery() private AssetFileEntry Create(string name) { - File.WriteAllBytes(Path.Combine(m_root, "Assets", name), GraphDocumentCodec.Encode( - ShaderGraphTemplates.CreateRaster(m_serialization, AssetSerializationContext.Create(m_assets)), m_serialization)); + File.WriteAllBytes( + Path.Combine(m_root, "Assets", name), + GraphDocumentCodec.Encode(ShaderGraphTemplates.CreateRaster(m_serialization, AssetSerializationContext.Create(m_assets)), m_serialization) + ); Assert.True(m_assets.Import(AssetPath.Project(name))); Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project(name), out AssetFileEntry entry)); return entry; @@ -426,7 +476,13 @@ public void DeleteKeysRemoveTheSelectedComputeStageAndItsDeclarationsAsOneUndo(K input.SetValue("stage", ShaderGraphDocument.Encode(output.id.value, m_serialization, AssetSerializationContext.Create(m_assets))); GraphDocument graph = controller.document.Clone(); graph.AddNode(input); - graph = ShaderGraphBindings.ChangeInput(graph, input.id, new() { id = "computeParameter", kind = ShaderIrInputKind.Uniform }, m_serialization, AssetSerializationContext.Create(m_assets)); + graph = ShaderGraphBindings.ChangeInput( + graph, + input.id, + new() { id = "computeParameter", kind = ShaderIrInputKind.Uniform }, + m_serialization, + AssetSerializationContext.Create(m_assets) + ); controller.ReplaceDocument(graph, "Add Compute Parameter"); byte[] before = GraphDocumentCodec.Encode(controller.document, m_serialization); Assert.True(interaction.Query("shader/delete").isEnabled); @@ -438,14 +494,19 @@ public void DeleteKeysRemoveTheSelectedComputeStageAndItsDeclarationsAsOneUndo(K var keyEvent = new KeyPressedEvent(0, key); m_runtime.HandleKeyPressed(keyEvent); m_logs.Flush(); - Assert.True(controller.document.FindNode(output.id) is null, "Focus: " + m_runtime.interactions.focusedArea + "; cut: " + interaction.Query("shader/cut").isEnabled + "; undo: " + m_runtime.interactions.history.undoName + "; " + string.Join("\n", m_messages.messages)); + Assert.True( + controller.document.FindNode(output.id)is null, + "Focus: " + m_runtime.interactions.focusedArea + "; cut: " + interaction.Query("shader/cut").isEnabled + "; undo: " + m_runtime.interactions.history.undoName + "; " + string.Join( + "\n", + m_messages.messages + ) + ); Assert.Null(controller.document.FindNode(input.id)); ShaderDefinition definition = ShaderGraphDocument.ReadDefinition(controller.document, m_serialization, AssetSerializationContext.Create(m_assets)); Assert.Single(definition.passes); Assert.DoesNotContain(definition.properties, property => property.id.value == "computeParameter"); using var nodes = new ShaderNodeCompilerRegistry(m_types); - Assert.True(new ShaderGraphProgramCompiler(nodes).Lower(controller.document, "bgfx", new Dictionary(), - m_serialization, AssetSerializationContext.Create(m_assets)).succeeded); + Assert.True(new ShaderGraphProgramCompiler(nodes).Lower(controller.document, "bgfx", new Dictionary(), m_serialization, AssetSerializationContext.Create(m_assets)).succeeded); Tick(); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Equal(before, GraphDocumentCodec.Encode(controller.document, m_serialization)); @@ -462,22 +523,34 @@ public void CutAndPasteStageKeepsItsContentsAndDoesNotCreateDuplicatePassDeclara Assert.True(interaction.Execute("shader/create-output", "Compute")); GraphDocumentController controller = Controller(entry); GraphNodeId stage = controller.document.nodes.Last().id; - GraphNodeId child = controller.AddNode("inno.shader.constant", new(10, 20), new Dictionary - { ["stage"] = ShaderGraphDocument.Encode(stage.value, m_serialization, AssetSerializationContext.Create(m_assets)) }); + GraphNodeId child = controller.AddNode( + "inno.shader.constant", + new(10, 20), + new Dictionary { ["stage"] = ShaderGraphDocument.Encode(stage.value, m_serialization, AssetSerializationContext.Create(m_assets)) } + ); Assert.True(interaction.Execute("shader/cut")); Assert.Null(controller.document.FindNode(child)); Assert.True(interaction.Execute("shader/paste")); Assert.Null(controller.document.FindNode(child)); Assert.Equal(2, controller.document.nodes.Count(node => node.definitionId == "inno.shader.stage-output" && ReadStage(node).stage != ShaderStage.Compute)); GraphNodeRecord pasted = controller.document.nodes.Single(node => node.definitionId == "inno.shader.stage-output" && ReadStage(node).stage == ShaderStage.Compute); - Assert.Contains(controller.document.nodes, node => ShaderGraphDocument.Read(node, "stage", "", m_serialization, AssetSerializationContext.Create(m_assets)) == pasted.id.value); + Assert.Contains( + controller.document.nodes, + node => ShaderGraphDocument.Read(node, "stage", "", m_serialization, AssetSerializationContext.Create(m_assets)) == pasted.id.value + ); Assert.False(interaction.Execute("shader/duplicate")); ShaderDefinition definition = ShaderGraphDocument.ReadDefinition(controller.document, m_serialization, AssetSerializationContext.Create(m_assets)); Assert.Equal(2, definition.passes.Length); Assert.Equal(2, definition.passes.Select(pass => pass.name).Distinct().Count()); Assert.True(m_runtime.interactions.history.Undo().succeeded); Assert.Single(ShaderGraphDocument.ReadDefinition(controller.document, m_serialization, AssetSerializationContext.Create(m_assets)).passes); - ShaderGraphStageSettings ReadStage(GraphNodeRecord node) => ShaderGraphDocument.Read(node, "settings", new ShaderGraphStageSettings(), m_serialization, AssetSerializationContext.Create(m_assets)); + ShaderGraphStageSettings ReadStage(GraphNodeRecord node) => ShaderGraphDocument.Read( + node, + "settings", + new ShaderGraphStageSettings(), + m_serialization, + AssetSerializationContext.Create(m_assets) + ); } [Fact] @@ -531,9 +604,7 @@ public void SourceLibrariesOwnTheirNestedCreationCatalogWithoutLeakingSeparators Assert.True(m_assets.SaveImportSettings(source, settings, snapshot.fingerprint)); AssetFileEntry entry = Create("Catalog.ishader"); SelectAndDraw(entry); - - EditorMenuItem create = m_runtime.interactions.For("panel/rendering.shader-editor", entry) - .BuildMenu().items.Single(item => item.label == "Create"); + EditorMenuItem create = m_runtime.interactions.For("panel/rendering.shader-editor", entry).BuildMenu().items.Single(item => item.label == "Create"); EditorMenuItem functions = create.children.Single(item => item.label == "Functions"); EditorMenuItem rendering2D = functions.children.Single(item => item.label == "Rendering 2D"); Assert.True(rendering2D.separatorBefore); @@ -549,8 +620,7 @@ public void PluginNodePresentationOwnsItsCreationGroupAndSeparator() { AssetFileEntry entry = Create("NodeCatalog.ishader"); SelectAndDraw(entry); - EditorMenuItem create = m_runtime.interactions.For("panel/rendering.shader-editor", entry) - .BuildMenu().items.Single(item => item.label == "Create"); + EditorMenuItem create = m_runtime.interactions.For("panel/rendering.shader-editor", entry).BuildMenu().items.Single(item => item.label == "Create"); EditorMenuItem domain = create.children.Single(item => item.label == "Domain"); EditorMenuItem extension = domain.children.Single(item => item.label == "Test Extension"); Assert.True(extension.separatorBefore); @@ -566,13 +636,38 @@ public void FormatOrdersSourcesByDestinationPortsToAvoidInputWireCrossings() AssetFileEntry entry = Create("Crossings.ishader"); SelectAndDraw(entry); var graph = new GraphDocument(); - var top = new GraphNodeRecord(new("top"), "inno.shader.constant") { position = new(800, 600) }; - var middle = new GraphNodeRecord(new("middle"), "inno.shader.constant") { position = new(800, 400) }; - var bottom = new GraphNodeRecord(new("bottom"), "inno.shader.constant") { position = new(800, 200) }; - var evaluate = new GraphNodeRecord(new("evaluate"), "inno.shader.construct") { position = new(400, 300) }; + var top = new GraphNodeRecord(new("top"), "inno.shader.constant") + { + position = new(800, 600) + }; + var middle = new GraphNodeRecord(new("middle"), "inno.shader.constant") + { + position = new(800, 400) + }; + var bottom = new GraphNodeRecord(new("bottom"), "inno.shader.constant") + { + position = new(800, 200) + }; + var evaluate = new GraphNodeRecord(new("evaluate"), "inno.shader.construct") + { + position = new(400, 300) + }; evaluate.SetValue("type", ShaderGraphDocument.Encode("float3", m_serialization, SerializationContext.empty)); - var output = new GraphNodeRecord(new("output"), "tests-output") { position = new(0, 300) }; - foreach (GraphNodeRecord node in new[] { top, middle, bottom, evaluate, output }) graph.AddNode(node); + var output = new GraphNodeRecord(new("output"), "tests-output") + { + position = new(0, 300) + }; + foreach (GraphNodeRecord node in new[] + { + top, + middle, + bottom, + evaluate, + output + } + + ) + graph.AddNode(node); graph.AddEdge(new(new("top-edge"), new(top.id, new("value")), new(evaluate.id, new("component.0")))); graph.AddEdge(new(new("middle-edge"), new(middle.id, new("value")), new(evaluate.id, new("component.1")))); graph.AddEdge(new(new("bottom-edge"), new(bottom.id, new("value")), new(evaluate.id, new("component.2")))); @@ -598,7 +693,14 @@ public void SavingAppliesTheGraphWithoutDependingOnFileWatcherAndUndoStaysDraftO GraphDocument graph = controller.document.Clone(); ShaderDefinition definition = ShaderGraphDocument.ReadDefinition(graph, m_serialization, AssetSerializationContext.Create(m_assets)); definition.name = "Saved Change"; - graph.SetMetadata(ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(m_serialization.Serialize(definition, AssetSerializationContext.Create(m_assets)), m_serialization, AssetSerializationContext.Create(m_assets))); + graph.SetMetadata( + ShaderGraphDocument.definitionKey, + ShaderGraphDocument.Encode( + m_serialization.Serialize(definition, AssetSerializationContext.Create(m_assets)), + m_serialization, + AssetSerializationContext.Create(m_assets) + ) + ); controller.ReplaceDocument(graph, "Rename Shader"); Tick(); Assert.Equal(before, asset.contentVersion); @@ -647,18 +749,19 @@ public void RestartRecoversOnlyTheLatestDraftAndNeverAppliesIt(bool undo) byte[] saved = File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath)); GraphNodeId node = Controller(entry).AddNode("tests.unsaved", new(0, 0)); Tick(); - if (undo) Assert.True(m_runtime.interactions.history.Undo().succeeded); + if (undo) + Assert.True(m_runtime.interactions.history.Undo().succeeded); // Shutdown must capture even the final undo that has not had an update frame yet. m_runtime.Dispose(); Assert.Equal(saved, File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath))); m_runtime = CreateRuntime(); SelectAndDraw(entry); - Assert.Equal(!undo, Controller(entry).document.FindNode(node) is not null); + Assert.Equal(!undo, Controller(entry).document.FindNode(node)is not null); Assert.Equal(saved, File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath))); } [Fact] - public async Task DraftCompilationNeverReplacesTheCanonicalArtifactAndLayoutChangesDoNotRecompile() + public void DraftCompilationNeverReplacesTheCanonicalArtifactAndLayoutChangesDoNotRecompile() { AssetFileEntry entry = Create("Preview.ishader"); ShaderAsset shader = m_assets.Load(entry.assetPath); @@ -668,7 +771,11 @@ public async Task DraftCompilationNeverReplacesTheCanonicalArtifactAndLayoutChan var compilation = new EditorShaderCompilation(m_artifacts, capabilities); RenderShaderArtifact? canonical = null; for (int i = 0; i < 100 && canonical is null; i++) - { _ = m_artifacts.GetShaderArtifact(shader, RenderShaderVariant.empty, capabilities, out canonical); await Task.Yield(); } + { + _ = m_artifacts.GetShaderArtifact(shader, RenderShaderVariant.empty, capabilities, out canonical); + Thread.Sleep(1); + } + Assert.NotNull(canonical); GraphDocument draft = m_source.Read(entry.assetPath).document; var context = AssetSerializationContext.Create(m_assets); @@ -678,22 +785,27 @@ public async Task DraftCompilationNeverReplacesTheCanonicalArtifactAndLayoutChan definition.properties[0] = property; draft.SetMetadata(ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(m_serialization.Serialize(definition, context), m_serialization, context)); Guid previewId = Guid.NewGuid(); - async Task Poll(ulong revision) + EditorShaderDraftCompilationSnapshot Poll(ulong revision) { EditorShaderDraftCompilationSnapshot result = compilation.RequestDraft(previewId, draft, revision, RenderShaderVariant.empty); for (int i = 0; i < 100 && result.state == EditorShaderCompilationState.Compiling; i++) - { await Task.Yield(); result = compilation.RequestDraft(previewId, draft, revision, RenderShaderVariant.empty); } + { + Thread.Sleep(1); + result = compilation.RequestDraft(previewId, draft, revision, RenderShaderVariant.empty); + } + return result; } - var preview = await Poll(1); + + var preview = Poll(1); Assert.Equal(EditorShaderCompilationState.Succeeded, preview.state); Assert.NotNull(preview.artifact); Assert.NotEqual(canonical!.contentHash, preview.artifact!.contentHash); draft.nodes[0].position = new(123, 456); - Assert.Same(preview.artifact, (await Poll(2)).artifact); + Assert.Same(preview.artifact, Poll(2).artifact); GraphNodeRecord output = draft.nodes.First(value => value.definitionId == ShaderGraphDocument.outputDefinitionId); draft.RemoveNode(output.id); - var failed = await Poll(3); + var failed = Poll(3); Assert.Equal(EditorShaderCompilationState.Failed, failed.state); Assert.True(failed.usingLastGood); Assert.Same(preview.artifact, failed.artifact); @@ -707,8 +819,12 @@ private EditorInteractionRuntime CreateRuntime() { RenderTextureFormat[] formats = Enum.GetValues(); var capabilities = new GraphicsCapabilities(GraphicsApi.Metal, GraphicsCapability.None, new(256, 8, 8192, 16), formats, formats, formats, formats, false, false); - var runtime = new EditorInteractionRuntime(new EditorContext(m_root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), m_types, m_logs, - [m_types, m_serialization, m_assets, m_sink, new EditorReloadCoordinator(), new EditorShaderCompilation(m_artifacts, capabilities), new EmptyPreviews()]); + var runtime = new EditorInteractionRuntime( + new EditorContext(m_root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), + m_types, + m_logs, + [m_types, m_serialization, m_identities, m_assets, m_sink, m_ui, new EditorReloadCoordinator(), new EditorShaderCompilation(m_artifacts, capabilities), new EmptyPreviews()] + ); runtime.Start(); return runtime; } @@ -723,14 +839,27 @@ public void MaterialGesturesAreIndependentAndOnlySavePublishesTheDraft() MaterialAsset canonical = m_assets.Load(path); void Set( float value, - bool finish = true + string? mergeKey = null ) { MaterialAsset candidate = documents.Read(id); candidate.Set(property, MaterialValue.FromColor(new Inno.Core.Mathematics.Color(value, 1, 1, 1))); - documents.Replace(id, candidate, finish); + documents.Replace(id, candidate, mergeKey); } - Set(0.1f, false); Set(0.2f, false); documents.Commit(id); - Set(0.3f, false); Set(0.4f, true); + + using (var gesture = new EditorEditGesture(m_runtime.interactions.history, "First Color")) + { + Set(0.1f, gesture.mergeKey); + Set(0.2f, gesture.mergeKey); + gesture.Commit(); + } + + using (var gesture = new EditorEditGesture(m_runtime.interactions.history, "Second Color")) + { + Set(0.3f, gesture.mergeKey); + Set(0.4f, gesture.mergeKey); + gesture.Commit(); + } + Assert.False(canonical.TryGet(property, out _)); Assert.Equal(original, File.ReadAllBytes(Path.Combine(m_root, "Assets", path.localPath))); Assert.True(m_runtime.interactions.history.Undo().succeeded); @@ -752,6 +881,24 @@ void Set( Assert.Equal(0.2f, value.vector.x); } + [Fact] + public void CancelledMaterialGestureRestoresDraftDirtyStateAndLeavesNoUndoEntry() + { + (AssetPath path, ShaderPropertyId property) = CreateMaterial(); + MaterialDocuments documents = Assert.IsType(m_sink.materials); + Guid id = documents.Open(path); + Assert.False(m_runtime.interactions.history.canUndo); + using var gesture = new EditorEditGesture(m_runtime.interactions.history, "Cancelled Material Edit"); + MaterialAsset candidate = documents.Read(id); + candidate.Set(property, MaterialValue.FromColor(new Inno.Core.Mathematics.Color(0.7f, 1, 1, 1))); + documents.Replace(id, candidate, gesture.mergeKey); + Assert.True(Assert.Single(m_runtime.interactions.documents.documents).isDirty); + Assert.True(gesture.Cancel().succeeded); + Assert.False(documents.Read(id).TryGet(property, out _)); + Assert.False(Assert.Single(m_runtime.interactions.documents.documents).isDirty); + Assert.False(m_runtime.interactions.history.canUndo); + } + [Fact] public void MaterialSaveConflictKeepsDiskAndRecoveredDraftDistinct() { @@ -795,6 +942,7 @@ void External(float value) Assert.True(m_assets.Import(path)); Tick(); } + External(0.2f); Assert.True(documents.Read(id).TryGet(property, out MaterialValue inherited)); Assert.Equal(0.2f, inherited.vector.x); @@ -820,32 +968,75 @@ public void MultipleMaterialSamplesCommitAsOneIndependentUndoWithoutPublishing() MaterialDocuments documents = Assert.IsType(m_sink.materials); Guid first = documents.Open(firstPath), second = documents.Open(secondPath); byte[] original = sources.Read(firstPath).bytes; - void Sample(float value) - { + void Sample( + float value, + string? mergeKey + ) { var candidates = new Dictionary(); - foreach (Guid id in new[] { first, second }) + foreach (Guid id in new[] + { + first, + second + } + + ) { MaterialAsset candidate = documents.Read(id); candidate.Set(property, MaterialValue.FromColor(new Inno.Core.Mathematics.Color(value, 1, 1, 1))); candidates.Add(id, candidate); } - documents.ReplaceMany(candidates, false); + + documents.ReplaceMany(candidates, mergeKey); + } + + using (var gesture = new EditorEditGesture(m_runtime.interactions.history, "First Selection Color")) + { + Sample(0.2f, gesture.mergeKey); + Sample(0.4f, gesture.mergeKey); + gesture.Commit(); + } + + using (var gesture = new EditorEditGesture(m_runtime.interactions.history, "Second Selection Color")) + { + Sample(0.6f, gesture.mergeKey); + gesture.Commit(); } - Sample(0.2f); Sample(0.4f); documents.CommitMany([first, second]); - Sample(0.6f); documents.CommitMany([first, second]); + Assert.True(m_runtime.interactions.history.canUndo); + Assert.True(m_runtime.interactions.history.Undo().succeeded); - foreach (Guid id in new[] { first, second }) + foreach (Guid id in new[] + { + first, + second + } + + ) { Assert.True(documents.Read(id).TryGet(property, out MaterialValue value)); Assert.Equal(0.4f, value.vector.x); } + Assert.True(m_runtime.interactions.history.Undo().succeeded); - foreach (Guid id in new[] { first, second }) Assert.False(documents.Read(id).TryGet(property, out _)); + foreach (Guid id in new[] + { + first, + second + } + + ) + Assert.False(documents.Read(id).TryGet(property, out _)); Assert.All(m_runtime.interactions.documents.documents, document => Assert.False(document.isDirty)); Assert.Equal(original, sources.Read(firstPath).bytes); Assert.Equal(original, sources.Read(secondPath).bytes); Assert.True(m_runtime.interactions.history.Redo().succeeded); - foreach (Guid id in new[] { first, second }) Assert.True(documents.Read(id).TryGet(property, out _)); + foreach (Guid id in new[] + { + first, + second + } + + ) + Assert.True(documents.Read(id).TryGet(property, out _)); } [Fact] @@ -859,20 +1050,24 @@ public void CommonMaterialEditingPreservesUntouchedMixedComponentsAndTextureRefe var firstTexture = new TextureAsset(1, 1, TextureColorSpace.Linear, "first"); var secondTexture = new TextureAsset(1, 1, TextureColorSpace.Linear, "second"); first = MaterialValue.FromTexture(firstTexture); - edited = first; edited.sampler = new(RenderSamplerFilter.Point, RenderSamplerAddressMode.Clamp, RenderSamplerAddressMode.Clamp, RenderSamplerAddressMode.Clamp); + edited = first; + edited.sampler = new(RenderSamplerFilter.Point, RenderSamplerAddressMode.Clamp, RenderSamplerAddressMode.Clamp, RenderSamplerAddressMode.Clamp); other = MaterialValue.FromTexture(secondTexture); result = ShaderPropertyInspector.ApplyEdit(ShaderPropertyType.Texture2D, first, edited, other); Assert.Same(secondTexture, result.texture); Assert.Equal(RenderSamplerFilter.Point, result.sampler.filter); } - [Fact] public void PipelineSettingsDraftsPreserveReferencesAndPublishOnlyAfterSave() { (AssetPath materialPath, _) = CreateMaterial(); MaterialAsset material = m_assets.Load(materialPath); - var originalSettings = new WorkflowPipelineSettings { material = material, exposure = 1 }; + var originalSettings = new WorkflowPipelineSettings + { + material = material, + exposure = 1 + }; var initial = new RenderPipelineAsset { pipelineTypeId = "tests.workflow.pipeline", @@ -907,7 +1102,6 @@ public void PipelineSettingsDraftsPreserveReferencesAndPublishOnlyAfterSave() Assert.True(m_runtime.interactions.documents.Revert(document.documentId)); Assert.Same(material, Read(documents.Read(id)).material); Assert.Equal(3, Read(documents.Read(id)).exposure); - WorkflowPipelineSettings Read(RenderPipelineAsset pipeline) { var result = new WorkflowPipelineSettings(); @@ -920,7 +1114,11 @@ WorkflowPipelineSettings Read(RenderPipelineAsset pipeline) public void PipelineMissingSettingsKeepOpaqueBytesThroughHistoryAndRecovery() { var unknown = new SerializedRenderExtensionState(Guid.NewGuid(), new byte[] { 19, 42, 7 }); - var source = new RenderPipelineAsset { pipelineTypeId = "tests.missing-pipeline", pipelineState = unknown }; + var source = new RenderPipelineAsset + { + pipelineTypeId = "tests.missing-pipeline", + pipelineState = unknown + }; AssetPath path = AssetPath.Project("Missing.irenderpipeline"); AssetSourceStore sources = m_assets.CreateSourceStore(); sources.Save(path, sources.Encode(source), null); @@ -947,8 +1145,11 @@ public void PipelineMissingSettingsKeepOpaqueBytesThroughHistoryAndRecovery() [StableTypeId("8c096b6f-92c3-48ed-917e-955858d6a65c")] public sealed unsafe class WorkflowPipelineSettings : ISerializable { - [SerializableProperty] public MaterialAsset? material { get; set; } - [SerializableProperty] public float exposure { get; set; } + [SerializableProperty] + public MaterialAsset? material { get; set; } + + [SerializableProperty] + public float exposure { get; set; } } private (AssetPath, ShaderPropertyId) CreateMaterial() @@ -978,12 +1179,25 @@ private Guid AssetId(AssetFileEntry entry) Assert.True(m_assets.TryGetInfo(entry.assetPath, out AssetInfo? info)); return info!.persistentId; } - private void SelectAndDraw(AssetFileEntry entry) { m_runtime.interactions.SetSelection(entry); Draw(); } - private void Tick() { m_runtime.Update(new(1f / 60, 0, true)); Draw(); } + + private void SelectAndDraw(AssetFileEntry entry) + { + Assert.True(m_assets.TryGetFileSystemEntry(entry.assetPath, out AssetFileEntry current)); + m_runtime.interactions.SetSelection(current); + Draw(); + } + + private void Tick() + { + m_runtime.Update(new(1f / 60, 0, true)); + Draw(); + } + private unsafe void Draw(Vector2? size = null) { EditorPanelExtension panel = Assert.Single(m_runtime.panels, panel => panel.id == "rendering.shader-editor"); panel.isOpen = true; + using IDisposable uiScope = m_ui.Enter(); ImGuiApi.NewFrame(); ImGuiApi.SetNextWindowPos(new(0, 0)); ImGuiApi.SetNextWindowSize(size ?? new(1200, 800)); @@ -995,12 +1209,17 @@ private unsafe void Draw(Vector2? size = null) m_canvasMinimum = ImGuiApi.GetItemRectMin(); m_canvasMaximum = ImGuiApi.GetItemRectMax(); } - finally { ImGuiApi.End(); ImGuiApi.Render(); } + finally + { + ImGuiApi.End(); + ImGuiApi.Render(); + } } public unsafe void Dispose() { m_runtime.Dispose(); + m_ui.Dispose(); ImGuiApi.Raw.DestroyContext(m_imgui); m_artifacts.Dispose(); m_assets.Dispose(); @@ -1042,10 +1261,11 @@ public override void Draw(ShaderNodeDrawContext context) public sealed unsafe class CanvasProbeCompiler : IShaderNodeCompiler { public string definitionId => "tests.shader-ui-probe"; + public IReadOnlyList GetPorts(ShaderNodeDescriptionContext context) => []; - public IReadOnlyDictionary Lower(ShaderNodeLoweringContext context) - => new Dictionary(); + public IReadOnlyDictionary Lower(ShaderNodeLoweringContext context) => new Dictionary(); } + [EditorModule("tests.shader-workflow", order: 160)] public sealed unsafe class WorkflowProbe : EditorModule { @@ -1054,25 +1274,27 @@ public WorkflowProbe( MaterialDocuments materials, PipelineDocuments pipelines, WorkflowSink sink - ) { sink.graphs = graphs; sink.materials = materials; sink.pipelines = pipelines; } + ) { + sink.graphs = graphs; + sink.materials = materials; + sink.pipelines = pipelines; + } } private sealed class WorkflowCompiler : IShaderCompilerToolchain { public string implementationId => "tests.workflow"; public IReadOnlyList supportedSourceLanguages => []; + public ShaderCompileTarget CreateTarget( GraphicsCapabilities capabilities, bool optimize = true, bool debugInformation = false - ) - => new("tests:workflow", capabilities, optimize, debugInformation); + ) => new("tests:workflow", capabilities, optimize, debugInformation); public ValueTask CompileAsync( ShaderStageToolRequest request, CancellationToken cancellationToken - ) - => ValueTask.FromResult(new ShaderStageToolResult([1, 2, 3], request.stage.inputs.Where(input => input.kind == ShaderIrInputKind.Uniform) - .Select(input => new ShaderStageBinding(input.id, "native_" + input.id, 0)), [])); + ) => ValueTask.FromResult(new ShaderStageToolResult([1, 2, 3], request.stage.inputs.Where(input => input.kind == ShaderIrInputKind.Uniform).Select(input => new ShaderStageBinding(input.id, "native_" + input.id, 0)), [])); } private sealed class EmptyPreviews : IEditorPreviewService @@ -1080,24 +1302,42 @@ private sealed class EmptyPreviews : IEditorPreviewService public bool TryRender( EditorViewportComposition composition, out EditorPreviewHandle handle - ) { handle = default; return false; } - public void ReleaseRendered(string viewportId) { } + ) { + handle = default; + return false; + } + + public void ReleaseRendered(string viewportId) + { + } + public uint deviceGeneration => 1; + public bool TryGetTexture( TextureAsset texture, out EditorPreviewHandle handle - ) { handle = default; return false; } + ) { + handle = default; + return false; + } + public bool TryGetTextureArtifact( RenderTextureArtifactReference texture, int pixelWidth, int pixelHeight, out EditorPreviewHandle handle - ) { handle = default; return false; } + ) { + handle = default; + return false; + } + public void Draw( EditorPreviewHandle handle, Vector2 logicalSize ) => throw new InvalidOperationException("No texture was requested by this workflow."); public bool Release(EditorPreviewHandle handle) => false; - public void ReleaseAll() { } + public void ReleaseAll() + { + } } } diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs index db074b964..f749d2482 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs @@ -3,7 +3,6 @@ using Xunit; namespace Inno.Tooling.Architecture.Tests; - [Collection("Architecture diagnostics")] public sealed class IntegrationBoundaryTests { @@ -18,6 +17,12 @@ public sealed class IntegrationBoundaryTests [InlineData("build/toolchains/Probe", "build/bindings/Inno.Build.Bindings", "neutral toolchains cannot depend", true)] [InlineData("build/bindings/Inno.Build.Bindings", "build/tasks/Inno.Build.Tasks", "binding generation cannot depend", true)] [InlineData("platforms/WindowsX64/build/Probe", "build/bindings/Inno.Build.Bindings", "platform modules must borrow", true)] + [InlineData("src/composition/editor/features/Inno.Editor.TestFeature", "src/composition/editor/presentation/Inno.Editor.ImGui", "Editor feature editing must be presentation-independent", true)] + [InlineData("src/composition/editor/features/Inno.Editor.TestFeature", "src/composition/editor/framework/Inno.Editor.Interactions", "Editor feature editing must be presentation-independent", false)] + [InlineData("src/composition/editor/panels/Inno.Editor.Panel.A", "src/composition/editor/panels/Inno.Editor.Panel.B", "Panels cannot depend on other Panels", true)] + [InlineData("src/composition/editor/panels/Inno.Editor.Panel.A", "src/composition/editor/presentation/Inno.Editor.Assets.ImGui", "Panels cannot depend on other Panels", false)] + [InlineData("src/composition/editor/panels/Inno.Editor.Panel.A", "backends/ImGui/native/Inno.Native.ImGui", "native UI access belongs", true)] + [InlineData("src/composition/editor/presentation/Inno.Editor.Graph.ImGui", "src/composition/editor/features/Inno.Editor.Shaders", "reusable Graph mechanisms cannot depend", true)] public void CliChecksActualPlatformIntegrationProjectReferences( string owner, string dependency, @@ -34,7 +39,6 @@ bool rejected (_, string report) = ArchitectureCommandFixture.Run(root); Assert.Equal(rejected, report.Contains(expected, StringComparison.Ordinal)); Assert.DoesNotContain("explicit project dependency is missing", report); - string CreateProject( string relative, string references diff --git a/tools/Inno.Tooling.Architecture/ArchitectureRules.cs b/tools/Inno.Tooling.Architecture/ArchitectureRules.cs index e8cd30a3f..705fbf9e1 100644 --- a/tools/Inno.Tooling.Architecture/ArchitectureRules.cs +++ b/tools/Inno.Tooling.Architecture/ArchitectureRules.cs @@ -6,12 +6,10 @@ using System.Xml.Linq; namespace Inno.Tooling.Architecture; - internal static partial class ArchitectureRules { private static readonly string[] S_SCAN_ROOTS = ["entry", "src", "backends", "platforms", "build", "tests"]; - private static readonly string[] S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS = - [ + private static readonly string[] S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS = [ "Inno.Editor.", "Inno.Build", "Inno.Scripting.Compiler", @@ -25,16 +23,8 @@ internal static partial class ArchitectureRules "Inno.Adapter.Serialization.DotNet", "Toolchains" ]; - private static readonly string[] S_FORBIDDEN_IMPLEMENTATION_WORDS = - [ - "Legacy", - "Compatibility", - "Migration", - "Former", - "Deprecated" - ]; - private static readonly string[] S_REMOVED_PROJECT_NAMES = - [ + private static readonly string[] S_FORBIDDEN_IMPLEMENTATION_WORDS = ["Legacy", "Compatibility", "Migration", "Former", "Deprecated"]; + private static readonly string[] S_REMOVED_PROJECT_NAMES = [ "Inno.Core.Framework", "Inno.Core.Assemblies", "Inno.Core.Reflection", @@ -44,11 +34,16 @@ internal static partial class ArchitectureRules "Inno.Rendering.Core", "Inno.Native.Dll", "Inno.Adapter.Input.Sdl3", - "Inno.Editor.Application", "Inno.Player", "Inno.Build.Toolchains.Host", "Inno.Build.Toolchains.Browser", - "Inno.Build.SupportPacks", "Inno.Build.Platform.Windows", "Inno.Build.Platform.MacOS", "Inno.Build.Platform.Browser" + "Inno.Editor.Application", + "Inno.Player", + "Inno.Build.Toolchains.Host", + "Inno.Build.Toolchains.Browser", + "Inno.Build.SupportPacks", + "Inno.Build.Platform.Windows", + "Inno.Build.Platform.MacOS", + "Inno.Build.Platform.Browser" ]; - private static readonly string[] S_CONCRETE_ADAPTER_MARKERS = - [ + private static readonly string[] S_CONCRETE_ADAPTER_MARKERS = [ "Inno.Adapter.Platform.Sdl3", "Inno.Adapter.Storage.FileSystem", "Inno.Adapter.Rendering.Bgfx", @@ -60,12 +55,12 @@ internal static partial class ArchitectureRules "MiniAudioDevice", "PlatformImGuiContext" ]; - internal static void Validate( string repositoryRoot, ICollection failures ) { ValidateHostIsolation(repositoryRoot, failures); + EditorUiBoundaryValidator.Validate(repositoryRoot, failures); PlatformOwnershipValidator.Validate(repositoryRoot, failures); NativePlatformPolicyValidator.Validate(repositoryRoot, failures); string backendRoot = Path.Combine(repositoryRoot, "backends"); @@ -80,35 +75,69 @@ ICollection failures ValidateRemovedProjects(repositoryRoot, failures); } - internal static bool IsForbiddenPlayerDependency(string assemblyName) => S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS - .Any(fragment => assemblyName.Contains(fragment, StringComparison.Ordinal)) - || assemblyName.StartsWith("Inno.Integration.", StringComparison.Ordinal) - && assemblyName.EndsWith(".Bgfx", StringComparison.Ordinal); - + internal static bool IsForbiddenPlayerDependency(string assemblyName) => S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS.Any(fragment => assemblyName.Contains(fragment, StringComparison.Ordinal)) + || assemblyName.StartsWith( + "Inno.Integration.", + StringComparison.Ordinal + ) + && assemblyName.EndsWith( + ".Bgfx", + StringComparison.Ordinal + ); private static void ValidateHostIsolation( string repositoryRoot, ICollection failures ) { var executables = new HashSet(StringComparer.Ordinal) { - "InnoEngine.Editor", "InnoEngine.Build" + "InnoEngine.Editor", + "InnoEngine.Build" }; - foreach (string rootName in new[] { "entry", "src", "backends", "platforms", "build", "tools" }) + foreach (string rootName in new[] + { + "entry", + "src", + "backends", + "platforms", + "build", + "tools" + } + + ) { foreach (string project in EnumerateFiles(Path.Combine(repositoryRoot, rootName), "*.csproj")) { XDocument document = XDocument.Load(project); - bool test = Relative(repositoryRoot, project).Contains("/tests/", StringComparison.Ordinal) + bool test = Relative( + repositoryRoot, + project + ).Contains( + "/tests/", + StringComparison.Ordinal + ) && document.Descendants("IsTestProject").Any(static value => value.Value == "true"); - if (document.Descendants("OutputType").Any(static value => value.Value == "Exe") - && !test && !executables.Contains(Path.GetFileNameWithoutExtension(project))) + if (document.Descendants("OutputType").Any(static value => value.Value == "Exe") && !test && !executables.Contains(Path.GetFileNameWithoutExtension(project))) failures.Add($"{Relative(repositoryRoot, project)}: only the Editor and Build development entries may be production executable projects."); - if (Path.GetFileNameWithoutExtension(project).StartsWith("Inno.Native.", StringComparison.Ordinal) - && Path.GetFileNameWithoutExtension(project).EndsWith(".Browser", StringComparison.Ordinal)) + if (Path.GetFileNameWithoutExtension(project).StartsWith( + "Inno.Native.", + StringComparison.Ordinal + ) + && Path.GetFileNameWithoutExtension(project).EndsWith( + ".Browser", + StringComparison.Ordinal + )) failures.Add($"{Relative(repositoryRoot, project)}: native targets must use binding profiles, not duplicate projects."); } } - foreach (string rootName in new[] { "src/foundation", "src/composition/shell", "src/composition/player/Inno.Player.Runtime" }) + + foreach (string rootName in new[] + { + "src/foundation", + "src/composition/shell", + "src/composition/player/Inno.Player.Runtime" + } + + ) { string root = Path.Combine(repositoryRoot, rootName); if (!Directory.Exists(root)) @@ -119,9 +148,9 @@ ICollection failures failures.Add($"{Relative(repositoryRoot, sourcePath)}: shared code must receive host capabilities through contracts."); } } + string browserProject = Path.Combine(repositoryRoot, "platforms/BrowserWasm32/player/Inno.Player.BrowserWasm32/Inno.Player.BrowserWasm32.csproj"); - if (File.Exists(browserProject) && - XDocument.Load(browserProject).Descendants("Compile").Any(static item => item.Attribute("Link") is not null)) + if (File.Exists(browserProject) && XDocument.Load(browserProject).Descendants("Compile").Any(static item => item.Attribute("Link")is not null)) failures.Add("Inno.Player.BrowserWasm32: shared Player source must be consumed through Inno.Player.Runtime."); } @@ -160,6 +189,7 @@ ICollection failures if (fileName.Contains(word, StringComparison.OrdinalIgnoreCase)) failures.Add($"{relative}: implementation file names cannot contain '{word}'."); } + foreach (Match match in DeclaredTypePattern().Matches(source)) { string name = match.Groups[2].Value; @@ -178,72 +208,140 @@ ICollection failures ) { if (relative.StartsWith("tools/Inno.Tooling.Architecture/", StringComparison.Ordinal)) return; - if (relative.StartsWith("src/services/platform/", StringComparison.Ordinal) && - (source.Contains("PlatformNativeHandles", StringComparison.Ordinal) || - source.Contains("PlatformNativeHandleId", StringComparison.Ordinal) || - source.Contains("INativeWindowSurface", StringComparison.Ordinal))) + if (relative.StartsWith( + "src/services/platform/", + StringComparison.Ordinal + ) + && (source.Contains( + "PlatformNativeHandles", + StringComparison.Ordinal + ) + || source.Contains( + "PlatformNativeHandleId", + StringComparison.Ordinal + ) + || source.Contains( + "INativeWindowSurface", + StringComparison.Ordinal + ))) { failures.Add($"{relative}: native surface ABI belongs to platform adapters, not the shared platform service."); } - if (source.Contains(".With", StringComparison.Ordinal) && - !relative.EndsWith( - "src/content/assets/Inno.Assets/Serialization/AssetSerializationContext.cs", - StringComparison.Ordinal)) + + if (source.Contains( + ".With", + StringComparison.Ordinal + ) + && !relative.EndsWith( + "src/content/assets/Inno.Assets/Serialization/AssetSerializationContext.cs", + StringComparison.Ordinal + )) { - failures.Add( - $"{relative}: asset-aware serialization must use the owner-complete AssetSerializationContext factory."); + failures.Add($"{relative}: asset-aware serialization must use the owner-complete AssetSerializationContext factory."); } + if (StaticManagerDeclarationPattern().IsMatch(source)) failures.Add($"{relative}: process-wide static Manager ownership is forbidden."); if (StaticLogFacadeCallPattern().IsMatch(source)) failures.Add($"{relative}: engine implementation must use an explicitly owned Logger."); - if ((relative.StartsWith("src/composition/editor/", StringComparison.Ordinal) || - relative.StartsWith("src/composition/player/Inno.Player/", StringComparison.Ordinal)) && - source.Contains("_ = typeof(", StringComparison.Ordinal)) - { - failures.Add( - $"{relative}: composition roots must discover engine modules from their declared dependency closure, not typeof anchors."); - } - if ((relative.StartsWith("src/composition/player/Inno.Player.Runtime/", StringComparison.Ordinal) || - relative.StartsWith("src/composition/shell/", StringComparison.Ordinal)) && - S_CONCRETE_ADAPTER_MARKERS.Any(source.Contains)) - { - failures.Add( - $"{relative}: product composition must use Shell and backend-neutral adapter contracts instead of concrete backend types."); - } - if (relative.StartsWith("src/adapters/audio/Inno.Adapter.Audio.MiniAudio/", StringComparison.Ordinal) && - (source.Contains("AudioContentProvider", StringComparison.Ordinal) || - source.Contains("AudioMixerExtension", StringComparison.Ordinal) || - source.Contains("AudioMixerFeature", StringComparison.Ordinal) || - source.Contains("TypeCatalog", StringComparison.Ordinal) || - source.Contains("System.Reflection", StringComparison.Ordinal))) - { - failures.Add( - $"{relative}: the real-time MiniAudio adapter cannot retain managed extension-generation objects or reflection state."); - } - if (relative.StartsWith("src/services/audio/", StringComparison.Ordinal) && - relative.EndsWith("/Properties/ScriptingApi.cs", StringComparison.Ordinal) && - (source.Contains("typeof(IAudioDevice)", StringComparison.Ordinal) || - source.Contains("typeof(AudioDevice)", StringComparison.Ordinal) || - source.Contains("Inno.Native", StringComparison.Ordinal) || - source.Contains("typeof(MiniAudio", StringComparison.Ordinal))) + if ((relative.StartsWith( + "src/composition/editor/", + StringComparison.Ordinal + ) + || relative.StartsWith( + "src/composition/player/Inno.Player/", + StringComparison.Ordinal + )) + && source.Contains( + "_ = typeof(", + StringComparison.Ordinal + )) + { + failures.Add($"{relative}: composition roots must discover engine modules from their declared dependency closure, not typeof anchors."); + } + + if ((relative.StartsWith( + "src/composition/player/Inno.Player.Runtime/", + StringComparison.Ordinal + ) + || relative.StartsWith( + "src/composition/shell/", + StringComparison.Ordinal + )) + && S_CONCRETE_ADAPTER_MARKERS.Any(source.Contains)) + { + failures.Add($"{relative}: product composition must use Shell and backend-neutral adapter contracts instead of concrete backend types."); + } + + if (relative.StartsWith( + "src/adapters/audio/Inno.Adapter.Audio.MiniAudio/", + StringComparison.Ordinal + ) + && (source.Contains( + "AudioContentProvider", + StringComparison.Ordinal + ) + || source.Contains( + "AudioMixerExtension", + StringComparison.Ordinal + ) + || source.Contains( + "AudioMixerFeature", + StringComparison.Ordinal + ) + || source.Contains( + "TypeCatalog", + StringComparison.Ordinal + ) + || source.Contains( + "System.Reflection", + StringComparison.Ordinal + ))) + { + failures.Add($"{relative}: the real-time MiniAudio adapter cannot retain managed extension-generation objects or reflection state."); + } + + if (relative.StartsWith( + "src/services/audio/", + StringComparison.Ordinal + ) + && relative.EndsWith( + "/Properties/ScriptingApi.cs", + StringComparison.Ordinal + ) + && (source.Contains( + "typeof(IAudioDevice)", + StringComparison.Ordinal + ) + || source.Contains( + "typeof(AudioDevice)", + StringComparison.Ordinal + ) + || source.Contains( + "Inno.Native", + StringComparison.Ordinal + ) + || source.Contains( + "typeof(MiniAudio", + StringComparison.Ordinal + ))) { failures.Add($"{relative}: scripting API cannot export audio devices, native bindings, or backend adapters."); } + int lineNumber = 0; using var reader = new StringReader(source); string? line; - while ((line = reader.ReadLine()) is not null) + while ((line = reader.ReadLine())is not null) { lineNumber++; string trimmed = line.TrimStart(); - if (trimmed.StartsWith("///", StringComparison.Ordinal) || - trimmed.StartsWith("//", StringComparison.Ordinal)) + if (trimmed.StartsWith("///", StringComparison.Ordinal) || trimmed.StartsWith("//", StringComparison.Ordinal)) { continue; } - if (PublicNativeSignaturePattern().IsMatch(line) && - !relative.StartsWith("native/", StringComparison.Ordinal)) + + if (PublicNativeSignaturePattern().IsMatch(line) && !relative.StartsWith("native/", StringComparison.Ordinal)) { failures.Add($"{relative}:{lineNumber}: public or protected API leaks a native implementation type."); } @@ -269,18 +367,17 @@ ICollection failures foreach (string path in EnumerateFiles(repositoryRoot, "*.csproj")) { string relative = Relative(repositoryRoot, path); - if (relative.StartsWith("extern/", StringComparison.Ordinal) || - relative.StartsWith("demo/", StringComparison.Ordinal)) + if (relative.StartsWith("extern/", StringComparison.Ordinal) || relative.StartsWith("demo/", StringComparison.Ordinal)) { continue; } + XDocument document = XDocument.Load(path); - string name = document.Descendants("AssemblyName").Select(static value => value.Value.Trim()) - .FirstOrDefault(static value => value.Length > 0) - ?? Path.GetFileNameWithoutExtension(path); + string name = document.Descendants("AssemblyName").Select(static value => value.Value.Trim()).FirstOrDefault(static value => value.Length > 0) ?? Path.GetFileNameWithoutExtension(path); var node = new ProjectNode(path, relative, name); graph.Add(Path.GetFullPath(path), node); } + foreach (ProjectNode node in graph.Values) { XDocument document = XDocument.Load(node.path); @@ -288,7 +385,7 @@ ICollection failures PackageDependencyBoundaryValidator.Validate(node.relative, document, failures); foreach (XElement reference in document.Descendants("ProjectReference")) { - if (string.Equals((string?)reference.Attribute("ReferenceOutputAssembly"), "false", StringComparison.OrdinalIgnoreCase)) + if (string.Equals((string? )reference.Attribute("ReferenceOutputAssembly"), "false", StringComparison.OrdinalIgnoreCase)) continue; string? include = reference.Attribute("Include")?.Value; if (string.IsNullOrWhiteSpace(include)) @@ -296,24 +393,25 @@ ICollection failures // SDK evaluation validates property-based references; this static pass does not invent property values. if (include.Contains("$(", StringComparison.Ordinal)) continue; - string normalizedInclude = include - .Replace('\\', Path.DirectorySeparatorChar) - .Replace('/', Path.DirectorySeparatorChar); + string normalizedInclude = include.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar); string targetPath = Path.GetFullPath(normalizedInclude, Path.GetDirectoryName(node.path)!); if (!graph.TryGetValue(targetPath, out ProjectNode? target)) { failures.Add($"{node.relative}: project reference '{include}' does not resolve to a repository project."); continue; } + if (node.references.Contains(target)) { failures.Add($"{node.relative}: project reference '{include}' is duplicated."); continue; } + node.references.Add(target); ValidateReferenceBoundary(node, target, failures); } } + return graph; } @@ -325,18 +423,25 @@ ICollection failures foreach (XElement noWarn in document.Descendants("NoWarn")) { string value = noWarn.Value; - if (value.Contains("CS1572", StringComparison.OrdinalIgnoreCase) || - value.Contains("CS1573", StringComparison.OrdinalIgnoreCase) || - value.Contains("CS1591", StringComparison.OrdinalIgnoreCase)) + if (value.Contains( + "CS1572", + StringComparison.OrdinalIgnoreCase + ) + || value.Contains( + "CS1573", + StringComparison.OrdinalIgnoreCase + ) + || value.Contains( + "CS1591", + StringComparison.OrdinalIgnoreCase + )) { failures.Add($"{project.relative}: public XML contract warnings cannot be suppressed."); } } } - private static bool IsNativeProject(string path) => path.StartsWith("backends/", StringComparison.Ordinal) - && path.Contains("/native/", StringComparison.Ordinal); - + private static bool IsNativeProject(string path) => path.StartsWith("backends/", StringComparison.Ordinal) && path.Contains("/native/", StringComparison.Ordinal); private static void ValidateReferenceBoundary( ProjectNode project, ProjectNode target, @@ -347,98 +452,184 @@ ICollection failures bool foundationTarget = targetPath.StartsWith("src/foundation/", StringComparison.Ordinal); if ((project.name is "Inno.Rendering" or "Inno.Content") && !foundationTarget) failures.Add($"{sourcePath}: neutral mechanisms may only reference Foundation contracts, not {targetPath}."); - if (project.name == "Inno.Rendering.Assets" && target.name is - "Inno.Rendering.Runtime" or "Inno.Rendering.Shaders" or "Inno.Rendering.Assets.Authoring" or "Inno.Assets.Pipeline") + if (project.name == "Inno.Rendering.Assets" + && target.name is "Inno.Rendering.Runtime" or "Inno.Rendering.Shaders" or "Inno.Rendering.Assets.Authoring" or "Inno.Assets.Pipeline") failures.Add($"{sourcePath}: runtime asset definitions cannot reference authoring or runtime owners in {targetPath}."); - if (project.name == "Inno.Rendering.Runtime" && target.name is - "Inno.Rendering.Assets.Authoring" or "Inno.Assets.Pipeline" or "Inno.Rendering.Shaders") + if (project.name == "Inno.Rendering.Runtime" && target.name is "Inno.Rendering.Assets.Authoring" or "Inno.Assets.Pipeline" or "Inno.Rendering.Shaders") failures.Add($"{sourcePath}: rendering runtime cannot include authoring implementation {targetPath}."); if (project.name == "Inno.Rendering.Shaders" && target.name == "Inno.Rendering.Runtime") failures.Add($"{sourcePath}: shader authoring cannot depend on the runtime owner {targetPath}."); - if (project.name == "Inno.Adapter.Input" && (IsNativeProject(targetPath) - || IsConcreteAdapter(target.name))) + if (project.name == "Inno.Adapter.Input" && (IsNativeProject(targetPath) || IsConcreteAdapter(target.name))) failures.Add($"{sourcePath}: shared event input cannot depend on native or platform adapters in {targetPath}."); - if (project.name == "Inno.Build" && (targetPath.StartsWith("build/composition/", StringComparison.Ordinal) - || targetPath.StartsWith("build/pipeline/Inno.Build.Platform.", StringComparison.Ordinal) + if (project.name == "Inno.Build" + && (targetPath.StartsWith( + "build/composition/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "build/pipeline/Inno.Build.Platform.", + StringComparison.Ordinal + ) || target.name == "Inno.Build.Managed.DotNet")) failures.Add($"{sourcePath}: the build pipeline must receive providers instead of referencing {targetPath}."); - if (IsNativeProject(sourcePath) && !IsNativeProject(targetPath) - && !(project.name == "Inno.Native.LibraryLoading" && target.name == "Inno.Core.IO")) + if (IsNativeProject(sourcePath) && !IsNativeProject(targetPath) && !(project.name == "Inno.Native.LibraryLoading" && target.name == "Inno.Core.IO")) { failures.Add($"{sourcePath}: Native cannot reference upper-layer project {targetPath}."); } - if (sourcePath.StartsWith("src/foundation/", StringComparison.Ordinal) && - !targetPath.StartsWith("src/foundation/", StringComparison.Ordinal)) + + if (sourcePath.StartsWith("src/foundation/", StringComparison.Ordinal) && !targetPath.StartsWith("src/foundation/", StringComparison.Ordinal)) { failures.Add($"{sourcePath}: Core cannot reference upper-layer project {targetPath}."); } - if (sourcePath.StartsWith("build/", StringComparison.Ordinal) && - !sourcePath.StartsWith("build/cli/", StringComparison.Ordinal) && - targetPath.StartsWith("src/composition/editor/", StringComparison.Ordinal)) + + if (sourcePath.StartsWith( + "build/", + StringComparison.Ordinal + ) + && !sourcePath.StartsWith( + "build/cli/", + StringComparison.Ordinal + ) + && targetPath.StartsWith( + "src/composition/editor/", + StringComparison.Ordinal + )) { failures.Add($"{sourcePath}: Build cannot reference Editor project {targetPath}."); } - if ((string.Equals(project.name, "Inno.Rendering", StringComparison.Ordinal) || - string.Equals(project.name, "Inno.Rendering.Runtime", StringComparison.Ordinal)) && - (targetPath.StartsWith("build/", StringComparison.Ordinal) || - targetPath.StartsWith("src/adapters/", StringComparison.Ordinal) || - IsNativeProject(targetPath))) + + if ((string.Equals( + project.name, + "Inno.Rendering", + StringComparison.Ordinal + ) + || string.Equals( + project.name, + "Inno.Rendering.Runtime", + StringComparison.Ordinal + )) + && (targetPath.StartsWith( + "build/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/adapters/", + StringComparison.Ordinal + ) + || IsNativeProject(targetPath))) { failures.Add($"{sourcePath}: backend-neutral Rendering cannot reference implementation project {targetPath}."); } - if (string.Equals(project.name, "Inno.Build", StringComparison.Ordinal) && - targetPath.StartsWith("build/support/", StringComparison.Ordinal)) + + if (string.Equals(project.name, "Inno.Build", StringComparison.Ordinal) && targetPath.StartsWith("build/support/", StringComparison.Ordinal)) { failures.Add($"{sourcePath}: Build pipeline must depend on the provisioner contract, not Support Pack implementations."); } - if (sourcePath.Contains("Inno.Rendering/", StringComparison.Ordinal) && - (targetPath.Contains("MaterialGraph", StringComparison.Ordinal) || - targetPath.StartsWith("src/content/scene/", StringComparison.Ordinal) || - targetPath.StartsWith("src/composition/editor/", StringComparison.Ordinal))) + + if (sourcePath.Contains( + "Inno.Rendering/", + StringComparison.Ordinal + ) + && (targetPath.Contains( + "MaterialGraph", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/content/scene/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/composition/editor/", + StringComparison.Ordinal + ))) { failures.Add($"{sourcePath}: backend-neutral Rendering cannot reference {targetPath}."); } - if (string.Equals(project.name, "Inno.Audio", StringComparison.Ordinal) && - (targetPath.StartsWith("src/services/audio/Inno.Audio.Runtime/", StringComparison.Ordinal) || - targetPath.StartsWith("src/adapters/audio/Inno.Adapter.Audio.MiniAudio/", StringComparison.Ordinal) || - targetPath.StartsWith("src/content/scene/", StringComparison.Ordinal) || - targetPath.StartsWith("src/runtime/", StringComparison.Ordinal) || - targetPath.StartsWith("src/composition/editor/", StringComparison.Ordinal) || - targetPath.StartsWith("src/services/platform/", StringComparison.Ordinal) || - IsNativeProject(targetPath))) + + if (string.Equals( + project.name, + "Inno.Audio", + StringComparison.Ordinal + ) + && (targetPath.StartsWith( + "src/services/audio/Inno.Audio.Runtime/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/adapters/audio/Inno.Adapter.Audio.MiniAudio/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/content/scene/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/runtime/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/composition/editor/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/services/platform/", + StringComparison.Ordinal + ) + || IsNativeProject(targetPath))) { failures.Add($"{sourcePath}: backend-neutral Audio cannot reference {targetPath}."); } - if (string.Equals(project.name, "Inno.Animation", StringComparison.Ordinal) && - (targetPath.StartsWith("src/runtime/", StringComparison.Ordinal) || - targetPath.StartsWith("src/content/scene/", StringComparison.Ordinal) || - targetPath.StartsWith("src/services/rendering/", StringComparison.Ordinal) || - targetPath.StartsWith("src/services/audio/", StringComparison.Ordinal) || - targetPath.StartsWith("src/composition/editor/", StringComparison.Ordinal) || - IsNativeProject(targetPath))) + + if (string.Equals( + project.name, + "Inno.Animation", + StringComparison.Ordinal + ) + && (targetPath.StartsWith( + "src/runtime/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/content/scene/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/services/rendering/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/services/audio/", + StringComparison.Ordinal + ) + || targetPath.StartsWith( + "src/composition/editor/", + StringComparison.Ordinal + ) + || IsNativeProject(targetPath))) { failures.Add($"{sourcePath}: backend-neutral Animation cannot reference {targetPath}."); } - if (target.name.Contains("Inno.Native.Bgfx", StringComparison.Ordinal) && - !IsAllowedBgfxConsumer(project.name)) + + if (target.name.Contains("Inno.Native.Bgfx", StringComparison.Ordinal) && !IsAllowedBgfxConsumer(project.name)) { failures.Add($"{sourcePath}: BGFX native code is restricted to BGFX adapters and toolchains."); } - if (target.name.Contains("Inno.Native.Sdl3", StringComparison.OrdinalIgnoreCase) && - !IsAllowedSdlConsumer(project.name)) + + if (target.name.Contains("Inno.Native.Sdl3", StringComparison.OrdinalIgnoreCase) && !IsAllowedSdlConsumer(project.name)) { failures.Add($"{sourcePath}: SDL3 native code is restricted to SDL3 adapters and toolchains."); } - if (target.name.Contains("Inno.Native.MiniAudio", StringComparison.Ordinal) && - !IsAllowedMiniAudioConsumer(project.name)) + + if (target.name.Contains("Inno.Native.MiniAudio", StringComparison.Ordinal) && !IsAllowedMiniAudioConsumer(project.name)) { failures.Add($"{sourcePath}: miniaudio native code is restricted to the MiniAudio adapter and toolchain."); } - if (target.name.Contains("Inno.Native.UI", StringComparison.Ordinal) && - !IsAllowedUiNativeConsumer(project.name)) + + if (target.name.Contains("Inno.Native.UI", StringComparison.Ordinal) && !IsAllowedUiNativeConsumer(project.name)) { failures.Add($"{sourcePath}: RmlUi native code is restricted to the RmlUi adapter and UI toolchain."); } + if (IsAdapterContract(project.name) && IsConcreteAdapter(target.name)) { failures.Add($"{sourcePath}: adapter contract projects cannot reference concrete implementation {targetPath}."); @@ -454,7 +645,6 @@ ICollection failures var stack = new List(); foreach (ProjectNode node in graph.Values) Visit(node); - void Visit(ProjectNode node) { if (states.TryGetValue(node, out VisitState state)) @@ -465,8 +655,10 @@ void Visit(ProjectNode node) string cycle = string.Join(" -> ", stack.Skip(start).Append(node).Select(static value => value.name)); failures.Add($"{Relative(repositoryRoot, node.path)}: project reference cycle detected: {cycle}."); } + return; } + states[node] = VisitState.Visiting; stack.Add(node); foreach (ProjectNode target in node.references) @@ -487,6 +679,7 @@ ICollection failures failures.Add("src/composition/player/Inno.Player: Player composition project is missing."); return; } + var visited = new HashSet(); var pending = new Stack(); pending.Push(player); @@ -495,12 +688,11 @@ ICollection failures ProjectNode current = pending.Pop(); if (!visited.Add(current)) continue; - if (!ReferenceEquals(current, player) && - S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS.Any(fragment => - current.name.Contains(fragment, StringComparison.OrdinalIgnoreCase))) + if (!ReferenceEquals(current, player) && S_FORBIDDEN_PLAYER_PROJECT_FRAGMENTS.Any(fragment => current.name.Contains(fragment, StringComparison.OrdinalIgnoreCase))) { failures.Add($"{Relative(repositoryRoot, player.path)}: Player dependency closure contains forbidden project {current.name}."); } + foreach (ProjectNode target in current.references) pending.Push(target); } @@ -517,13 +709,9 @@ ICollection failures failures.Add($"InnoEngine.sln: removed project '{removed}' remains in the solution."); foreach (string projectPath in EnumerateFiles(repositoryRoot, "*.csproj")) { - if (string.Equals( - Path.GetFileNameWithoutExtension(projectPath), - removed, - StringComparison.Ordinal)) + if (string.Equals(Path.GetFileNameWithoutExtension(projectPath), removed, StringComparison.Ordinal)) { - failures.Add( - $"{Relative(repositoryRoot, projectPath)}: removed project '{removed}' remains in the repository."); + failures.Add($"{Relative(repositoryRoot, projectPath)}: removed project '{removed}' remains in the repository."); } } } @@ -543,8 +731,7 @@ ICollection failures ConceptualLayer? targetLayer = ClassifyConceptualLayer(target); if (targetLayer is null || targetLayer <= sourceLayer) continue; - failures.Add( - $"{project.relative}: {sourceLayer} project cannot reference upper {targetLayer} project {target.relative}."); + failures.Add($"{project.relative}: {sourceLayer} project cannot reference upper {targetLayer} project {target.relative}."); } } } @@ -555,26 +742,23 @@ ICollection failures ) { ValidateHost("Inno.Player.Runtime"); ValidateHost("Inno.Editor.Hosting"); - void ValidateHost(string projectName) { - ProjectNode? host = graph.Values.SingleOrDefault(value => - string.Equals(value.name, projectName, StringComparison.Ordinal)); + ProjectNode? host = graph.Values.SingleOrDefault(value => string.Equals(value.name, projectName, StringComparison.Ordinal)); if (host is null) { failures.Add($"{projectName}: product composition project is missing."); return; } - if (!host.references.Any(static target => - string.Equals(target.name, "Inno.Shell", StringComparison.Ordinal))) + + if (!host.references.Any(static target => string.Equals(target.name, "Inno.Shell", StringComparison.Ordinal))) { failures.Add($"{host.relative}: product composition must inherit the common Inno.Shell lifecycle."); } - foreach (ProjectNode target in host.references.Where(target => - projectName == "Inno.Player.Runtime" && IsConcreteAdapter(target.name))) + + foreach (ProjectNode target in host.references.Where(target => projectName == "Inno.Player.Runtime" && IsConcreteAdapter(target.name))) { - failures.Add( - $"{host.relative}: product composition cannot reference concrete adapter project {target.relative}; use Inno.Adapter.Default and neutral contracts."); + failures.Add($"{host.relative}: product composition cannot reference concrete adapter project {target.relative}; use Inno.Adapter.Default and neutral contracts."); } } } @@ -587,100 +771,207 @@ void ValidateHost(string projectName) { return ConceptualLayer.Foundation; } + if (project.relative.StartsWith("src/composition/", StringComparison.Ordinal)) { return ConceptualLayer.Composition; } + if (project.name.StartsWith("Inno.Adapter", StringComparison.Ordinal)) { return ConceptualLayer.Adapter; } + return ConceptualLayer.EngineCapability; } - private static bool IsAdapterContract(string name) - => string.Equals(name, "Inno.Adapter", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Platform", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Input", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Storage", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Rendering", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Audio", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Text", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.UI", StringComparison.Ordinal) || - string.Equals(name, "Inno.Adapter.Presentation", StringComparison.Ordinal); - - private static bool IsConcreteAdapter(string name) - => name.StartsWith("Inno.Adapter.Content.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Platform.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Input.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Storage.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Rendering.", StringComparison.Ordinal) && - !string.Equals(name, "Inno.Adapter.Rendering.Authoring", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Audio.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Text.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.UI.", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Presentation.", StringComparison.Ordinal); - - private static bool IsAllowedBgfxConsumer(string name) - => name.StartsWith("Inno.Adapter.Rendering.Bgfx", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Presentation.ImGui.Bgfx", StringComparison.Ordinal) || - name.StartsWith("Inno.Build.Toolchains.Bgfx", StringComparison.Ordinal) || - name.StartsWith("Inno.Native.Bgfx", StringComparison.Ordinal) || - name.EndsWith(".Tests", StringComparison.Ordinal); - - private static bool IsAllowedSdlConsumer(string name) - => name.StartsWith("Inno.Adapter.Platform.Sdl3", StringComparison.Ordinal) || - name.StartsWith("Inno.Adapter.Presentation.ImGui.Sdl3", StringComparison.Ordinal) || - name.StartsWith("Inno.Build.Toolchains.Sdl3", StringComparison.Ordinal) || - name.StartsWith("Inno.Native.Sdl3", StringComparison.Ordinal) || - name is "Inno.Integration.WindowsX64.Sdl3" or "Inno.Integration.MacOSArm64.Sdl3" or "Inno.Integration.BrowserWasm32.Sdl3" || - name.EndsWith(".Tests", StringComparison.Ordinal); - - private static bool IsAllowedMiniAudioConsumer(string name) - => name.StartsWith("Inno.Adapter.Audio.MiniAudio", StringComparison.Ordinal) || - name.StartsWith("Inno.Build.Toolchains.MiniAudio", StringComparison.Ordinal) || - name.StartsWith("Inno.Native.MiniAudio", StringComparison.Ordinal); - - private static bool IsAllowedUiNativeConsumer(string name) - => name.StartsWith("Inno.Adapter.UI.RmlUi", StringComparison.Ordinal) || - name.StartsWith("Inno.Build.Toolchains.UI", StringComparison.Ordinal) || - name.StartsWith("Inno.Native.UI", StringComparison.Ordinal) || - name.EndsWith(".Tests", StringComparison.Ordinal); - + private static bool IsAdapterContract(string name) => string.Equals( + name, + "Inno.Adapter", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Platform", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Input", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Storage", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Rendering", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Audio", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Text", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.UI", + StringComparison.Ordinal + ) + || string.Equals( + name, + "Inno.Adapter.Presentation", + StringComparison.Ordinal + ); + private static bool IsConcreteAdapter(string name) => name.StartsWith( + "Inno.Adapter.Content.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Platform.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Input.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Storage.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Rendering.", + StringComparison.Ordinal + ) + && !string.Equals( + name, + "Inno.Adapter.Rendering.Authoring", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Audio.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Text.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.UI.", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Presentation.", + StringComparison.Ordinal + ); + private static bool IsAllowedBgfxConsumer(string name) => name.StartsWith( + "Inno.Adapter.Rendering.Bgfx", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Presentation.ImGui.Bgfx", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Build.Toolchains.Bgfx", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Native.Bgfx", + StringComparison.Ordinal + ) + || name.EndsWith( + ".Tests", + StringComparison.Ordinal + ); + private static bool IsAllowedSdlConsumer(string name) => name.StartsWith( + "Inno.Adapter.Platform.Sdl3", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Adapter.Presentation.ImGui.Sdl3", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Build.Toolchains.Sdl3", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Native.Sdl3", + StringComparison.Ordinal + ) + || name is "Inno.Integration.WindowsX64.Sdl3" or "Inno.Integration.MacOSArm64.Sdl3" or "Inno.Integration.BrowserWasm32.Sdl3" + || name.EndsWith( + ".Tests", + StringComparison.Ordinal + ); + private static bool IsAllowedMiniAudioConsumer(string name) => name.StartsWith( + "Inno.Adapter.Audio.MiniAudio", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Build.Toolchains.MiniAudio", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Native.MiniAudio", + StringComparison.Ordinal + ); + private static bool IsAllowedUiNativeConsumer(string name) => name.StartsWith( + "Inno.Adapter.UI.RmlUi", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Build.Toolchains.UI", + StringComparison.Ordinal + ) + || name.StartsWith( + "Inno.Native.UI", + StringComparison.Ordinal + ) + || name.EndsWith( + ".Tests", + StringComparison.Ordinal + ); private static IEnumerable EnumerateFiles( string root, string pattern ) => RepositorySourceInventory.Files(root, pattern); - - private static bool IsGenerated(string source) - => source.Contains("", StringComparison.OrdinalIgnoreCase) || - source.Contains("[GeneratedCode", StringComparison.Ordinal); - + private static bool IsGenerated(string source) => source.Contains( + "", + StringComparison.OrdinalIgnoreCase + ) + || source.Contains( + "[GeneratedCode", + StringComparison.Ordinal + ); private static string Relative( string repositoryRoot, string path ) => Path.GetRelativePath(repositoryRoot, path).Replace('\\', '/'); - [GeneratedRegex(@"\b(class|struct|interface|enum|record)\s+([A-Za-z_]\w*)")] private static partial Regex DeclaredTypePattern(); - [GeneratedRegex(@"\bstatic\s+class\s+(DiagnosticManager|LogManager|IdentityManager|JobSystemManager|AssemblyManager|TypeCacheManager|SerializationManager|ProjectSettingsManager|AssetManager|PluginManager|Shell)\b")] private static partial Regex StaticManagerDeclarationPattern(); - [GeneratedRegex(@"\bLog\.(Debug|Info|Warn|Error|Fatal)\s*\(")] private static partial Regex StaticLogFacadeCallPattern(); - [GeneratedRegex(@"\b(public|protected)\b[^\r\n;{]*(Inno\.Native\.|bgfx_|SDL[A-Z]|ImGuiPtr|Ma[A-Z]\w*)")] private static partial Regex PublicNativeSignaturePattern(); - [GeneratedRegex(@"BindingFlags\s*\.[^\r\n;]*(NonPublic|Private)|(GetField|GetMethod|GetProperty|GetConstructor)\s*\([^\r\n;]*BindingFlags\s*\.[^\r\n;]*(NonPublic|Private)")] private static partial Regex NonPublicReflectionPattern(); - private sealed class ProjectNode( string path, string relative, string name - ) { + ) + { internal string path { get; } = path; internal string relative { get; } = relative; internal string name { get; } = name; diff --git a/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs b/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs index 462690926..e6f21b275 100644 --- a/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs +++ b/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs @@ -109,7 +109,10 @@ public static int Execute( else if (!argument.StartsWith("--", StringComparison.Ordinal) && paths.Count == 0) paths.Add(argument); else - throw new ArgumentException("Usage: verify [engine-root] [--configuration Debug|Release] [--expand-xml] [--dotnet SDK] [--project-graph output.json] [--wiki] [--wiki-target targetId] [--wiki-api inventory.json] [--wiki-examples examples.json] [--wiki-runtime-reference assembly.dll] [--wiki-editor-reference assembly.dll]", nameof(arguments)); + throw new ArgumentException( + "Usage: verify [engine-root] [--configuration Debug|Release] [--expand-xml] [--dotnet SDK] [--project-graph output.json] [--wiki] [--wiki-target targetId] [--wiki-api inventory.json] [--wiki-examples examples.json] [--wiki-runtime-reference assembly.dll] [--wiki-editor-reference assembly.dll]", + nameof(arguments) + ); } if (projectGraphPath is not null && dotnetHost is null) throw new ArgumentException("Evaluated project graph output requires an explicitly selected --dotnet SDK.", nameof(arguments)); @@ -584,7 +587,7 @@ ICollection failures return "src/composition/editor/panels"; if (string.Equals(projectName, "Inno.Editor.Hosting", StringComparison.Ordinal)) return "src/composition/editor/hosting"; - if (string.Equals(projectName, "Inno.Editor.ImGui", StringComparison.Ordinal)) + if (projectName is "Inno.Editor.ImGui" or "Inno.Editor.Workbench" or "Inno.Editor.Assets.ImGui" or "Inno.Editor.Shaders.ImGui" or "Inno.Editor.Graph.ImGui") return "src/composition/editor/presentation"; if (string.Equals(projectName, "Inno.Editor.Core", StringComparison.Ordinal) || string.Equals(projectName, "Inno.Editor.Diagnostics", StringComparison.Ordinal) || diff --git a/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs b/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs new file mode 100644 index 000000000..ca98e2161 --- /dev/null +++ b/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Linq; + +namespace Inno.Tooling.Architecture; +internal static class EditorUiBoundaryValidator +{ + internal static void Validate( + string repositoryRoot, + ICollection failures + ) { + string editorRoot = Path.Combine(repositoryRoot, "src/composition/editor"); + foreach (string path in RepositorySourceInventory.Files(editorRoot, "*.csproj")) + { + string name = Path.GetFileNameWithoutExtension(path); + string relative = Path.GetRelativePath(repositoryRoot, path).Replace('\\', '/'); + bool feature = relative.StartsWith("src/composition/editor/features/", StringComparison.Ordinal); + bool panel = relative.StartsWith("src/composition/editor/panels/", StringComparison.Ordinal); + foreach (XElement reference in XDocument.Load(path).Descendants("ProjectReference")) + { + string dependency = Path.GetFileNameWithoutExtension(((string? )reference.Attribute("Include") ?? "").Replace('\\', '/')); + if (feature + && (dependency.Contains( + "ImGui", + StringComparison.Ordinal + ) + || dependency.StartsWith( + "Inno.Native.", + StringComparison.Ordinal + ) + || dependency.StartsWith( + "Inno.Editor.Panel.", + StringComparison.Ordinal + ) + || dependency is "Inno.Editor.Workbench" or "Inno.Editor.Hosting")) + failures.Add($"{relative}: Editor feature editing must be presentation-independent; remove {dependency}."); + if (panel && dependency.StartsWith("Inno.Editor.Panel.", StringComparison.Ordinal)) + failures.Add($"{relative}: Panels cannot depend on other Panels; move shared behavior to its feature owner ({dependency})."); + if (name != "Inno.Editor.ImGui" && name != "Inno.Editor.Hosting" && dependency.StartsWith("Inno.Native.", StringComparison.Ordinal)) + failures.Add($"{relative}: native UI access belongs to the Editor ImGui adapter, not {name}."); + if (name is "Inno.Editor.Graph" or "Inno.Editor.Graph.ImGui" + && (dependency.Contains( + "Shader", + StringComparison.Ordinal + ) + || dependency.Contains( + "Rendering", + StringComparison.Ordinal + ))) + failures.Add($"{relative}: reusable Graph mechanisms cannot depend on Shader or Rendering semantics."); + } + } + } +} From 61f855c00275d26274e6faedfdab204b11b05a2f Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 08:07:53 +0800 Subject: [PATCH 02/54] fix(editor): guard transient targets and script SDK owners Keep boxed reference-bearing targets weak across selection, focus and snapshots, while retaining known neutral scalar values for host workflows. Reference reusable authoring APIs directly from the script SDK and preserve the view-owned public drop contracts. Validate actual assembly export declarations and compile the complete contract probe from an independent SDK output. Record the final 604 local contract passes, product checks and ownership review. --- AGENTS.md | 2 + .../cli/Inno.Build.Cli/Inno.Build.Cli.csproj | 7 +++- docs/architecture/EDITOR_UI_ARCHITECTURE.md | 6 +++ docs/build/Inno.Build.Cli.md | 2 + docs/editor/Inno.Editor.Core.md | 4 +- .../EDITOR_UI_IMPLEMENTATION_PLAN.md | 4 +- .../EDITOR_UI_ACCEPTANCE_2026_10_27.md | 21 +++++----- .../Runtime/EditorTargetReference.cs | 7 +++- .../EditorRuntimeTests.Selection.cs | 39 +++++++++++++++++++ .../IntegrationBoundaryTests.cs | 31 +++++++++++++++ .../EditorUiBoundaryValidator.cs | 35 ++++++++++++++++- 11 files changed, 142 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e91a80b49..aceffe25f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -326,3 +326,5 @@ public void AAA( - Final delivery must evaluate architecture, organization, state/lifetime cleanliness, abstraction, reuse, visual consistency, extensibility, interaction, Undo/Redo, menu clarity and component granularity individually. Each evaluation must identify actual evidence, limitations and remaining improvements; directory changes and interface counts alone are insufficient. - UI-neutral name formatting and Action/Menu queries must work without a native context or drawing session. Keep pure label formatting in Editor Core; only drawing and native borrows require an active UI scope. - Temporary presentation worlds register their borrowed Core allocator through the existing selection coordinator's routing lease. Restore Edit selection before releasing that lease; failed retirement must preserve its owner. The routing table never becomes a second live-object authority. +- Boxed value types are not automatically neutral: a host struct may contain extension objects. Cross-callback targets retain only known neutral scalars strongly; all other non-identity objects use weak references, including boxed structs. Selection, focus and snapshots must not prolong their referenced objects' lifetime. +- The formal script SDK host references the actual exported API owners directly, including reusable presentation libraries when required. It must not use concrete Panels as transitive dependency carriers for asset, shader or graph authoring APIs. diff --git a/build/cli/Inno.Build.Cli/Inno.Build.Cli.csproj b/build/cli/Inno.Build.Cli/Inno.Build.Cli.csproj index edafd08c6..3bf9a40b8 100644 --- a/build/cli/Inno.Build.Cli/Inno.Build.Cli.csproj +++ b/build/cli/Inno.Build.Cli/Inno.Build.Cli.csproj @@ -45,8 +45,11 @@ - - + + + + + diff --git a/docs/architecture/EDITOR_UI_ARCHITECTURE.md b/docs/architecture/EDITOR_UI_ARCHITECTURE.md index 9330732a2..e29f10996 100644 --- a/docs/architecture/EDITOR_UI_ARCHITECTURE.md +++ b/docs/architecture/EDITOR_UI_ARCHITECTURE.md @@ -52,6 +52,8 @@ src/composition/editor/ 普通菜单、快捷键、工具栏与 command search 消费同一 Action 的 Query/Execute;presentation 不能维护另一份 enabled/checked 业务规则。Action 索引按 active generation 失效。所有跨帧目标用 Core Identity 重新解析,不能保留 Component wrapper 或 collectible Type。 +非 Identity target 只有明确的中立标量可以强持有,其余对象使用弱引用,包括 boxed struct。宿主程序集的值类型仍可能包含插件对象,不能根据 `IsValueType` 或程序集不可回收就假定其内容中立。Selection、focus 与 snapshot 复用同一规则。 + 连续输入在首个有效 sample 打开 EditorEditGesture,release 提交,Escape 回滚。显式 Selection 切换先提交当前输入,准备失败则保持原选择;关闭 owner 或未完成的控件消失时回滚。命令和 reload 边界同样使用已有 Core Events 准备通知。材质/Pipeline 不再自己维护 deferred gesture;SceneEdits 和 asset draft service 都立即记录。 多选包含有序身份和 anchor;visible range 来自同一过滤/排序结果。批量源操作归一化最小 roots,先验证所有 owner,再由既有 History transaction 执行;失败不接受半批结果。共同属性使用 owner serialization context,保持各对象独立的 before 值。 @@ -66,6 +68,10 @@ ShaderEditor 是最大的领域专用 view,其 canvas、预览、菜单和文 EditorUiBoundaryValidator 校验 feature 无 UI/native/Panel 引用、Panel 无相互引用、native 访问集中在 adapter、Graph 无 Rendering/Shader 依赖。公开 API 依赖分组、XML、逻辑脚本导出、Solution 和 Wiki 继续由仓库统一验证器验证。 +正式脚本 SDK 的 Build CLI 直接引用 Assets、Shaders、Graph 等导出 API 的实际 owner,不通过 FileBrowser、ShaderEditor、Hierarchy 或 Inspector Panel 间接携带编译 API。CLI 的这项引用边界同样受正反例约束。 + +Hierarchy 与 Inspector 自己公开的 drop target 协议仍由它们各自声明并被 SDK 直接引用;这些是明确的 UI 扩展契约,不是其他模块 API 的传递载体。校验器解析真实的 assembly export 声明,空清单或注释中的声明都不能充当 owner 证明。 + ## 精确多选分量与 History 查询 diff --git a/docs/build/Inno.Build.Cli.md b/docs/build/Inno.Build.Cli.md index c887df676..24bf22bf5 100644 --- a/docs/build/Inno.Build.Cli.md +++ b/docs/build/Inno.Build.Cli.md @@ -29,6 +29,8 @@ dotnet run --project entry/InnoEngine.Build/InnoEngine.Build.csproj -- verify . SDK 生成的 `Inno.Build.Cli.deps.json` 随开发入口复制。该清单属于 CLI 自己的显式创作闭包,使库宿主和开发入口发现同一组脚本契约;不在编译器中维护领域类型名单。 +Assets、Shaders 与通用 Graph 创作 API 直接引用其 feature 或可复用 presentation 项目,不引用具体 Panel 来间接携带脚本契约。命令行编译无需实例化这些 UI;实际绘制仍由 Editor 的 session 和 Panel 负责。 + CLI 与平台 Editor 组合库显式启用共同的 `InnoExposeRuntimeClosure` SDK 构建规则。清单与实际运行依赖随引用复制,不改变项目的 `PrivateAssets="compile"` 边界,也不扫描输出目录来猜测扩展。 | 命令 | 行为 | diff --git a/docs/editor/Inno.Editor.Core.md b/docs/editor/Inno.Editor.Core.md index 3ac216a91..760c78478 100644 --- a/docs/editor/Inno.Editor.Core.md +++ b/docs/editor/Inno.Editor.Core.md @@ -478,8 +478,8 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 | [`public interface Inno.Editor.Core.IEditorPanelReloadState`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L8) | Optionally migrates presentation-neutral panel state across assembly reloads. | — | — | | [`System.ReadOnlyMemory Inno.Editor.Core.IEditorPanelReloadState.CaptureReloadState()`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L16) | Captures presentation-neutral state without retaining runtime or plugin object references. | An immutable byte payload that can be consumed by the replacement panel generation. | — | | [`void Inno.Editor.Core.IEditorPanelReloadState.RestoreReloadState(System.ReadOnlyMemory state)`](../../src/composition/editor/framework/Inno.Editor.Core/Extensions/IEditorPanelReloadState.cs#L24) | Restores presentation-neutral state captured from the previous panel generation.
`state`: The immutable payload returned by the retiring panel generation. | — | — | -| [`public static class Inno.Editor.Core.EditorDisplayNames`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L7) | Formats readable Editor labels independently from a native UI context or session. | — | — | -| [`public static string Inno.Editor.Core.EditorDisplayNames.Nicify(string name)`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L18) | Converts an identifier into a readable editor label.
`name`: Source identifier. | A label with identifier separators and lower-to-upper transitions spaced; empty when the input is blank. | — | +| [`public static class Inno.Editor.Core.EditorDisplayNames`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L9) | Formats readable Editor labels independently from a native UI context or session. | — | — | +| [`public static string Inno.Editor.Core.EditorDisplayNames.Nicify(string name)`](../../src/composition/editor/framework/Inno.Editor.Core/Presentation/EditorDisplayNames.cs#L20) | Converts an identifier into a readable editor label.
`name`: Source identifier. | A label with identifier separators and lower-to-upper transitions spaced; empty when the input is blank. | — | | [`public sealed class Inno.Editor.Core.EditorReloadCoordinator : Inno.Scripting.Reload.IScriptReloadCoordinator`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L16) | Coordinates weakly registered editor feature migrations around atomic assembly reload sessions. | — | — | | [`public System.IDisposable Inno.Editor.Core.EditorReloadCoordinator.RegisterGuard(Inno.Editor.Core.IEditorReloadGuard guard)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L34) | Registers a weak admission guard without stopping its owner or creating a generation transaction.
`guard`: The owner whose current state determines reload admission. | A caller-owned lease retaining the guard until registration is released. | `System.ArgumentNullException`: The guard is null. | | [`public bool Inno.Editor.Core.EditorReloadCoordinator.CanRequestReload(out string? reason)`](../../src/composition/editor/framework/Inno.Editor.Core/Reloading/EditorReloadCoordinator.cs#L55) | Queries whether every registered owner accepts an explicit reload request.
`reason`: The first blocking reason, or null when the request is allowed. | True when all live guards permit a manual request. | — | diff --git a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md index e0e491a57..ef8898fbd 100644 --- a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md +++ b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md @@ -47,8 +47,10 @@ The final report must assess architecture, readability, state ownership, abstrac ## Closed local regressions -- 553 shared Editor/tooling tests and one real isolated viewport-retirement fact passed. Three further isolated Core/compiler/build facts passed through the corrected SDK test runner. Rendering2D authoring has 42 passing tests. +- 558 shared Editor/tooling tests and one real isolated viewport-retirement fact passed. Three further isolated Core/compiler/build facts passed through the corrected SDK test runner. Rendering2D authoring has 42 passing tests: 604 distinct local cases in total, excluding focused reruns. - Temporary Play identity routing now has an explicit Host lease and retains Missing selections when a domain retires. The real FlappyBird probe verified the same persistent ID in Edit/Play and restoration to the original domain. - Pure label formatting belongs to Editor Core; menu queries require no native drawing context. All engine/plugin callers use the shared formatter. - VSTest discovery errors are no longer treated as successful empty discovery; raw discovery logs are preserved. - Native vendor/cold SDK diagnostics and automatic maintenance-budget deferrals remain visible in their original logs. They were not suppressed. +- Final lifetime review reproduced boxed structs retaining referenced objects through selection/focus/snapshots. Targets now retain only known neutral scalars strongly; all other non-identity objects use weak references. The failing-before regression and complete passing Interactions suite are retained separately. +- The SDK directly references reusable API owners. Its Hierarchy/Inspector contract references remain because they explicitly export four drop targets; a fresh SDK and real script probe verify these public contracts. Non-exporting Panel carriers are rejected by syntax-aware ownership fixtures. diff --git a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md index 18984d4a7..f2cd154a9 100644 --- a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md +++ b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md @@ -21,6 +21,8 @@ The implementation and local correctness checks are complete, including the fina - Rendering2D uses the current session and edit contract. Its package in FlappyBird was produced by the official exporter, never edited as an archive. - Pure identifier formatting moved out of session-bound widgets into `EditorDisplayNames` in Editor Core. Menu metadata can be queried without a native UI context. The old widget member was removed and every engine/plugin caller updated. +- Non-identity targets retain only known neutral scalars strongly. Boxed host structs can contain extension objects, so selection, focus and snapshots hold those targets weakly. A failing GC regression reproduced the retention before the correction; the complete 121-case Interactions suite passed afterward. +- The script SDK references reusable Assets, Shaders and Graph API owners directly. Hierarchy and Inspector remain direct SDK references for their own four public drop-target contracts. A fresh SDK output and a real EditorScripts probe verify these exports; no Panel is used only to carry another module's API. The full current file tree and execution flow are in the architecture document. Solution registration, project reference visibility, scripting exports, English XML and individual Editor Wiki pages were updated together. @@ -30,18 +32,19 @@ Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. | Check | Final result | Evidence | | --- | --- | --- | -| Windows UI source build | Passed; zero compiler warnings/errors | `ui-tests2-build.log`, `contract-tests-build.log`, binlogs | -| Shared UI contracts | 553 passed; 0 failed/skipped | `closed-tests/` TRX and execution summary | -| Isolated contracts | 4 passed; 0 failed/skipped | One native retirement case in `closed-tests/`; three Core/compiler/build cases in `isolation-final-tests/` | -| Rendering2D authoring | 42 passed, 0 failed/skipped | `rendering2d-final-tests/results/authoring.trx` | +| Windows UI source build | Passed; zero compiler warnings/errors | `ui-boxed-build.log` and binlog | +| Shared UI contracts | 558 passed; 0 failed/skipped | `boxed-suite/` TRX, with its earlier 150 architecture cases replaced by the final 152 in `architecture-sdk-owner-tests/` | +| Isolated contracts | 4 passed; 0 failed/skipped | One native retirement case in `boxed-suite/`; three unchanged Core/compiler/build cases in `isolation-final-tests/` | +| Rendering2D authoring | 42 passed, 0 failed/skipped against the fresh SDK output | `rendering2d-sdk-owner-tests/results/authoring.trx` | | Official package export | Passed | `rendering2d-export-accepted.log` | | Architecture and public XML | Passed; final Wiki graph also checks evaluated ownership | `architecture-final.log`, `xml-domain.log`; new public source compiles with XML warnings as errors | -| SDK Wiki graph, signatures, links and examples | Passed; 241 project ownership evaluations, 34,493 API entries and 197 examples | `wiki-final-accepted.log`, `wiki-final-api.json`, `wiki-final-examples.json` | -| Editor Debug and Release | Passed; zero errors, one automatic maintenance-budget warning each | `editor-debug-final.*`, `editor-release-final.*`; cold Release vendor warnings remain in `editor-release.log` | -| FlappyBird product | Debug: 15,000 frames, 300 Play frames and Edit return; Release: 1,000 frames; D3D12 and Vulkan: 1,000 frames each; all orderly exits | `flappy-debug-closed*`, `flappy-Release-d3d11.log`, `flappy-Debug-d3d12.log`, `flappy-Debug-vulkan.log` | +| SDK Wiki graph, signatures, links and examples | Passed; 241 project ownership evaluations, 34,493 API entries and 197 examples | `wiki-sdk-owner.log`, `wiki-sdk-owner-api.json`, `wiki-sdk-owner-examples.json` | +| Editor Debug and Release | Passed; zero warnings/errors | `editor-Debug-boxed.*`, `editor-Release-boxed.*`; earlier maintenance deferrals and cold vendor warnings remain in their original logs | +| Fresh formal SDK and public drop contracts | Passed; no old output directory required | `sdk-actual-owners.*`, `build-entry-sdk-owners.*`, `sdk-api-probe-after.log` | +| FlappyBird product | Debug: 15,000 frames; Release: 5,000 frames; both complete 300 Play frames and return to Edit. D3D12 and Vulkan: 1,000 renderer frames each; all orderly exits | `flappy-Debug-boxed*`, `flappy-Release-boxed*`, `flappy-Debug-d3d12.log`, `flappy-Debug-vulkan.log` | | GitHub CI | Tracked separately on the pull request for this branch | Its exact head revision, run links and final results accompany delivery; this report does not claim an unexecuted remote pass | -Initial failures remain in the evidence owner: the wrong native product context, a missing direct test annotation dependency, fixtures missing the new session/presentation boundary, and an EventHub whose dispatcher was not retained. Their corrective runs passed; these failures are not labeled as upstream BGCS defects. The full SourceDebug run interrupted during contract changes is not counted as a pass. +Initial failures remain in the evidence owner: the wrong native product context, a missing direct test annotation dependency, fixtures missing the new session/presentation boundary, an EventHub whose dispatcher was not retained, and boxed target retention (`boxed-before/`). The SDK probe also rejected an exploratory removal of actual Panel contract owners (`sdk-api-probe-before.log`); their required direct references were restored before delivery. Their corrective runs passed; these failures are not labeled as upstream BGCS defects. The full SourceDebug run interrupted during contract changes is not counted as a pass. The 604 distinct local passes are 558 shared contracts, four isolated contracts and 42 Rendering2D cases; focused reruns are not added again. ## Performance and resource ownership @@ -83,7 +86,7 @@ These assessments describe verified engineering properties. “Enterprise-grade ## Unexecuted or limited acceptance -- No macOS device is available; Metal and real macOS Editor behavior are unexecuted. +- No local macOS device is available; local Metal and interactive macOS acceptance are unexecuted. Remote runner results are tracked separately and do not certify manual device journeys. - Computer Use captured the real FlappyBird Scene/Game view, but window activation failed twice. Following the tool's recovery limit, further manual input was stopped. Mouse interaction was not certified; the owned visual session was terminated. Separate bounded runs verify orderly exit. - Full accessibility, all focus/DPI combinations, audible audio quality, extended usability sessions and long-duration GPU/GC behavior are not certified by native layout tests or bounded smoke runs. - Existing engine issues outside this UI plan remain independently tracked. This refactor cannot establish that the entire engine is perfect. diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs index b890da532..790e2a766 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorTargetReference.cs @@ -29,8 +29,13 @@ internal static EditorTargetReference Capture(object? value) return new EditorTargetReference(runtime, target.identity.persistentId, null, null); } - if (value is null or string || value.GetType().IsValueType && !value.GetType().Assembly.IsCollectible) + if (value is null or string or decimal or Guid or DateTime or DateTimeOffset + or DateOnly or TimeOnly or TimeSpan or Half or Int128 or UInt128) return new EditorTargetReference(null, Guid.Empty, value, null); + Type type = value.GetType(); + if (type.IsPrimitive || type.IsEnum && !type.IsCollectible) + return new EditorTargetReference(null, Guid.Empty, value, null); + // A host-defined struct can still contain extension objects; boxing does not make it neutral. return new EditorTargetReference(null, Guid.Empty, null, new WeakReference(value)); } diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs index 19de0c708..e8f57e9fb 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs @@ -9,6 +9,45 @@ namespace Inno.Editor.Interactions.Tests; public sealed partial class EditorRuntimeTests { + [Fact] + public void BoxedTargetsDoNotKeepReferencedObjectsAliveThroughSelectionFocusOrSnapshots() + { + WeakReference reference = SelectBoxedReferenceTarget(); + EditorSelectionSnapshot snapshot = m_runtime.interactions.selection.Capture(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(reference.IsAlive); + Assert.Null(m_runtime.interactions.selection.selectedTarget); + Assert.Null(m_runtime.interactions.focusedTarget); + Assert.Equal(0, snapshot.CopyTargets(m_runtime.interactions, new List())); + GC.KeepAlive(snapshot); + } + + [Fact] + public void NeutralScalarSelectionRemainsAvailableWithoutAnotherOwner() + { + Guid value = Guid.NewGuid(); + SelectScalarTarget(value); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.Equal(value, Assert.IsType(m_runtime.interactions.selection.selectedTarget)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void SelectScalarTarget(Guid value) => m_runtime.interactions.SetSelection(value); + + [MethodImpl(MethodImplOptions.NoInlining)] + private WeakReference SelectBoxedReferenceTarget() + { + var payload = new object(); + object target = new KeyValuePair("payload", payload); + m_runtime.interactions.SetSelection(target); + m_runtime.interactions.For("tests/boxed-target", target).Focus(); + return new WeakReference(payload); + } + [Fact] public void RejectedDomainRetirementOnAnotherThreadPreservesTheRoutingLease() { diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs index f749d2482..379cfda2a 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/IntegrationBoundaryTests.cs @@ -56,4 +56,35 @@ string references Directory.Delete(root, recursive: true); } } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ScriptSdkReferencesPanelContractsOnlyWhenThePanelActuallyExportsThem(bool exportsContract) + { + string root = Path.Combine(Path.GetTempPath(), "InnoSdkOwnerTests", Guid.NewGuid().ToString("N")); + try + { + string host = Path.Combine(root, "build/cli/Inno.Build.Cli"); + string panel = Path.Combine(root, "src/composition/editor/panels/Inno.Editor.Panel.Probe"); + Directory.CreateDirectory(host); + Directory.CreateDirectory(Path.Combine(panel, "Properties")); + File.WriteAllText(Path.Combine(root, "InnoEngine.sln"), "Microsoft Visual Studio Solution File, Format Version 12.00"); + string panelProject = Path.Combine(panel, "Inno.Editor.Panel.Probe.csproj"); + File.WriteAllText(panelProject, "net10.0"); + string include = Path.GetRelativePath(host, panelProject).Replace('\\', '/'); + File.WriteAllText(Path.Combine(host, "Inno.Build.Cli.csproj"), + "net10.0"); + const string C_EXPORT_DECLARATION = "[assembly: Inno.Scripting.Api.ScriptingApiExport(typeof(System.String), Inno.Scripting.Api.ScriptingApiScope.Editor)]"; + File.WriteAllText(Path.Combine(panel, "Properties/ScriptingApi.cs"), exportsContract ? C_EXPORT_DECLARATION : "// " + C_EXPORT_DECLARATION); + (_, string report) = ArchitectureCommandFixture.Run(root); + Assert.Equal(!exportsContract, report.Contains("script SDK host must reference API owners", StringComparison.Ordinal)); + } + finally + { + if (Directory.Exists(root)) + Directory.Delete(root, recursive: true); + } + } } diff --git a/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs b/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs index ca98e2161..bd8ac4808 100644 --- a/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs +++ b/tools/Inno.Tooling.Architecture/EditorUiBoundaryValidator.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Xml.Linq; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Inno.Tooling.Architecture; internal static class EditorUiBoundaryValidator @@ -11,7 +13,11 @@ internal static void Validate( ICollection failures ) { string editorRoot = Path.Combine(repositoryRoot, "src/composition/editor"); - foreach (string path in RepositorySourceInventory.Files(editorRoot, "*.csproj")) + var projects = new List(RepositorySourceInventory.Files(editorRoot, "*.csproj")); + string sdkHost = Path.Combine(repositoryRoot, "build/cli/Inno.Build.Cli/Inno.Build.Cli.csproj"); + if (File.Exists(sdkHost)) + projects.Add(sdkHost); + foreach (string path in projects) { string name = Path.GetFileNameWithoutExtension(path); string relative = Path.GetRelativePath(repositoryRoot, path).Replace('\\', '/'); @@ -37,6 +43,13 @@ ICollection failures failures.Add($"{relative}: Editor feature editing must be presentation-independent; remove {dependency}."); if (panel && dependency.StartsWith("Inno.Editor.Panel.", StringComparison.Ordinal)) failures.Add($"{relative}: Panels cannot depend on other Panels; move shared behavior to its feature owner ({dependency})."); + if (name == "Inno.Build.Cli" && dependency.StartsWith("Inno.Editor.Panel.", StringComparison.Ordinal)) + { + string include = (string?)reference.Attribute("Include") ?? string.Empty; + string referencedProject = Path.GetFullPath(include.Replace('\\', '/'), Path.GetDirectoryName(path)!); + if (!DeclaresScriptingApi(referencedProject)) + failures.Add($"{relative}: the script SDK host must reference API owners instead of transitive Panel carriers ({dependency})."); + } if (name != "Inno.Editor.ImGui" && name != "Inno.Editor.Hosting" && dependency.StartsWith("Inno.Native.", StringComparison.Ordinal)) failures.Add($"{relative}: native UI access belongs to the Editor ImGui adapter, not {name}."); if (name is "Inno.Editor.Graph" or "Inno.Editor.Graph.ImGui" @@ -52,4 +65,24 @@ ICollection failures } } } + + private static bool DeclaresScriptingApi(string project) + { + string manifest = Path.Combine(Path.GetDirectoryName(project)!, "Properties/ScriptingApi.cs"); + if (!File.Exists(manifest)) + return false; + var syntax = CSharpSyntaxTree.ParseText( + File.ReadAllText(manifest), + new CSharpParseOptions(LanguageVersion.CSharp14) + ).GetCompilationUnitRoot(); + foreach (AttributeListSyntax list in syntax.AttributeLists) + { + if (list.Target?.Identifier.ValueText != "assembly") + continue; + foreach (AttributeSyntax attribute in list.Attributes) + if (attribute.Name.GetLastToken().ValueText is "ScriptingApiExport" or "ScriptingApiExportAttribute") + return true; + } + return false; + } } From 2dc37114b7c9b5137e866efc057a66e0dfd72046 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 11:26:27 +0800 Subject: [PATCH 03/54] test(editor): align export shutdown fixture with session ownership --- AGENTS.md | 1 + .../EDITOR_UI_IMPLEMENTATION_PLAN.md | 3 ++- .../EDITOR_UI_ACCEPTANCE_2026_10_27.md | 5 +++- .../Inno.Build.Tests/BuildPipelineTests.cs | 26 ++++++++++++++++--- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index aceffe25f..b088f81d9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -328,3 +328,4 @@ public void AAA( - Temporary presentation worlds register their borrowed Core allocator through the existing selection coordinator's routing lease. Restore Edit selection before releasing that lease; failed retirement must preserve its owner. The routing table never becomes a second live-object authority. - Boxed value types are not automatically neutral: a host struct may contain extension objects. Cross-callback targets retain only known neutral scalars strongly; all other non-identity objects use weak references, including boxed structs. Selection, focus and snapshots must not prolong their referenced objects' lifetime. - The formal script SDK host references the actual exported API owners directly, including reusable presentation libraries when required. It must not use concrete Panels as transitive dependency carriers for asset, shader or graph authoring APIs. +- Native presentation fixtures must create and enter an `EditorUiSession`, explicitly compose their feature/presentation owners, and resolve the exact service under test. Asynchronous shutdown fixtures must retry the public Pending retirement protocol until owned cleanup completes; they cannot rely on continuation timing or remove resource-release assertions. diff --git a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md index ef8898fbd..df2a32fac 100644 --- a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md +++ b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md @@ -47,10 +47,11 @@ The final report must assess architecture, readability, state ownership, abstrac ## Closed local regressions -- 558 shared Editor/tooling tests and one real isolated viewport-retirement fact passed. Three further isolated Core/compiler/build facts passed through the corrected SDK test runner. Rendering2D authoring has 42 passing tests: 604 distinct local cases in total, excluding focused reruns. +- 558 shared Editor/tooling tests and one real isolated viewport-retirement fact passed. Three further isolated Core/compiler/build facts passed through the corrected SDK test runner. Rendering2D authoring has 42 passing tests. The corrected export shutdown integration case passed three times: 605 distinct local cases in total, excluding focused reruns. - Temporary Play identity routing now has an explicit Host lease and retains Missing selections when a domain retires. The real FlappyBird probe verified the same persistent ID in Edit/Play and restoration to the original domain. - Pure label formatting belongs to Editor Core; menu queries require no native drawing context. All engine/plugin callers use the shared formatter. - VSTest discovery errors are no longer treated as successful empty discovery; raw discovery logs are preserved. - Native vendor/cold SDK diagnostics and automatic maintenance-budget deferrals remain visible in their original logs. They were not suppressed. - Final lifetime review reproduced boxed structs retaining referenced objects through selection/focus/snapshots. Targets now retain only known neutral scalars strongly; all other non-identity objects use weak references. The failing-before regression and complete passing Interactions suite are retained separately. - The SDK directly references reusable API owners. Its Hierarchy/Inspector contract references remain because they explicitly export four drop targets; a fresh SDK and real script probe verify these public contracts. Non-exporting Panel carriers are rejected by syntax-aware ownership fixtures. +- Complete Windows/macOS CI at `61f855c0` exposed one remaining Build fixture using the old drawing host. Its real UI session, typed export owner and asynchronous retirement retry are now explicit; original assertions remain. Both platforms' other stages passed. Final CI must be evaluated on the replacement commit. diff --git a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md index f2cd154a9..335c03c6f 100644 --- a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md +++ b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md @@ -36,6 +36,7 @@ Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. | Shared UI contracts | 558 passed; 0 failed/skipped | `boxed-suite/` TRX, with its earlier 150 architecture cases replaced by the final 152 in `architecture-sdk-owner-tests/` | | Isolated contracts | 4 passed; 0 failed/skipped | One native retirement case in `boxed-suite/`; three unchanged Core/compiler/build cases in `isolation-final-tests/` | | Rendering2D authoring | 42 passed, 0 failed/skipped against the fresh SDK output | `rendering2d-sdk-owner-tests/results/authoring.trx` | +| Export shutdown integration | Passed three consecutive runs; counted as one distinct case | `ci-export-retirement/export-retirement.trx`, `ci-export-retirement-repeat/` | | Official package export | Passed | `rendering2d-export-accepted.log` | | Architecture and public XML | Passed; final Wiki graph also checks evaluated ownership | `architecture-final.log`, `xml-domain.log`; new public source compiles with XML warnings as errors | | SDK Wiki graph, signatures, links and examples | Passed; 241 project ownership evaluations, 34,493 API entries and 197 examples | `wiki-sdk-owner.log`, `wiki-sdk-owner-api.json`, `wiki-sdk-owner-examples.json` | @@ -44,7 +45,7 @@ Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. | FlappyBird product | Debug: 15,000 frames; Release: 5,000 frames; both complete 300 Play frames and return to Edit. D3D12 and Vulkan: 1,000 renderer frames each; all orderly exits | `flappy-Debug-boxed*`, `flappy-Release-boxed*`, `flappy-Debug-d3d12.log`, `flappy-Debug-vulkan.log` | | GitHub CI | Tracked separately on the pull request for this branch | Its exact head revision, run links and final results accompany delivery; this report does not claim an unexecuted remote pass | -Initial failures remain in the evidence owner: the wrong native product context, a missing direct test annotation dependency, fixtures missing the new session/presentation boundary, an EventHub whose dispatcher was not retained, and boxed target retention (`boxed-before/`). The SDK probe also rejected an exploratory removal of actual Panel contract owners (`sdk-api-probe-before.log`); their required direct references were restored before delivery. Their corrective runs passed; these failures are not labeled as upstream BGCS defects. The full SourceDebug run interrupted during contract changes is not counted as a pass. The 604 distinct local passes are 558 shared contracts, four isolated contracts and 42 Rendering2D cases; focused reruns are not added again. +Initial failures remain in the evidence owner: the wrong native product context, a missing direct test annotation dependency, fixtures missing the new session/presentation boundary, an EventHub whose dispatcher was not retained, and boxed target retention (`boxed-before/`). The SDK probe also rejected an exploratory removal of actual Panel contract owners (`sdk-api-probe-before.log`); their required direct references were restored before delivery. Their corrective runs passed; these failures are not labeled as upstream BGCS defects. The full SourceDebug run interrupted during contract changes is not counted as a pass. The 605 distinct local passes are 558 shared contracts, four isolated contracts, 42 Rendering2D cases and the export shutdown integration case; focused reruns are not added again. ## Performance and resource ownership @@ -113,6 +114,8 @@ Explicit owner-aware cache maintenance reclaimed 20,782,799,012 logical bytes an The full local SourceDebug build interrupted during contract changes remains uncounted. The final focused UI graph, three additional isolation projects and standard Debug/Release product entries passed; the complete SourceDebug matrix belongs to remote CI. No test filter excludes an unresolved UI failure. +The first complete remote matrix at `61f855c0` passed both platforms' Debug/Release products, renderer smoke, BGCS identity, ten hot builds, Solution, architecture, Support Pack and Wiki. Its sole failing fact on both platforms was `EditorShutdownDrainsSupportPackCleanupBeforeReleasingExportServices`: the Build fixture drew a modal without the new UI session and still selected an arbitrary module rather than the export service. The fixture now explicitly composes both owners, enters the session, resolves `EditorExportService` and retries the existing Pending retirement protocol after releasing the fake cleanup barrier. All original UI, cancellation, release and generation-readiness assertions remain. The production implementation and BGCS were not changed for this correction. The replacement commit's complete CI result is tracked on the PR, not inferred from the earlier head. + ## Companion source commits diff --git a/tests/build/Inno.Build.Tests/BuildPipelineTests.cs b/tests/build/Inno.Build.Tests/BuildPipelineTests.cs index e8b472ef8..5b32b046f 100644 --- a/tests/build/Inno.Build.Tests/BuildPipelineTests.cs +++ b/tests/build/Inno.Build.Tests/BuildPipelineTests.cs @@ -25,6 +25,7 @@ using Inno.Core.Settings; using Inno.Extensibility.Types; using Inno.Editor.Core; +using Inno.Editor.Exporting; using Inno.Editor.Interactions; using Inno.Native.ImGui; using Inno.Plugins.Authoring; @@ -693,7 +694,10 @@ public unsafe void EditorShutdownDrainsSupportPackCleanupBeforeReleasingExportSe var defaults = BuildSettings.CreateDefault("Test Game", "Scenes/Startup.iscene", new BuildTargetId("macos-arm64")); var buildSettings = new BuildSettingsStore( Path.Combine(m_projectRoot, "Settings.Build.inno"), m_engine.serialization, defaults); - m_engine.modules.Register("Tests.Exporting", [Assembly.Load(new AssemblyName("Inno.Editor.Exporting"))]); + m_engine.modules.Register("Tests.Exporting", [ + typeof(EditorExportService).Assembly, + Assembly.Load(new AssemblyName("Inno.Editor.Workbench")) + ]); var context = new EditorContext(m_projectRoot, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); using var runtime = new EditorInteractionRuntime( context, m_engine.types, m_engine.logs, [pipeline, buildSettings, m_settings]); @@ -704,6 +708,8 @@ public unsafe void EditorShutdownDrainsSupportPackCleanupBeforeReleasingExportSe ImGuiNativeContext* imgui = NativeImGui.Raw.CreateContext(null); try { + using var uiSession = new Inno.Editor.ImGui.EditorUiSession(); + using IDisposable uiScope = uiSession.Enter(); ImGuiIO* io = NativeImGui.Raw.GetIO(); io->DisplaySize = new ImVec2C { X = 1200f, Y = 900f }; io->DeltaTime = 1f / 60f; @@ -737,14 +743,28 @@ public unsafe void EditorShutdownDrainsSupportPackCleanupBeforeReleasingExportSe Assert.True(plugin.TryGetPresentation(out EditorModalExtension.Presentation pluginPresentation)); Assert.False(pluginPresentation.isVisible); - Assert.True(runtime.interactions.TryGetModule(out EditorModule? module)); + Assert.True(runtime.interactions.TryGetModule(out EditorExportService? module)); Assert.NotNull(module); Assert.Equal("Inno.Editor.Exporting", module.GetType().Assembly.GetName().Name); Assert.Throws(() => module.Stop(context)); Assert.True(provisioner.canceled.Task.IsCompletedSuccessfully); Assert.False(provisioner.completed); provisioner.cleanup.SetResult(); - runtime.Dispose(); + var retirement = System.Diagnostics.Stopwatch.StartNew(); + while (true) + { + try + { + runtime.Dispose(); + break; + } + catch (Exception pending) when (RetirementPendingException.Find(pending) is not null) + { + if (retirement.Elapsed >= TimeSpan.FromSeconds(5)) + throw; + Thread.Sleep(1); + } + } Assert.True(provisioner.completed); m_engine.generations.EnsureReady("verify export retirement drained its services"); } From 30d18489fbf68763229cd8ed35a7005f543019ad Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 18:17:28 +0800 Subject: [PATCH 04/54] docs(editor): reconcile UI acceptance with the approved plan --- AGENTS.md | 1 + docs/architecture/CURRENT_ACCEPTANCE.md | 6 +- docs/architecture/CURRENT_ISSUES.md | 11 +- .../EDITOR_UI_IMPLEMENTATION_PLAN.md | 11 +- .../EDITOR_UI_ACCEPTANCE_2026_10_27.md | 4 +- .../EDITOR_UI_PLAN_REVIEW_2026_10_27.md | 168 ++++++++++++++++++ 6 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md diff --git a/AGENTS.md b/AGENTS.md index b088f81d9..5ea25f9bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -324,6 +324,7 @@ public void AAA( - Continuous Inspector and viewport edits use one `EditorEditGesture` over `EditorInteractions.history`. Each sample records immediately; Escape rolls back the same transaction. Save, commands and generation changes finish the gesture before their own operation. No feature may delay History publication behind a separate UI-active flag. - Selection, deferred commands and cross-frame tools resolve identities through Core Identity. Batch operations validate the complete selection, normalize overlapping roots and roll back atomically. Mixed, Missing, read-only and unassigned states remain distinct. - Final delivery must evaluate architecture, organization, state/lifetime cleanliness, abstraction, reuse, visual consistency, extensibility, interaction, Undo/Redo, menu clarity and component granularity individually. Each evaluation must identify actual evidence, limitations and remaining improvements; directory changes and interface counts alone are insufficient. +- Reassess completion against the original approved plan, including every functional and quantitative acceptance requirement. Separate missing implementation from missing evidence; green CI and focused benchmarks cannot establish untested scale, visual, reload or lifecycle guarantees. Correct stale completion statements and issue ledgers when later review finds a gap. - UI-neutral name formatting and Action/Menu queries must work without a native context or drawing session. Keep pure label formatting in Editor Core; only drawing and native borrows require an active UI scope. - Temporary presentation worlds register their borrowed Core allocator through the existing selection coordinator's routing lease. Restore Edit selection before releasing that lease; failed retirement must preserve its owner. The routing table never becomes a second live-object authority. - Boxed value types are not automatically neutral: a host struct may contain extension objects. Cross-callback targets retain only known neutral scalars strongly; all other non-identity objects use weak references, including boxed structs. Selection, focus and snapshots must not prolong their referenced objects' lifetime. diff --git a/docs/architecture/CURRENT_ACCEPTANCE.md b/docs/architecture/CURRENT_ACCEPTANCE.md index de6ddf729..074d87e5a 100644 --- a/docs/architecture/CURRENT_ACCEPTANCE.md +++ b/docs/architecture/CURRENT_ACCEPTANCE.md @@ -4,6 +4,8 @@ 当前 Editor UI 的分层、Session、Selection、History、插件调用方及逐项质量评价见[Editor UI 验收](../../records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md)和[当前 UI 架构](EDITOR_UI_ARCHITECTURE.md)。该报告分别列出自动验证、产品运行与未执行的人工/设备检查,不将此前数量累加为本次结果。 +**原计划尚未全部完成。** [逐项复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)确认菜单索引、Graph 抽取、Console/Stats 功能及规模/生命周期验收仍有缺口,PR #26 保持 Draft。源码 `2dc37114` 的 6/6 CI checks 已通过,包含真实 macOS ARM64 自动测试;不能继续把所有 Mac 自动验收说成未执行,也不能把 CI 通过等同于全部 UI 计划完成。本次另有 121 项 Interactions 复跑通过。 + 当前依赖通过标准 `.csproj` / `PackageReference` 和 SDK Restore 解析,源码不保存独立依赖锁清单;本轮范围与证据见[标准 NuGet 还原验收](../../records/architecture/acceptance/STANDARD_NUGET_RESTORE_ACCEPTANCE_2026_10_26.md)。基线保持 .NET SDK 10.0.402、C# 14、BGCS 4.1.2。 此前 BGCS 消费与三项优先修复的独立证据见[BGCS 4.1.2 消费验收](../../records/architecture/acceptance/BGCS412_CONSUMER_ACCEPTANCE_2026_10_25.md)及[专项报告](../../records/architecture/acceptance/PRIORITY_FIX_ACCEPTANCE_2026_10_25.md)。历史通过数量不能当作当前源码已重跑。 @@ -28,9 +30,9 @@ 本轮构建专项合计 74 项通过,0 失败 / 跳过,不重复计入此前 Coroutine / Runtime 的 142 项。本次发现的旧 package content 与新 SDK DLL 混装由 owned Restore / compilation 求值隔离修复;初始失败、哈希证明及最终复验见报告。 -## 尚未完成的矩阵与问题 +## 此前 BGCS 专项的矩阵与问题 -- macOS / Metal 无设备,未执行。 +- 该次 BGCS 专项没有本地 macOS / Metal 设备验收;后续 Editor 的 Mac 自动 CI 结果见上方最新复审,人工设备检查仍未执行。 - Windows Player 六条有界图形路径已通过;Editor Debug/Release、完整视觉、焦点、DPI、音频及长期运行矩阵未在本轮重跑。 - 示例产品已观察存储读取,尚未触发新的最高分写入,不能宣称分数跨启动存储已验收。 - 非致命 console 资源诊断仍待归因;自动维护预算、热构建成本及其他审计问题继续开放,均按报告链接进入 issue。 diff --git a/docs/architecture/CURRENT_ISSUES.md b/docs/architecture/CURRENT_ISSUES.md index 7a71ad5f9..f81dcdd36 100644 --- a/docs/architecture/CURRENT_ISSUES.md +++ b/docs/architecture/CURRENT_ISSUES.md @@ -6,6 +6,14 @@ 最新全仓深审见[四仓库核心验收报告](../../records/architecture/acceptance/FULL_ENGINE_AUDIT_2026_10_24.md)。正式架构门禁通过不表示资源清理、错误原子性和热路径成本都已闭合。 +## 当前 Editor UI 计划缺口 + +[2026-10-27 原计划复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)是本分支 UI 完成状态的最新依据。菜单/快捷键 generation 索引、通用 Graph 节点与框选呈现、Console 搜索、Stats 趋势与快照复用尚未完整实现;键盘旅程、10k/2k 性能、100 次开关和 10 次 reload 验收不完整。全部 CI 通过不能关闭这些条目;PR #26 保持 Draft。 + +F03 / [#13](https://github.com/Wolfiora/InnoEngine/issues/13) 已在本分支修复:History 先验证栈顶 ID 再 Pop,三种非 LIFO 拒绝回归在最终 Windows/macOS CI 通过,本次 121 项 Interactions 复跑也通过。等待 PR 合并后关闭 issue,不再把当前分支描述为仍在校验前弹栈。 + +F09 / [#16](https://github.com/Wolfiora/InnoEngine/issues/16) 的固定默认 assets 读取已在当前源码及 master 中移除;测试读取 SDK Restore 实际返回的 ProjectAssetsFile。原 issue 的 package/load mismatch 等完整负向矩阵本次未重新证明,暂不将根因修正扩大为全部验收完成。 + ## 本轮已修复并验证 F02/F08 已在 Core Coroutine owner 内统一修复:嵌套枚举器逆序释放,完成/停止/失败共用退休流程;重入停止失效 handle 后在回调返回安全点清理,不再改坏活动遍历。Debug/Release 各 40 项回归通过,Runtime 62 项通过。异常明确汇总,未增加第二套调度器或事件系统。 @@ -25,10 +33,9 @@ B02 / F09 的当前处理是使用标准 `.csproj` 依赖还原,不再维护 | R02 | Runtime coroutine 接入 | 私有 scheduler 只有创建、Tick、stop 和 Dispose,当前没有 admission 路径;需落地最小真实契约或删除未使用状态,不能宣称已集成。 | | T01/T02 | Browser 验收覆盖 | 非致命 console 404 尚未归因;最高分跨启动写入没有在有界游戏运行中触发,已各自提交 issue。 | | B01 | Native 构建诊断 | Windows native 源码编码告警仍需按组件 owner 处理;B02 的依赖还原范围独立验收。 | -| F03 | History 拒绝操作的原子性 | 非 LIFO Commit 在校验 ID 前弹栈,public handler 反例确认内层修改无法随后回滚/Undo。不能用正常顺序用例通过关闭此项。 | | F04/F05 | 插件 UI / 2D 热路径 | UI 插件在纹理 GPU 命中前按 draw 完整复制像素;2D 模型的非空 viewContent 使完整帧缓存持续绕过。见全仓报告的分配实测和调用链,尚未修复。 | | F06/F07 | 热构建与缓存维护成本 | 暖 Native 准备反复读取约 4.5 GB;当前 5 秒 inventory 超时,30 秒 dry-run 可完成。内容/owner 正确性协议已存在,但没有以此证明日常维护必然收敛或性能足够。 | -| O01 | macOS / Metal | 历史 revision 的两条 CI 在单线程 Metal surface 退休用例超时,根因未确认。本轮没有 Mac,不能关闭或直接归责 BGCS。 | +| O01 | macOS / Metal | 历史 revision 的超时保留原记录;`2dc37114` 的 Mac 自动 CI 已通过。Metal refcount warning 仍存在且 master 也可见,根因未确认;人工 Mac 交互未执行,不能宣称无泄漏或归责 BGCS。 | | O02 | 完整构建性能 | 本轮有当前热构建采样及同 .NET 10 的路径解析实现对照,但没有完整改造前的空闲、同输入五次 Editor 基线。历史高负载耗时不作有效对照,不宣称整体提速或已排除全部回退。 | | O06 | XML / Wiki 语义 | 符号、签名和示例门禁不证明全部历史注释足够具体;部分历史参数说明仍需补充实际约束和失败语义。新增构建 API 的所有权、失败与生命周期单独说明。 | diff --git a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md index df2a32fac..fa9161ed1 100644 --- a/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md +++ b/records/architecture/EDITOR_UI_IMPLEMENTATION_PLAN.md @@ -2,6 +2,8 @@ This ledger tracks the approved Editor UI plan against the current `editor-ui` branch. It is an implementation record, not acceptance evidence. +Status correction: the [original-plan review](acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md) found incomplete implementation and acceptance requirements. The branch is not fully complete; PR #26 remains a draft. Previous stage labels described implemented subsets, not every requirement in the approved plan. + Baseline: `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`. Evidence owner: `D:/InnoEngine-Verification/editor-ui-2026-10-27`. ## Required outcomes @@ -24,12 +26,12 @@ Baseline: `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`. Evidence owner: `D:/InnoEn | --- | --- | --- | | Baseline and ownership | Recorded | External baseline inventory and SDK information | | History atomicity | Implemented; local regressions passed | LIFO rejection, shared gestures, immediate draft records and generation-only queries | -| Domain and presentation boundaries | Implemented | Headless features; Assets/Shaders/Graph presentation; no Panel references | -| UI session and styles | Implemented | Session/owner state, pointer-free facade, native borrow guards; 49 native tests passed | +| Domain and presentation boundaries | Main boundaries implemented; graph extraction partial | Headless features and no Panel references; generic node/hit/box selection presentation remains incomplete | +| UI session and styles | Session implemented; style and lifetime acceptance partial | Session/owner state and native borrow guards passed focused tests; domain metrics and full lifecycle matrix remain | | Selection, gestures and commands | Implemented; local regressions passed | Ordered identities, mixed values, precise scalar components and atomic batch operations | -| Panel integration | Implemented | Inspector, Hierarchy, File Browser, SceneView, ShaderEditor and Workbench | +| Panel integration | Partial | Core views integrated; Console search, keyboard journey proof and Stats trends remain incomplete | | Architecture, scripting and documentation | Passed locally | 241 SDK projects evaluated; current API mappings synchronized; final XML formatting, API signatures and compiled Wiki examples passed | -| Product, performance and final acceptance | Local product checks passed | Final Debug/Release builds, 15,000-frame FlappyBird run, 300 Play frames and renderer matrix; isolated UTF-8 comparison recorded | +| Product, performance and final acceptance | Bounded products passed; full plan acceptance incomplete | Final Debug/Release and FlappyBird checks passed; 10k/2k UI performance, 100-cycle, 10-reload and complete visual matrix are not established by the UTF-8 microbenchmark | ## Acceptance rules @@ -55,3 +57,4 @@ The final report must assess architecture, readability, state ownership, abstrac - Final lifetime review reproduced boxed structs retaining referenced objects through selection/focus/snapshots. Targets now retain only known neutral scalars strongly; all other non-identity objects use weak references. The failing-before regression and complete passing Interactions suite are retained separately. - The SDK directly references reusable API owners. Its Hierarchy/Inspector contract references remain because they explicitly export four drop targets; a fresh SDK and real script probe verify these public contracts. Non-exporting Panel carriers are rejected by syntax-aware ownership fixtures. - Complete Windows/macOS CI at `61f855c0` exposed one remaining Build fixture using the old drawing host. Its real UI session, typed export owner and asynchronous retirement retry are now explicit; original assertions remain. Both platforms' other stages passed. Final CI must be evaluated on the replacement commit. +- Replacement commit `2dc37114` passed all six checks, including complete Windows/macOS suites. The 2026-10-27 plan review additionally rebuilt and ran 121 Interactions tests successfully. These passes do not close the implementation/acceptance gaps listed above. diff --git a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md index 335c03c6f..00d47fc35 100644 --- a/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md +++ b/records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md @@ -6,7 +6,9 @@ This report covers the approved Editor UI refactor on `editor-ui`, starting at `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`, together with the Rendering2D authoring caller updates and the officially exported FlappyBird package. It does not certify the entire engine, every platform or every interaction as perfect. -The implementation and local correctness checks are complete, including the final SDK Wiki gate. Remote CI is tracked on the pull request for the committed branch; local results do not substitute for its complete matrix. +The implemented subset passed its local correctness checks, including the final SDK Wiki gate. This is not completion of every requirement in the approved plan. The [plan-by-plan review](EDITOR_UI_PLAN_REVIEW_2026_10_27.md) supersedes the earlier completion assessment: menu indexing, generic graph presentation, Console/Stats workflows and the complete scale/lifecycle acceptance matrix remain incomplete. The results below retain their original scope. + +Remote CI subsequently passed all six checks at `2dc37114b7c9b5137e866efc057a66e0dfd72046`. The review records exact Windows/macOS outcomes and distinguishes those passes from plan requirements that CI does not exercise. PR #26 remains a draft pending that work. ## Implemented boundaries and behavior diff --git a/records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md b/records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md new file mode 100644 index 000000000..fdddc7f86 --- /dev/null +++ b/records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md @@ -0,0 +1,168 @@ +# Editor UI 原计划逐项复审 — 2026-10-27 + +[当前验收](../../../docs/architecture/CURRENT_ACCEPTANCE.md) · [当前问题](../../../docs/architecture/CURRENT_ISSUES.md) · [实现记录](../EDITOR_UI_IMPLEMENTATION_PLAN.md) · [前次验收](EDITOR_UI_ACCEPTANCE_2026_10_27.md) · [PR #26](https://github.com/Wolfiora/InnoEngine/pull/26) + +## 结论与审查范围 + +**尚未完整达到原计划的完成标准。** 当前已经形成较好的工程基础,尤其是领域与呈现分层、Session 所有权、Core Identity 路由、统一 History 和回归测试。但仍有明确的功能、抽象和性能实现缺口,也缺少原计划要求的完整规模、生命周期和视觉验收。不能把这些统一解释成“只剩人工看看”,也不能把 CI 全绿解释成整个计划完成。 + +前次报告中的 “The implementation and local correctness checks are complete” 范围过大,本次予以纠正。已有通过结果继续有效;改变的是对它们能够证明什么的判断。本次没有发现足以否定全部重构的证据,也没有据此认定存在新的 BGCS 缺陷。 + +- 审查源码:`2dc37114b7c9b5137e866efc057a66e0dfd72046`,分支 `editor-ui`。 +- 远端 master:`cf799c8280f74f6faa19916a89f216778391fcd9`;PR #26 已存在,base 为 master,状态为 Draft。 +- 原始计划:用户提供的 `InnoEngine_Editor_UI_Plan.md`;SHA-256 为 `a55f25b76db09befd6cdd2db06a607d0e2a70d660dca6ca5429c64f3d5623646`。 +- Editor 范围:30 个项目、507 个 C# 源文件;PR 相对 master 共 373 个变更文件。此次沿关键路径深查,并不等于对每个分支作形式化正确性证明。 +- 方法:对照原计划条目、项目依赖、执行链和测试源码,重新解析最终 Windows/macOS CI 的 TRX,并重新构建运行 Interactions 测试。没有使用 computer use,没有重新执行全产品矩阵。 +- 本次修改范围是审查记录、当前文档与规范;没有修改生产代码、测试实现、BGCS、平台或生成绑定。 + +## 十一个维度的逐项评价 + +| 维度 | 已经成立的工程性质 | 尚未达到的要求 / 改进点 | 判断 | +| --- | --- | --- | --- | +| 架构 | Features 不依赖原生 UI 或 Panels;Panels 不互相引用;Assets 操作、Export 生命周期与绘制分开;相关边界有架构测试 | Graph 通用呈现只抽出了坐标、网格、曲线和连线命中,节点呈现、节点命中、框选等仍在 Shader Canvas;不能仅凭新增程序集判定抽象完成 | 主干达标,原计划未全闭合 | +| 代码整洁度 | 功能目录与 owner 更明确,公开依赖分组、Solution、脚本导出及调用方同步 | Shader Canvas 主文件约 1,110 行,多个 partial 共享 draft/controller 状态;部分源码同时使用多个指向同一 ImGui facade 的别名,阅读仍有噪声 | 明显改善,尚非全面清晰 | +| 代码干净程度 | Session/owner 退休、native borrow 校验、弱目标、Core Events 订阅所有权均有实际实现;没有靠全局 UI 状态表或额外 Undo 栈解决问题 | 全量异常退出与长期资源回收证据不足;Console、Shader 绘制局部仍有手动 Push/Pop 缺少局部 finally 的路径,需要逐个证明异常安全 | 核心机制较强,不能认证无泄漏/无残留 | +| 抽象程度 | Action、History、Inspection、Graph 文档和领域 Feature 边界实际有消费者;没有单靠空接口实现“解耦” | Graph presentation 抽取不足;通用样式中仍保留具体 Browser 布局策略;应沿真实复用点提取,避免再造万能 UI 框架 | 部分达标 | +| 可复用程度 | Core Events、Identity、Serialization、IO、History 和 Graph 被复用;资产操作不再只能经 File Browser 使用;脚本 SDK 直接引用真实 API owner | 通用节点呈现/命中/框选尚不能独立复用于非 Shader 图;需要第二种真实图呈现验证抽象边界 | 主要基础能力达标,图呈现仍有限 | +| 样式统一 | Session 统一 metrics/palette,表单、树、菜单和有界下拉有共享控件,已有缩放和布局回归 | 三层样式尚未完全落实;Browser 专属列比例、图标尺寸策略等仍在通用 metrics;没有完整 Zoom/DPI 与同条件前后截图矩阵 | 部分达标,未完成视觉验收 | +| 可拓展程度 | Attribute/Registry、Action 查询与执行、Drawer 发现、脚本导出按现有协议接入;有候选切换、Missing 和恢复测试 | 新 Graph 呈现不改 Shader/核心的完整接入证明不足;连续 10 次 UI/plugin reload 与旧 ALC 回收证据缺失 | 扩展基础达标,完整承诺未验收 | +| 操作逻辑性 | 有序选择、anchor/range/toggle、多对象修改、Play identity 路由和编辑手势具备共同契约 | Console 搜索未实现;统一键盘选择链没有完成证明;Stats 趋势未实现;真实完整编辑→Undo→保存→重开旅程不能由帧数代替 | 部分达标 | +| UndoRedo | 非 LIFO 拒绝先校验再弹栈;同一 History 处理手势、批量操作和中立 payload;失败保留操作,Missing 可恢复;公开边界回归较强 | 全 Panel 生命周期、键盘/鼠标混合输入、连续 reload 的端到端矩阵仍未完成;不可将单独 handler 测试当成全部界面旅程 | 本轮最扎实的部分,核心契约达标 | +| 菜单清晰度 | 菜单/快捷键/工具栏/搜索共享 Action Query/Execute;已有冲突诊断和禁用原因契约 | 静态菜单结构及 shortcut 候选尚未按 generation 建索引;目录顺序/无重复/禁用原因覆盖和完整键盘旅程仍需验收 | 功能基础达标,结构与成本未完全达标 | +| 组件颗粒度 | 领域操作、呈现和 Panel 的项目粒度合理;大部分功能文件有明确 owner | Shader Canvas 仍集中多类交互,Console 手动布局与通用样式仍需收拢;应按可独立验证的职责拆分,不以文件数为目标 | 主干合理,局部仍偏粗 | + +因此,不能将当前整体认证为“企业级、现代级、现象级全部达成”。其中现代化的生命周期与分层基础已经存在;“现象级”还包含主观使用体验,自动测试不能给出这样的结论。 + +## 真实调用链与边界自查 + +### 命令、选择与修改 + +实际链路是输入 → Session/Widget → Action 或 Feature → owner 的序列化上下文 → 统一 History → 当前 generation 的 handler。Panel 不应自行建立第二套业务可用性、序列化、对象索引或 Undo 协议。 + +`EditorActionRouter` 的 Action ID 索引确实按 discovery 失效;执行时仍重新查询上下文。`EditorExtensionCatalog.ValidateActions/ValidateShortcuts` 会报告冲突,不能因为运行时排序存在 tie-break 就认定没有冲突检测。跨帧目标通过 Core Identity 或受约束的弱引用解析;Play allocator 的 routing lease 只路由,不是另一张 live-object 权威表。 + +### Session 与基础能力复用 + +`EditorUiSession.current` 是 ThreadStatic 同步作用域入口;状态由实例的 widgets/menus 持有,不能简单把出现 static 视为全局状态泄漏。Enter/Dispose 检查线程、嵌套和上下文,owner 退休会清理自己的控件/菜单状态。非 Identity target 只有明确中立标量强持有,包含引用的 boxed struct 不被错误视作纯值。 + +本轮关键链复用了 Core Events、Identity、Serialization 与现有 History;没有发现本次 UI 重构新增平行事件总线、Undo 栈或对象身份系统。这个判断限定于已检查的 UI 改动,不能替代全引擎所有事件/缓存实现的审计。 + +### 平台和第三方边界 + +这些 UI 缺口属于 Editor owner,不要求修改 BGCS、Native generated、第三方源码或平台层。前次 BGCS 4.1.2 包完整性与平台无改动证据继续有效,本次没有重新把 122 个文件的历史校验写成新执行。Mac CI 已通过真实 ARM64 自动运行;本地没有 Mac 交互设备,人工 Metal/视觉检查仍未执行。 + +## 确认的实现缺口与待验证风险 + +以下“确认”表示从源码可直接定位的现状;没有测量的数据不标为性能回退,没有复现的异常不标为已发生故障。 + +### U01 — 静态菜单与快捷键索引未落实 + +- 原计划:第三节 Action/菜单与性能条目,静态结构按 extension generation 索引,可用性 fresh 查询。 +- 位置:`framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs:14–112`;`Actions/EditorActionRouter.cs:179–220,350`(均相对 `src/composition/editor`)。 +- 当前 `Build` 每次遍历 registrations、收集 placement、规范化并拆分路径、排序和构造树;shortcut dispatch 每次收集全部 Action ID 和候选。 +- 已有 `m_byId` 不等于静态菜单或 shortcut 索引。动态菜单也不能被整体缓存成过期 enabled/checked 状态。 +- 完成方式:generation 内固定静态 metadata/候选索引;上下文、动态 contribution 和 Query 继续 fresh;候选切换时替换并释放旧 generation 引用。复用现有 catalog,不另建全局 cache service。 +- 验收:重复打开不重建静态结构;新增/卸载扩展正确更新;上下文变化立刻生效;冲突仍明确失败;对菜单打开至少五次采样 CPU/分配,包含旧 ALC 退休。 + +### U02 — 大数据视图与 Stats 尚未满足成本要求 + +- `FileBrowserPanel.List.cs:73` 遍历全部 entry,Grid 同样逐项提交;Hierarchy 每次绘制重新构造 visible-selection 列表。Native clipping 不等于避免 managed 每项处理。 +- `ShaderEditorCanvas.Node` **已有离屏矩形提前返回**(约 622 行),不能声称 Graph 完全不裁剪。但 `PortPositions()` 仍为全图创建 Dictionary 并计算端口,绘制/命中仍遍历图。是否构成严重瓶颈必须通过 2,000 节点实测判断。 +- `EditorStatistics.GetSnapshot():230` 每次合并 Dictionary、排序和 ToArray;`StatsPanel.OnDraw():35` 每次 GroupBy/Select,并重新构建字符串。它有 0.5 秒帧时长采样器,但不能据此宣称展示只读取复用采样快照;趋势显示尚不存在。 +- 完成方式:列表按可见范围提交,保留编辑/拖动/展开状态;显示数据按实际 revision、过滤、排序和尺度更新。Stats 的采样与不可变展示快照由明确 owner 管理;Graph 先测全图成本,再决定必要的增量几何复用。不能用复杂池掩盖重复工作。 +- 验收:10,000 项列表/树、2,000 节点图,空闲/滚动/搜索/拖动至少五次重复,CPU、分配、GC、median/P95;不得丢失多选或拖动状态,不得降低刷新正确性。 + +### U03 — 通用 Graph 呈现与领域样式边界仅部分完成 + +- `presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs` 提供坐标转换、网格、连线绘制和连线命中。它保持无 Shader 依赖,是实际有效的抽取。 +- 原计划还要求通用节点/命中/框选;这些行为仍与 Shader 的端口、默认参数、文档 draft 交织在 `ShaderEditorCanvas`。partial 文件分开并不自动解除共享状态依赖。 +- `EditorStyleMetrics.cs:230–562` 集中 Browser pane、breadcrumb、列分隔比例及图标布局等策略。它们不是平台耦合,也不等同于“包含资产类型白名单”,但三层样式中领域 owner 的责任仍不够清楚。 +- 完成方式:由真实第二种图消费者验证最小可复用节点、命中和框选输入/结果;Shader 只贡献自身端口/参数/颜色语义。Browser 专属布局值由 Assets presentation owner 组合通用尺度,通用层不反向认识 Browser。 +- 验收:非 Shader 测试图可使用同一呈现机制;领域专属类型不泄漏进通用层;节点选择/框选/缩放/连线与 History 语义保持;样式同帧一致、API/脚本/Wiki 同步。 + +### U04 — Console 搜索与键盘操作验收未闭合 + +- `ConsolePanel.DrawToolbar():102` 只有 Collapse、级别 Filter、Clear;DrawEntries 只有级别筛选。原计划明确要求的文本搜索没有实现。 +- Console 没有统一的条目键盘选择模型。Hierarchy 的共享 Tree 返回鼠标 clicked/doubleClicked;原生 Tree 可以处理部分导航,但这不足以证明它与引擎 ordered selection、范围选择、焦点和批量命令一致。 +- File Browser 使用原生 Selectable,不能因此断言完全没有键盘激活;缺少的是原计划统一键盘选择/焦点行为的完整证明。 +- 完成方式:Console 搜索使用现有 Diagnostics snapshot 与 revision;键盘动作接入既有 Action/Selection,不能新造列表专用事件系统。明确焦点、上下移动、范围扩展、激活、取消和只读行为。 +- 验收:真实公开输入驱动 Console/Hierarchy/File Browser,覆盖过滤后导航、Shift 范围、Ctrl 切换、输入框与 Popup 遮挡;结果与鼠标操作一致,批量修改仍原子化。 + +### U05 — 异常栈平衡与长期退休仍有证据缺口 + +- Shader 节点端口标签局部 `PushClip`/`Text`/`PopClip` 没有局部 finally;Console 也有部分 Child、style 和 ID push/pop 直接顺序配对。 +- 这是源码审查得到的异常安全风险,不是本次已复现的崩溃。正常产品跑通不能证明中途异常一定平衡,也不能在没有可触发路径时武断判为 P0。 +- 完成方式:在已有 owner/borrow 边界补齐成对资源的异常安全;通过实际可替换 public boundary 注入失败,确认原生栈和后续绘制可继续。没有必要引入一个覆盖所有 UI 的新异常框架。 +- 再执行 100 次 Panel/Document 开关、10 次脚本/plugin reload、Host 退出资源检查;记录 UI owner、订阅、gesture、旧 ALC weak monitor 和 GPU 资源。保持严格 unload barrier,不放宽超时。 + +## 原计划验收对照 + +| 原计划条目 | 当前有效证据 | 本次判定 | +| --- | --- | --- | +| 无 Panel→Panel、feature→UI/native 依赖 | 架构验证与当前项目引用 | 已通过该边界检查;不代替全部内部职责审查 | +| History LIFO、手势、失败/Missing、批量 | 当前源码、121 项 Interactions 本次复跑;Windows/Mac CI 对应回归 | 核心契约通过 | +| 无 native borrow 越界、Session owner 退休 | UiSession/native widget tests,关闭 fixture | 有针对性证据;不等于全 UI 长时认证 | +| Static menu/shortcut generation index | 仅 Action ID 索引存在 | 实现未完成,见 U01 | +| 通用节点/命中/框选 presentation | 通用坐标/网格/曲线/连线命中存在 | 部分完成,见 U03 | +| Console 搜索、键盘选择;Stats 趋势 | 搜索/趋势未实现,键盘矩阵不足 | 未完成,见 U02/U04 | +| 7 档 Zoom × 可执行 DPI 场景、长标签/遮挡/焦点/中文输入 | 有若干真实 native layout/输入测试 | 不能推断全矩阵已通过 | +| 同布局、数据、尺度和尺寸的前后截图 | 有 FlappyBird 截图 | 没有完整成对视觉证据 | +| 当前 UI 操作、菜单与三层样式规范 | 有 `EDITOR_UI_ARCHITECTURE.md` 架构说明;原计划的 `EDITOR_UI_DESIGN_STANDARD.md` 未落地 | 架构说明不能直接代替操作/样式设计规范;应补足实质内容,文件名本身不是完成标准 | +| 10,000 项树/资源列表、2,000 节点图;至少五次 UI CPU/alloc/GC/median/P95 | 只有独立 UTF-8 编码 microbenchmark 和产品帧运行记录 | 未执行完整规模矩阵,不能宣称整体提速 | +| 100 次 Panel/Document 开关 | 有单次/定向生命周期测试 | 没有计划要求的 100-cycle 结果 | +| 连续 10 次脚本/plugin reload、严格旧 ALC 回收 | 有 reload/Missing/GC 定向测试 | 没有整个 UI 场景的 10-cycle 结果 | +| Windows Debug/Release、D3D11/12/Vulkan、Play→Edit | 本地 FlappyBird 和远端产品 smoke | 有界产品路径通过;不是完整人工操作旅程 | +| Canvas/Rendering2D 当前脚本及 SDK/API 投影 | 新 SDK probe、Rendering2D 42 项、正式包导出 | 已有证据按前次记录范围成立;不能扩大为所有插件操作已验收 | +| Mac CI | 最终 ARM64 job 通过 | 自动验收通过;本地/人工 Mac 交互未执行 | + +原计划明确允许通过真实 ImGui Context 和公开输入完成测试,**不依赖 computer use**。因此此前窗口激活失败只能说明人工操作没执行,不能解释或豁免完整自动几何/输入矩阵的缺失。 + +## Issue 核对 + +| Issue | 源码与证据结论 | 处理 | +| --- | --- | --- | +| [#13](https://github.com/Wolfiora/InnoEngine/issues/13) History 非 LIFO 拒绝破坏栈 | `EditorHistory.PopTransaction` 先 Peek/校验,再 Pop;Commit/Rollback/Dispose 三种拒绝后仍可正常回滚/Undo。最终 Windows/Mac TRX 各三项通过;本次 Interactions 复跑通过 | 已在本分支修复,PR 添加 `Fixes #13`;合并前不把 master 标为已修复 | +| [#16](https://github.com/Wolfiora/InnoEngine/issues/16) 固定默认 assets 路径 | 当前及 master 测试都由 SDK Restore 返回实际 `ProjectAssetsFile`,使用独立 ArtifactsPath;两平台最终 TRX 通过。旧描述的固定路径已不存在 | 根因已修正;原 issue 还要求真实 package/load mismatch 拒绝等完整矩阵。本次没有新的 mismatch 反例证据,不直接宣布全部关闭 | +| [#18](https://github.com/Wolfiora/InnoEngine/issues/18) API XML/Wiki 语义 | UI 新边界已补文档,但 Stats 等仍有泛化描述,全仓语义审查不因 XML/Wiki gate 通过而结束 | 保持开放;与本次文档质量有关 | +| [#19](https://github.com/Wolfiora/InnoEngine/issues/19) 多 SDK reference 一致性 | SDK 直接引用 API owner 修复了另一种依赖问题;没有证明多 pack/root 输入一致 | 保持开放,不混淆两类问题 | +| #14 / #15 | 热 Native 准备成本、自动维护预算收敛 | 0 native processes、显式清理成功都不是成本/自动预算验收;保持开放 | +| #17 / #20 / #21 / #22 | 文件系统语义、原生编码、Runtime coroutine、Browser 诊断 | UI 改动和本次审查没有关闭这些问题的证据 | +| #23 | NuGet 锁文件治理 | 已关闭且被标准 PackageReference 流程接替;不重新引入锁清单 | + +没有因为 issue 标题看起来相近就直接关闭,也没有将旧问题重新包装成此次 UI 回归。U01–U05 在本报告给出 owner 和验收条件,作为 PR 尚未完成的计划清单;本次没有批量新建重复 issue。 + +## 测试与证据 + +本次重新运行: + +```powershell +dotnet test tests/editor/Inno.Editor.Interactions.Tests/Inno.Editor.Interactions.Tests.csproj --no-restore -c SourceDebug --logger "trx;LogFileName=interactions.trx" --results-directory D:/InnoEngine-Verification/editor-ui-2026-10-27/plan-review-tests -v quiet +``` + +结果:**121 passed、0 failed、0 skipped**。此命令重新构建当前源代码,不以 `--no-build` 的旧程序集替代。日志 `plan-review-interactions.log`;TRX `plan-review-tests/interactions.trx`。 + +本次重新解析最终 CI 原始归档(不是重新运行 CI): + +| 来源 | 结果 | 适用限制 | +| --- | --- | --- | +| [Engine CI 40444183857](https://github.com/Wolfiora/InnoEngine/actions/runs/40444183857),head `2dc37114` | Windows 2,230 pass / 18 skip;macOS ARM64 2,247 pass / 1 skip;均 0 fail | 环境相关 skip 保留;自动产品 smoke 不替代完整人工旅程 | +| 两个 managed job | 各 1,185 pass | 与完整 suite 重叠,不相加 | +| [NuGet graph 40444183878](https://github.com/Wolfiora/InnoEngine/actions/runs/40444183878) 与 submission | 通过;该 head 共 6/6 checks success | 不代表原计划缺失条目已经被 CI 覆盖 | +| 本次从两平台 TRX 定向抽取 History/SceneHistory/UiSession/GraphCanvas/包身份 | 每平台 54 条通过,包含 #13 的三种拒绝和 #16 还原身份 | 子集证据,不与上列累加 | + +证据 owner 为 `D:/InnoEngine-Verification/editor-ui-2026-10-27`,新增 `plan-review-evidence.json` 保存原计划哈希、归档哈希、具体 test ID/名称/结果及 TRX 路径。既有 `remote-ci-final.json`、两平台 `remote-ci-*-contract-analysis.json` 和实际下载归档保留。 + +前次微基准只证明编码缓冲实现减少分配,不能推出整个 Editor 更快。Mac Metal 的八条 BGFX refcount warning 在 master 基线也存在,device count 有差异、根因未知;本次不将其当作新 UI 缺陷,也不宣称已经无 GPU 泄漏。GitHub Action runtime 弃用告警同样不是此次 UI 完成标准的替代项。 + +## 后续闭合顺序与 PR 决定 + +1. **先修正文档和状态**:撤回“整个实现完成”的表述,保持原通过日志,区分缺功能、缺抽象、缺测量。当前问题页修正仍把 #13 描述为未修代码的旧结论。 +2. **完成 U01/U04 的用户可见闭环**:generation 索引、Console 搜索、统一键盘选择;保留 fresh Query、既有 Core Events/Selection。 +3. **完成 U03 的职责收拢**:用实际非 Shader 消费者验证通用 Graph 机制;领域布局回到领域 presentation,公开 API 和 SDK 同步。 +4. **完成 U02 的测量与针对性实现**:先固定相同基线与输入,记录 10k/2k 成本,再实现必要可见范围/增量快照;不凭猜测建池。 +5. **闭合 U05 和完整验收矩阵**:异常路径、100-cycle/10-reload、Zoom/DPI、同条件截图、完整 FlappyBird 编辑旅程及插件安装态。真实 Mac 人工项单列。 +6. **最终同一源码再判定**:适用实现与验收全部通过后,将 PR 从 Draft 转 Ready。同步 Rendering2D/Samples 的调用方交付状态;它们此前仅本地提交,不把引擎 PR 当作四仓库都已远端发布。 + +本次不为给出漂亮评价而临时大改未测的菜单缓存或图形算法。已存在的 **PR #26 继续面向 master 并保持 Draft**,无需再创建一个同分支重复 PR。标题和正文继续英文、`type(scope): description`;`Fixes #13` 只在合并时关闭已修复 issue。 + +自查答案:**基础分层和 Core 复用有真实代码支撑,但“超干净、全部达标”的说法目前不成立。** 上述缺口属于明确的后续工程工作,不能用改名、拆文件、增加接口或更多正常路径 smoke 掩盖。Windows 无 `/System/Library/Sounds/Glass.aiff`,本次未播放指定提示音。 From 199ae0dcff070c21728bc19994edb407378f43ce Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 19:31:18 +0800 Subject: [PATCH 05/54] chore(editor): track closure issues and acceptance requirements --- .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 records/architecture/EDITOR_UI_CLOSURE_PLAN.md diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md new file mode 100644 index 000000000..46a8193df --- /dev/null +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -0,0 +1,81 @@ +# Editor UI closure execution ledger + +[Original-plan review](acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md) · [Parent issue #27](https://github.com/Wolfiora/InnoEngine/issues/27) · [PR #26](https://github.com/Wolfiora/InnoEngine/pull/26) + +Baseline: `30d18489fbf68763229cd8ed35a7005f543019ad`. Evidence owner: `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`. The original desktop plan has SHA-256 `a55f25b76db09befd6cdd2db06a607d0e2a70d660dca6ca5429c64f3d5623646`. + +Every work item below is a real GitHub issue created before implementation. Issues remain open after branch verification. Only completely verified items receive an individual `Fixes #N` in PR #26; they close on merge. The broader #18 uses `Refs #18`. No automatic merge is authorized. + +## Issue execution map + +| Work | Issue | Owner | State | +| --- | --- | --- | --- | +| E01 scopes and failures | [#28](https://github.com/Wolfiora/InnoEngine/issues/28) | ImGui adapter and concrete presentation | In progress | +| E02 command indexes | [#29](https://github.com/Wolfiora/InnoEngine/issues/29) | Interactions generation catalog | Not started | +| E03 styles | [#30](https://github.com/Wolfiora/InnoEngine/issues/30) | Shared and domain presentation | Not started | +| E04 keyboard and Console | [#31](https://github.com/Wolfiora/InnoEngine/issues/31) | Selection, controls and Diagnostics views | Not started | +| E05 graph | [#32](https://github.com/Wolfiora/InnoEngine/issues/32) | Graph/Graph.ImGui and Shader consumer | Not started | +| E06 statistics | [#33](https://github.com/Wolfiora/InnoEngine/issues/33) | EditorStatistics and Stats Panel | Not started | +| E07 large views | [#34](https://github.com/Wolfiora/InnoEngine/issues/34) | Browser/tree/Console/graph presentation | Not started | +| E08 workflows | [#35](https://github.com/Wolfiora/InnoEngine/issues/35) | Existing domain features and History | Not started | +| Actual restore/load scope | [#16](https://github.com/Wolfiora/InnoEngine/issues/16) | Build public SDK tests | Not started | +| Frozen framework inputs | [#19](https://github.com/Wolfiora/InnoEngine/issues/19) | Host/Build capture and Compiler | Not started | +| E09 scale/input | [#36](https://github.com/Wolfiora/InnoEngine/issues/36) | Native public-input fixtures | Not started | +| E10 retirement/reload | [#37](https://github.com/Wolfiora/InnoEngine/issues/37) | Host and generation owners | Not started | +| E11 performance | [#38](https://github.com/Wolfiora/InnoEngine/issues/38) | Deterministic public-host workloads | Baseline preparation | +| E12 documentation/review | [#39](https://github.com/Wolfiora/InnoEngine/issues/39) | Each changed API and Wiki owner | Not started | +| Existing History rejection fix | [#13](https://github.com/Wolfiora/InnoEngine/issues/13) | History | Previously verified; final rerun required | + +## Original requirement traceability + +Each row must ultimately record a final commit, executable command and evidence path. Empty evidence is not a pass. Historical results in the review are background only. + +| Requirement | Issues | Required final evidence | State | +| --- | --- | --- | --- | +| Features independent of native UI/Panels; no Panel references | #30, #32, #39 | Evaluated graph and architecture tests | Pending | +| Session-owned state and per-owner retirement | #28, #37 | Independent contexts, failure and close cycles | Pending | +| Frame/window/context/thread native borrows | #28, #36 | Public misuse and recovery tests | Pending | +| Core Events/Identity/Serialization/IO reuse | #28, #35, #39 | Call-chain and lifecycle review | Pending | +| Ordered identity selection, anchor/range/toggle | #31, #35 | Filtered/sorted mouse and keyboard inputs | Pending | +| Deferred identity and Play domain routing | #35, #37 | Play/Edit and generation recovery | Pending | +| Static menu and shortcut generation indexes | #29 | Fresh queries, reuse, conflicts and retirement | Pending | +| Shared Action availability/execute/search/toolbar | #29, #35 | Native and headless entry equivalence | Pending | +| Stable menu order, no empty groups, disabled reasons | #29, #36 | Menu model/input matrix | Pending | +| Non-LIFO rejection preserves transactions | #13, #35 | Commit/Rollback/Dispose and legal completion | Pending | +| Immediate gestures, cancel and independent drags | #35 | Inspector and viewport actual inputs | Pending | +| Atomic batches and Missing barriers | #35, #37 | Partial failure and recovery without stack movement | Pending | +| Granular Scene payload and owner serialization context | #35, #39 | Mutation/payload contract review and regressions | Pending | +| Mixed fields, exact vector axes, duplicate components | #35, #36 | Multi-object/property input tests | Pending | +| Reference select/ping/clear/drop, Missing/read-only | #35, #36 | Shared field interactions and source protection | Pending | +| Three style layers and domain ownership | #30 | References, API and scale tests | Pending | +| Label/input ratio, single scroll owner, bounded combos | #30, #36 | Native geometry and input assertions | Pending | +| Workbench and global workflows | #29, #35 | Commands/settings/export success/failure/cancel | Pending | +| Hierarchy and Browser complete operations | #31, #34, #35 | Multi-select, parent/source edits and rollback | Pending | +| Scene navigation/Gizmo/picking/drawing separation | #35, #39 | Owner review and multi-transform input | Pending | +| Game input/focus/aspect/scale and Play isolation | #35, #36 | Public Host input and Play/Edit journey | Pending | +| Generic graph nodes/hits/box/edges and neutral intents | #32 | Real non-Shader consumer plus Shader regression | Pending | +| Graph document/clipboard/save/history | #32, #35 | Actual graph workflow tests | Pending | +| Settings validation/Apply/Cancel/close | #35 | Domain/presentation failure and reopen tests | Pending | +| Console search/filter/group/detail/copy/keyboard | #31, #34 | Snapshot and native input tests | Pending | +| Stats sampled snapshots and trends | #33 | Frame ordering, reuse, 120-sample/60-second bounds | Pending | +| Export operation ownership/progress/cancel/retry | #28, #35, #37 | Exact owner and Pending retirement protocol | Pending | +| Visible-range submission and revision-driven display | #34 | 10k/2k functional and work-count evidence | Pending | +| New Panel/Action/Drawer/Graph without central switches | #29, #32, #35 | Independent public extension fixtures | Pending | +| Runtime/IDE inputs and C#14 capabilities agree | #19 | Multi SDK/root/pack and language matrix | Pending | +| Actual assets/load identity, output scopes and mismatch | #16 | Default/isolated positive and negative SDK tests | Pending | +| Zoom 75/85/90/100/110/125/150; DPI 100/125/150/200 | #36 | At least 28 combinations and window variants | Pending | +| Unicode/clipboard/focus/occlusion/tree same-frame inputs | #36 | Real context/public input assertions | Pending | +| Same-layout/input/scale baseline/final screenshots | #36 | Matched images; human judgment distinct | Pending | +| 100 cycles per stateful Panel/document kind | #37 | Owner/resources return to expected inventory | Pending | +| 10 reloads, strict ALC barrier and Host shutdown | #37 | Weak monitors, subscriptions, gestures and fault paths | Pending | +| Five-repeat FlappyBird/10k/2k CPU/alloc/GC/median/P95 | #38 | Controlled baseline/final raw measurements | Pending | +| No allocation growth or repeatable >10% regression | #38 | Paired comparison with noise and limits | Pending | +| Windows Debug/Release D3D11/D3D12/Vulkan | #35, #39 | Final revision FlappyBird product journeys | Pending | +| Canvas/Rendering2D development and installed plugins | #19, #35, #39 | Formal export and compiler/Drawer regressions | Pending | +| Mac ARM64 CI; manual device limits explicit | #39 | Final CI plus unexecuted manual disclosure | Pending | +| XML/Wiki/Solution/projects/current design standard | #18, #39 | Semantic review and automated gates | Pending | +| Eleven quality dimensions and deep self-review | #39 | Final evidence-based individual assessment | Pending | + +## Delivery constraints + +.NET SDK 10.0.402, C# 14 and BGCS 4.1.2 remain fixed. No BGCS/vendor/generated modifications, global/implicit imports, IVT, private reflection, test backdoors, compatibility readers or parallel infrastructure. Author and committer are Aaron Liao , with no co-author. Preserve exact source ownership and clean only proven inactive owned outputs; retain current products and evidence. From 65d222ae43631083c0a5db6747300d655611c298 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 19:31:19 +0800 Subject: [PATCH 06/54] fix(editor): preserve drawing scopes when extensions fail --- .../Console/ConsolePanel.cs | 287 ++++++++++-------- .../Canvas/ShaderEditorCanvas.cs | 6 +- .../WidgetLayoutTests.cs | 55 ++++ .../ConsolePanelNativeLayoutTests.cs | 63 ++++ 4 files changed, 287 insertions(+), 124 deletions(-) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs index f0a984352..6a55beb08 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs @@ -87,10 +87,16 @@ protected override void Restore(EditorState state) protected override void OnDraw(EditorContext context) { Ui.BeginChild("ConsoleChild", Vector2.Zero, (ImGuiChildFlags.None), (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); - DrawToolbar(); - Ui.Separator(); - DrawConsoleRegion(); - Ui.EndChild(); + try + { + DrawToolbar(); + Ui.Separator(); + DrawConsoleRegion(); + } + finally + { + Ui.EndChild(); + } } #endregion @@ -157,20 +163,25 @@ private bool DrawFilterCombo() private void DrawConsoleRegion() { Ui.BeginChild("ConsoleRegion", Vector2.Zero, 0, 0); - EditorConsoleSnapshot snapshot = m_console.Capture(); - RemoveStaleOpenEntries(snapshot); - bool hasNewEntries = m_lastRevision >= 0 && snapshot.revision != m_lastRevision; - m_lastRevision = snapshot.revision; - bool scrollAtBottom = Ui.GetScrollY() >= Ui.GetScrollMaxY() - EditorUI.current.widgets.style.logAutoScrollTolerance; - bool shouldScrollToBottom = m_requestScrollToBottom || (hasNewEntries && scrollAtBottom); - DrawEntries(snapshot); - if (shouldScrollToBottom) + try { - Ui.SetScrollHereY(1f); - m_requestScrollToBottom = false; + EditorConsoleSnapshot snapshot = m_console.Capture(); + RemoveStaleOpenEntries(snapshot); + bool hasNewEntries = m_lastRevision >= 0 && snapshot.revision != m_lastRevision; + m_lastRevision = snapshot.revision; + bool scrollAtBottom = Ui.GetScrollY() >= Ui.GetScrollMaxY() - EditorUI.current.widgets.style.logAutoScrollTolerance; + bool shouldScrollToBottom = m_requestScrollToBottom || (hasNewEntries && scrollAtBottom); + DrawEntries(snapshot); + if (shouldScrollToBottom) + { + Ui.SetScrollHereY(1f); + m_requestScrollToBottom = false; + } + } + finally + { + Ui.EndChild(); } - - Ui.EndChild(); } #endregion @@ -221,72 +232,94 @@ string identity ) { (Vector4 levelColor, string levelIcon) = m_content.GetLevelVisual(entry.level); ConsoleEntryImGuiIdentity.Push(identity); - bool open = m_openEntries.Contains(identity); - if (!open) + try + { + DrawEntryCard(entry, occurrences, repeatCount, identity, levelColor, levelIcon); + } + finally { - Vector4 collapsedCardBg = m_content.GetCollapsedBgColor(); - Vector4 collapsedCardBorder = m_content.GetCollapsedBorderColor(); - Ui.PushStyleColor((ImGuiCol.FrameBg), collapsedCardBg); - Ui.PushStyleColor((ImGuiCol.Border), collapsedCardBorder); - Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), UiDrawing.GetLayout().frameRounding); - Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), EditorUI.current.widgets.style.borderSize); - Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.framePadding); - ImGuiChildFlags collapsedChildFlags = ImGuiChildFlags.FrameStyle; - ImGuiWindowFlags collapsedChildWindowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; - float collapsedHeight = Ui.GetFrameHeight() + UiDrawing.GetLayout().framePadding.Y * 2f; - if (Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, collapsedHeight), collapsedChildFlags, collapsedChildWindowFlags)) - { - DrawHeader(entry, repeatCount, levelColor, levelIcon, false, collapsedCardBg, out bool toggled); - if (toggled) - m_openEntries.Add(identity); - } - - Ui.EndChild(); - DrawEntryContextMenu(entry, repeatCount); - Ui.PopStyleVar(3); - Ui.PopStyleColor(2); ConsoleEntryImGuiIdentity.Pop(); - return; } + } - Vector4 cardBg = m_content.GetExpandedBgColor(levelColor); - Vector4 cardBorder = EditorPalette.GetLogExpandedBorder(levelColor); + private void DrawEntryCard( + EditorConsoleOccurrence entry, + IReadOnlyList occurrences, + int repeatCount, + string identity, + Vector4 levelColor, + string levelIcon + ) { + bool open = m_openEntries.Contains(identity); + Vector4 cardBg = open ? m_content.GetExpandedBgColor(levelColor) : m_content.GetCollapsedBgColor(); + Vector4 cardBorder = open ? EditorPalette.GetLogExpandedBorder(levelColor) : m_content.GetCollapsedBorderColor(); Ui.PushStyleColor((ImGuiCol.FrameBg), cardBg); Ui.PushStyleColor((ImGuiCol.Border), cardBorder); Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), UiDrawing.GetLayout().frameRounding); Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), EditorUI.current.widgets.style.borderSize); Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.framePadding); - ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | ImGuiChildFlags.AutoResizeY; - ImGuiWindowFlags childWindowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; - if (Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, 0f), childFlags, childWindowFlags)) + try { - DrawHeader(entry, repeatCount, levelColor, levelIcon, true, cardBg, out bool toggled); - if (toggled) - m_openEntries.Remove(identity); - Ui.PushStyleColor((ImGuiCol.Separator), EditorPalette.GetLogSeparator(cardBg)); + ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | (open ? ImGuiChildFlags.AutoResizeY : ImGuiChildFlags.None); + ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; + float height = open ? 0f : Ui.GetFrameHeight() + UiDrawing.GetLayout().framePadding.Y * 2f; + bool visible = Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, height), childFlags, windowFlags); try { - Ui.Separator(); - for (int i = 0; i < occurrences.Count; i++) + if (visible) { - if (i > 0) - Ui.Separator(); - Ui.PushId(unchecked((int)occurrences[i].sequence)); - DrawDetail(occurrences[i]); - Ui.PopId(); + DrawHeader(entry, repeatCount, levelColor, levelIcon, open, cardBg, out bool toggled); + if (toggled) + { + if (open) + m_openEntries.Remove(identity); + else + m_openEntries.Add(identity); + } + if (open) + DrawOccurrences(occurrences, cardBg); } } finally { - Ui.PopStyleColor(); + Ui.EndChild(); } + DrawEntryContextMenu(entry, repeatCount); } + finally + { + Ui.PopStyleVar(3); + Ui.PopStyleColor(2); + } + } - Ui.EndChild(); - DrawEntryContextMenu(entry, repeatCount); - Ui.PopStyleVar(3); - Ui.PopStyleColor(2); - ConsoleEntryImGuiIdentity.Pop(); + private static void DrawOccurrences( + IReadOnlyList occurrences, + Vector4 cardBackground + ) { + Ui.PushStyleColor(ImGuiCol.Separator, EditorPalette.GetLogSeparator(cardBackground)); + try + { + Ui.Separator(); + for (int i = 0; i < occurrences.Count; i++) + { + if (i > 0) + Ui.Separator(); + Ui.PushId(unchecked((int)occurrences[i].sequence)); + try + { + DrawDetail(occurrences[i]); + } + finally + { + Ui.PopId(); + } + } + } + finally + { + Ui.PopStyleColor(); + } } #endregion @@ -321,55 +354,63 @@ out bool toggled ImGuiTableFlags flags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoPadInnerX | ImGuiTableFlags.NoSavedSettings; if (Ui.BeginTable("##HeaderTable", 3, flags, tableOuterSize)) { - Ui.TableSetupColumn("##prefix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), prefixW); - Ui.TableSetupColumn("##content", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), contentW); - Ui.TableSetupColumn("##suffix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), suffixColW); - Ui.TableNextRow(); - if (isOpen) + try { - uint headerBgU32 = Ui.ToPackedColor(headerBgColor); - Ui.TableSetBgColor((ImGuiTableBgTarget.RowBg0), headerBgU32); - } + Ui.TableSetupColumn("##prefix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), prefixW); + Ui.TableSetupColumn("##content", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), contentW); + Ui.TableSetupColumn("##suffix", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), suffixColW); + Ui.TableNextRow(); + if (isOpen) + { + uint headerBgU32 = Ui.ToPackedColor(headerBgColor); + Ui.TableSetBgColor((ImGuiTableBgTarget.RowBg0), headerBgU32); + } - _ = Ui.TableSetColumnIndex(0); - Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.logDisclosurePadding); - Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), 0f); - Ui.PushStyleColor((ImGuiCol.Button), EditorPalette.logToggle); - Ui.PushStyleColor((ImGuiCol.ButtonHovered), EditorPalette.logToggleHovered); - Ui.PushStyleColor((ImGuiCol.ButtonActive), EditorPalette.logToggleActive); - if (Ui.SmallButton($"{toggleText}##HeaderToggle")) - { - toggled = true; - } + _ = Ui.TableSetColumnIndex(0); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.logDisclosurePadding); + Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), 0f); + Ui.PushStyleColor((ImGuiCol.Button), EditorPalette.logToggle); + Ui.PushStyleColor((ImGuiCol.ButtonHovered), EditorPalette.logToggleHovered); + Ui.PushStyleColor((ImGuiCol.ButtonActive), EditorPalette.logToggleActive); + try + { + if (Ui.SmallButton($"{toggleText}##HeaderToggle")) + { + toggled = true; + } - Ui.PopStyleColor(3); - Ui.PopStyleVar(2); - Ui.SameLine(0f, -1f); - Ui.PushStyleColor((ImGuiCol.Text), levelColor); - Ui.Text(prefix); - Ui.PopStyleColor(); - _ = Ui.TableSetColumnIndex(1); - if (isOpen) - { - Ui.PushTextWrapPos(0f); - Ui.Text(content); - Ui.PopTextWrapPos(); - } - else - { - Ui.Text(content); - } + } + finally + { + Ui.PopStyleColor(3); + Ui.PopStyleVar(2); + } + Ui.SameLine(0f, -1f); + EditorUI.current.widgets.ColoredText(levelColor, prefix); + _ = Ui.TableSetColumnIndex(1); + if (isOpen) + { + EditorUI.current.widgets.WrappedText(content); + } + else + { + Ui.Text(content); + } + + _ = Ui.TableSetColumnIndex(2); + if (!string.IsNullOrEmpty(repeatText)) + { + float remainW = Ui.GetContentRegionAvailable().X; + float suffixOffsetX = MathF.Max(0f, remainW - suffixW); + Ui.SetCursorPosX(Ui.GetCursorPosX() + suffixOffsetX); + Ui.Text(repeatText); + } - _ = Ui.TableSetColumnIndex(2); - if (!string.IsNullOrEmpty(repeatText)) + } + finally { - float remainW = Ui.GetContentRegionAvailable().X; - float suffixOffsetX = MathF.Max(0f, remainW - suffixW); - Ui.SetCursorPosX(Ui.GetCursorPosX() + suffixOffsetX); - Ui.Text(repeatText); + Ui.EndTable(); } - - Ui.EndTable(); } } @@ -398,23 +439,27 @@ private static void DrawDetail(EditorConsoleOccurrence entry) } else if (Ui.BeginTable("##ConsoleEntryDetails", 2, (ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings), new Vector2(availableWidth, 0))) { - Ui.TableSetupColumn("##label", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), labelWidth); - Ui.TableSetupColumn("##value", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), 1f); - DrawDetailFieldRow("Kind:", entry.kind.ToString()); - DrawDetailFieldRow("File:", fileWithLineText); - DrawDetailFieldRow("Source:", sourceText); - if (entry.sessionId.isAssigned) - DrawDetailFieldRow("Session:", entry.sessionId.ToString()); - DrawDetailFieldRow("Time:", timeText); - Ui.EndTable(); + try + { + Ui.TableSetupColumn("##label", (ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize), labelWidth); + Ui.TableSetupColumn("##value", (ImGuiTableColumnFlags.WidthStretch | ImGuiTableColumnFlags.NoResize), 1f); + DrawDetailFieldRow("Kind:", entry.kind.ToString()); + DrawDetailFieldRow("File:", fileWithLineText); + DrawDetailFieldRow("Source:", sourceText); + if (entry.sessionId.isAssigned) + DrawDetailFieldRow("Session:", entry.sessionId.ToString()); + DrawDetailFieldRow("Time:", timeText); + } + finally + { + Ui.EndTable(); + } } if (!string.IsNullOrWhiteSpace(entry.stackTrace)) { Ui.Separator(); - Ui.PushTextWrapPos(0f); - Ui.Text(entry.stackTrace); - Ui.PopTextWrapPos(); + EditorUI.current.widgets.WrappedText(entry.stackTrace); } } @@ -445,9 +490,7 @@ string value _ = Ui.TableSetColumnIndex(0); EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); _ = Ui.TableSetColumnIndex(1); - Ui.PushTextWrapPos(0f); - Ui.Text(value); - Ui.PopTextWrapPos(); + EditorUI.current.widgets.WrappedText(value); } private static void DrawStackedField( @@ -455,9 +498,7 @@ private static void DrawStackedField( string value ) { EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, label); - Ui.PushTextWrapPos(0f); - Ui.Text(value); - Ui.PopTextWrapPos(); + EditorUI.current.widgets.WrappedText(value); } #endregion } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs index 935a1a1d5..cfb20a739 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs @@ -668,6 +668,7 @@ Dictionary points Vector2 clipMin = new(clipLeft, bodyTop); Vector2 clipMax = new(clipRight, rect.max.Y); draw.PushClip(clipMin, clipMax, true); + try { draw.Text( new(x, point.Y - 8 * zoom), @@ -677,7 +678,10 @@ Dictionary points ); } - draw.PopClip(); + finally + { + draw.PopClip(); + } if (Vector2.DistanceSquared(point, ImGuiApi.GetMousePos()) <= 64) EditorUI.current.widgets.DrawTooltip(port.type.id + (missing ? " · missing port; reconnect explicitly" : optional ? " · optional input" : port.direction == GraphPortDirection.Input ? " · required input" : "")); } diff --git a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs index 2f80de903..2961907c8 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs @@ -16,6 +16,61 @@ namespace Inno.Editor.ImGui.Tests; public sealed unsafe class WidgetLayoutTests { + [Theory] + [InlineData("card-leading")] + [InlineData("card-trailing")] + [InlineData("card-menu")] + [InlineData("tree-content")] + [InlineData("tree-overlay")] + public void ExtensionDrawingFailureRestoresTheEnclosingWindow(string failure) + { + using NativeContext context = new(1f); + for (int frame = 0; frame < 2; frame++) + { + context.Frame(() => + { + BeginWindow("Failure owner", new Vector2(400f, 300f)); + nint window = (nint)NativeImGui.Raw.GetCurrentWindow(); + int depth = NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth; + Vector2 padding = Unsafe.BitCast(NativeImGui.Raw.GetStyle()->FramePadding); + var widgets = EditorUI.current.widgets; + Action rejected = () => throw new InvalidOperationException("Rejected extension draw"); + Assert.Throws(() => + { + if (failure.StartsWith("card-", StringComparison.Ordinal)) + { + _ = widgets.CollapsingCard( + "failed-card", + "Extension", + drawLeadingControl: failure == "card-leading" ? rejected : null, + drawTrailingControl: failure == "card-trailing" ? rejected : null, + dimmed: true, + drawContextMenu: failure == "card-menu" ? rejected : null); + } + else + { + widgets.SetNextTreeNodeOpen(true); + _ = widgets.TreeNode("failed-tree", _ => + { + if (failure == "tree-content") + rejected(); + EditorImGui.Text("Extension"); + }, new() { drawViewportOverlay = failure == "tree-overlay" ? rejected : null }); + } + }); + Assert.Equal(window, (nint)NativeImGui.Raw.GetCurrentWindow()); + Assert.Equal(depth, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); + Assert.Equal(padding, Unsafe.BitCast(NativeImGui.Raw.GetStyle()->FramePadding)); + widgets.SetNextTreeNodeOpen(true); + var next = widgets.TreeNode("next-tree", _ => EditorImGui.Text("Next valid row"), new()); + Assert.True(next.isOpen); + NativeImGui.TreePop(); + EditorImGui.GetDrawList().Line(new(30f, 30f), new(60f, 60f), uint.MaxValue); + NativeImGui.End(); + }); + } + } + [Fact] public void DrawingBorrowRejectsAnotherWindowOrFrame() { diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs index c3cceb9f3..c8bc5492f 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs @@ -24,6 +24,61 @@ namespace Inno.Editor.PlayMode.Tests; public sealed unsafe class ConsolePanelNativeLayoutTests { + [Fact] + public void SnapshotFailureQuarantinesTheConsoleWithoutLeakingChildWindows() + { + string root = Path.Combine(Path.GetTempPath(), "InnoConsoleFailure", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(root); + _ = Assembly.Load("Inno.Editor.Panel.Logging"); + ImGuiNativeContext* nativeContext = ImGuiApi.Raw.CreateContext(null); + try + { + ImGuiIO* io = ImGuiApi.Raw.GetIO(); + io->DisplaySize = Unsafe.BitCast(new Vector2(1200f, 800f)); + io->DeltaTime = 1f / 60f; + io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; + io->Fonts->RendererHasTextures = 1; + io->IniFilename = null; + using var modules = new ModuleHost(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(ConsolePanelNativeLayoutTests).Assembly) }); + using var types = new TypeCatalog(modules, new ReflectionTypeCatalogSource()); + using var logs = new LogRouter(); + var diagnostics = new DiagnosticHub(); + using IDisposable diagnosticScope = diagnostics.EnterScope(); + using var ui = new EditorUiSession(); + using var runtime = new EditorInteractionRuntime( + new EditorContext(root, new EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")), + types, + logs, + [new RejectedConsole()]); + runtime.Start(); + EditorPanelExtension panel = Assert.Single(runtime.panels, value => value.id == "diagnostics.console"); + panel.isOpen = true; + using IDisposable scope = ui.Enter(); + for (int frame = 0; frame < 2; frame++) + { + ImGuiApi.NewFrame(); + ImGuiApi.SetNextWindowSize(new(800f, 600f)); + using NativeStringLease title = Utf8Codec.EncodeNullTerminated("Failure parent", allowsNull: false); + _ = ImGuiApi.Raw.Begin((sbyte*)title.pointer, null, 0); + nint parent = (nint)ImGuiApi.Raw.GetCurrentWindow(); + if (frame == 0) + { + Assert.False(panel.Draw(runtime.context)); + Assert.False(panel.isOpen); + } + Assert.Equal(parent, (nint)ImGuiApi.Raw.GetCurrentWindow()); + Inno.Editor.ImGui.ImGui.Text("Following valid drawing"); + ImGuiApi.End(); + ImGuiApi.Render(); + } + } + finally + { + ImGuiApi.Raw.DestroyContext(nativeContext); + Directory.Delete(root, recursive: true); + } + } + [Theory] [InlineData(false, 1f)] [InlineData(true, 1f)] @@ -120,6 +175,14 @@ void Draw(float width) finally { ImGuiApi.Raw.DestroyContext(context); Directory.Delete(root, true); } } + private sealed class RejectedConsole : IEditorConsole + { + public int capacity { get; set; } + public bool clearOnPlay { get; set; } + public EditorConsoleSnapshot Capture() => throw new InvalidOperationException("Snapshot source failed."); + public void Clear() { } + } + private sealed class InactivePlayMode : IEditorPlayMode { public EditorPlayModeState state => EditorPlayModeState.Editing; From 88df3c798ab5186e4357e57bb5ff4d058b25b968 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 19:41:16 +0800 Subject: [PATCH 07/54] perf(editor): index command metadata per extension generation --- docs/architecture/EDITOR_UI_ARCHITECTURE.md | 2 + .../Actions/EditorActionRouter.cs | 97 ++++++-- .../Actions/HotKeyGesture.cs | 5 + .../Menus/EditorMenuCatalog.cs | 228 +++++++++++++----- .../Runtime/EditorInteractions.cs | 2 + .../EditorRuntimeTests.Commands.cs | 117 +++++++++ .../EditorRuntimeTests.Performance.cs | 49 ++++ 7 files changed, 414 insertions(+), 86 deletions(-) create mode 100644 tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Commands.cs create mode 100644 tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Performance.cs diff --git a/docs/architecture/EDITOR_UI_ARCHITECTURE.md b/docs/architecture/EDITOR_UI_ARCHITECTURE.md index e29f10996..ab7f5005f 100644 --- a/docs/architecture/EDITOR_UI_ARCHITECTURE.md +++ b/docs/architecture/EDITOR_UI_ARCHITECTURE.md @@ -52,6 +52,8 @@ src/composition/editor/ 普通菜单、快捷键、工具栏与 command search 消费同一 Action 的 Query/Execute;presentation 不能维护另一份 enabled/checked 业务规则。Action 索引按 active generation 失效。所有跨帧目标用 Core Identity 重新解析,不能保留 Component wrapper 或 collectible Type。 +静态菜单路径和结构在当前 generation 内冻结;快捷键按按键、规范化修饰键及 area 索引候选。缓存只保存声明事实,菜单状态和动态 source 仍按请求查询。动态菜单采用局部不可变结构合并,不把请求参数保存到静态模板。候选完成和 shutdown 明确清空索引;此处是内部执行机制,不增加项目作者配置。 + 非 Identity target 只有明确的中立标量可以强持有,其余对象使用弱引用,包括 boxed struct。宿主程序集的值类型仍可能包含插件对象,不能根据 `IsValueType` 或程序集不可回收就假定其内容中立。Selection、focus 与 snapshot 复用同一规则。 连续输入在首个有效 sample 打开 EditorEditGesture,release 提交,Escape 回滚。显式 Selection 切换先提交当前输入,准备失败则保持原选择;关闭 owner 或未完成的控件消失时回滚。命令和 reload 边界同样使用已有 Core Events 准备通知。材质/Pipeline 不再自己维护 deferred gesture;SceneEdits 和 asset draft service 都立即记录。 diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs index fbd7ab904..7c18f3996 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/EditorActionRouter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Inno.Core.Events; +using Inno.Core.Input; using Inno.Core.Logging; using Inno.Editor.Core; @@ -17,6 +18,7 @@ Logger log private readonly HashSet m_presentationFailures = new(StringComparer.Ordinal); private readonly HashSet m_queryFailures = new(StringComparer.Ordinal); private IReadOnlyDictionary? m_byId; + private Dictionary<(KeyCode key, KeyModifier modifiers, string area), string[]>? m_shortcutIndex; internal EditorActionState Query( string action, EditorActionContext context @@ -143,7 +145,11 @@ internal void ResetTransientState() InvalidateDiscovery(); } - internal void InvalidateDiscovery() => m_byId = null; + internal void InvalidateDiscovery() + { + m_byId = null; + m_shortcutIndex = null; + } internal void LosePresentationExcept(object? target) { var visited = new HashSet(ReferenceEqualityComparer.Instance); @@ -181,26 +187,40 @@ internal bool DispatchShortcut( string area, object? target ) { - var actionIds = new HashSet(StringComparer.Ordinal); - foreach (EditorExtensionCatalog.ActionRegistration registration in catalog.extensions.actions) - _ = actionIds.Add(registration.id); + if (keyEvent.repeat) + return false; + m_shortcutIndex ??= BuildShortcutIndex(); + KeyModifier modifiers = new HotKeyGesture(keyEvent.key, keyEvent.modifiers).normalizedModifiers; + _ = m_shortcutIndex.TryGetValue((keyEvent.key, modifiers, area), out string[]? exact); + _ = m_shortcutIndex.TryGetValue((keyEvent.key, modifiers, string.Empty), out string[]? global); + if (exact is null && global is null) + return false; var context = new EditorActionContext(editor, interactions, area, target); var candidates = new List(); - foreach (string action in actionIds) + for (int pass = 0; pass < 2; pass++) { - ResolvedAction? resolved = ResolveWithSpecificity(action, context); - if (resolved is null) - { + string[]? actionIds = pass == 0 ? exact : global; + if (actionIds is null) continue; - } - - foreach (EditorShortcutAttribute shortcut in GetApplicableShortcuts(resolved.Value.registration, area)) + foreach (string action in actionIds) { - HotKeyGesture gesture = CreateGesture(shortcut); - if (!gesture.Matches(keyEvent)) + if (pass == 1 && exact is not null && Array.IndexOf(exact, action) >= 0) + continue; + ResolvedAction? resolved = ResolveWithSpecificity(action, context); + if (resolved is null) continue; - candidates.Add(new ShortcutCandidate(action, resolved.Value, gesture)); - break; + EditorExtensionCatalog.ActionRegistration registration = resolved.Value.registration; + bool hasExact = HasExactShortcut(registration, area); + foreach (EditorShortcutAttribute shortcut in registration.shortcuts) + { + if (hasExact ? !string.Equals(shortcut.area, area, StringComparison.Ordinal) : !string.IsNullOrEmpty(shortcut.area)) + continue; + HotKeyGesture gesture = CreateGesture(shortcut); + if (!gesture.Matches(keyEvent)) + continue; + candidates.Add(new ShortcutCandidate(action, resolved.Value, gesture)); + break; + } } } @@ -235,15 +255,19 @@ private bool TryResolveShortcut( string area, out HotKeyGesture gesture ) { - EditorShortcutAttribute? best = registration is null ? null : GetApplicableShortcuts(registration, area).FirstOrDefault(); - if (best is null) + if (registration is not null) { - gesture = default; - return false; + bool hasExact = HasExactShortcut(registration, area); + foreach (EditorShortcutAttribute shortcut in registration.shortcuts) + { + if (hasExact ? !string.Equals(shortcut.area, area, StringComparison.Ordinal) : !string.IsNullOrEmpty(shortcut.area)) + continue; + gesture = CreateGesture(shortcut); + return true; + } } - - gesture = CreateGesture(best); - return true; + gesture = default; + return false; } private EditorExtensionCatalog.ActionRegistration? Resolve( @@ -347,12 +371,35 @@ private HotKeyGesture CreateGesture(EditorShortcutAttribute shortcut) => shortcu shortcut.modifiers, editor.keyboard.superModifierLabel ); - private static IEnumerable GetApplicableShortcuts( + private Dictionary<(KeyCode key, KeyModifier modifiers, string area), string[]> BuildShortcutIndex() + { + var candidates = new Dictionary<(KeyCode key, KeyModifier modifiers, string area), HashSet>(); + foreach (EditorExtensionCatalog.ActionRegistration registration in catalog.extensions.actions) + { + foreach (EditorShortcutAttribute shortcut in registration.shortcuts) + { + HotKeyGesture gesture = CreateGesture(shortcut); + string area = string.IsNullOrEmpty(shortcut.area) ? registration.area ?? string.Empty : shortcut.area; + var key = (gesture.key, gesture.normalizedModifiers, area); + if (!candidates.TryGetValue(key, out HashSet? actions)) + candidates.Add(key, actions = new(StringComparer.Ordinal)); + actions.Add(registration.id); + } + } + return candidates.ToDictionary(static pair => pair.Key, + static pair => pair.Value.OrderBy(static value => value, StringComparer.Ordinal).ToArray()); + } + + private static bool HasExactShortcut( EditorExtensionCatalog.ActionRegistration registration, string area ) { - bool hasExact = registration.shortcuts.Any(shortcut => !string.IsNullOrEmpty(shortcut.area) && string.Equals(shortcut.area, area, StringComparison.Ordinal)); - return registration.shortcuts.Where(shortcut => hasExact ? string.Equals(shortcut.area, area, StringComparison.Ordinal) : string.IsNullOrEmpty(shortcut.area)); + foreach (EditorShortcutAttribute shortcut in registration.shortcuts) + { + if (!string.IsNullOrEmpty(shortcut.area) && string.Equals(shortcut.area, area, StringComparison.Ordinal)) + return true; + } + return false; } private void TryCancel( diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs index 0532e6de6..834c45362 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Actions/HotKeyGesture.cs @@ -108,6 +108,11 @@ internal bool Matches(KeyPressedEvent keyEvent) keyEvent.key == key && Normalize(key, keyEvent.modifiers) == Normalize(key, modifiers); + /// + /// Gets the normalized modifiers used by generation-owned shortcut indexes. + /// + internal KeyModifier normalizedModifiers => Normalize(key, modifiers); + private static KeyModifier Normalize( KeyCode key, KeyModifier value diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs index d3c0897bf..3f3ba1b03 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuCatalog.cs @@ -5,6 +5,7 @@ using Inno.Editor.Core; namespace Inno.Editor.Interactions; + internal sealed class EditorMenuCatalog( EditorExtensionCatalog catalog, EditorActionRouter actions, @@ -12,20 +13,13 @@ Logger log ) { private readonly HashSet m_sourceFailures = new(StringComparer.Ordinal); + private Dictionary? m_templates; + internal EditorMenuModel Build(EditorMenuContext context) { ArgumentNullException.ThrowIfNull(context); - var placements = new List(); - foreach (EditorExtensionCatalog.ActionRegistration registration in catalog.extensions.actions) - { - foreach (EditorMenuAttribute menu in registration.menus) - { - if (!string.Equals(menu.area, context.area, StringComparison.Ordinal)) - continue; - placements.Add(new Placement(NormalizePath(menu.path), registration.id, menu.order, menu.separatorBefore, argument: null, status: null, isGroup: false)); - } - } - + TemplateNode[] template = GetTemplate(context.area); + var dynamicPlacements = new List(); foreach (EditorExtensionCatalog.MenuSourceRegistration registration in catalog.extensions.menuSources) { if (!string.Equals(registration.area, context.area, StringComparison.Ordinal)) @@ -36,94 +30,208 @@ internal EditorMenuModel Build(EditorMenuContext context) registration.source.Build(context, builder); string sourceName = registration.type.FullName ?? registration.type.Name; m_sourceFailures.Remove(sourceName); - placements.AddRange(builder.groups.Select(static group => new Placement(NormalizePath(group.path), actionId: null, group.order, group.separatorBefore, argument: null, status: null, isGroup: true))); - placements.AddRange(builder.items.Select(static item => new Placement(NormalizePath(item.path), item.actionId, item.order, item.separatorBefore, item.argument, status: null, isGroup: false))); + if (builder.groups.Count == 0 && builder.items.Count == 0) + continue; + var placements = new List(builder.groups.Count + builder.items.Count); + foreach (var group in builder.groups) + placements.Add(CreatePlacement(group.path, null, group.order, group.separatorBefore, null)); + foreach (var item in builder.items) + placements.Add(CreatePlacement(item.path, item.actionId, item.order, item.separatorBefore, item.argument)); + dynamicPlacements.AddRange(placements); } catch (Exception exception) { string sourceName = registration.type.FullName ?? registration.type.Name; if (m_sourceFailures.Add(sourceName)) - { log.Write(LogLevel.Error, "Editor menu source '{0}' failed: {1}", [sourceName, exception]); + } + } + if (dynamicPlacements.Count > 0) + { + foreach (Placement placement in dynamicPlacements.OrderBy(static value => value.order).ThenBy(static value => value.path, StringComparer.Ordinal)) + template = Merge(template, placement, 0); + } + return new EditorMenuModel(Resolve(template, context)); + } + + internal void InvalidateDiscovery() + { + m_templates = null; + m_sourceFailures.Clear(); + } + + private TemplateNode[] GetTemplate(string area) + { + if (m_templates is null) + { + var placementsByArea = new Dictionary>(StringComparer.Ordinal); + foreach (EditorExtensionCatalog.ActionRegistration registration in catalog.extensions.actions) + { + foreach (EditorMenuAttribute menu in registration.menus) + { + if (!placementsByArea.TryGetValue(menu.area, out List? placements)) + placementsByArea.Add(menu.area, placements = []); + placements.Add(CreatePlacement(menu.path, registration.id, menu.order, menu.separatorBefore, null)); } } + var templates = new Dictionary(StringComparer.Ordinal); + foreach ((string key, List placements) in placementsByArea) + { + var root = new MutableNode(string.Empty, 0, false); + foreach (Placement placement in placements.OrderBy(static value => value.order).ThenBy(static value => value.path, StringComparer.Ordinal)) + AddPlacement(root, placement); + templates.Add(key, FreezeTemplate(root.children.Values)); + } + m_templates = templates; } + return m_templates.TryGetValue(area, out TemplateNode[]? template) ? template : []; + } - var root = new MutableNode(string.Empty, 0, false); - foreach (Placement placement in placements.OrderBy(static value => value.order).ThenBy(static value => value.path, StringComparer.Ordinal)) + private IReadOnlyList Resolve( + TemplateNode[] nodes, + EditorMenuContext context + ) { + if (nodes.Length == 0) + return Array.Empty(); + var result = new List(nodes.Length); + foreach (TemplateNode node in nodes) + { + IReadOnlyList children = Resolve(node.children, context); + Placement? actionPlacement = null; + EditorActionState state = EditorActionState.hidden; + int order = children.Count > 0 ? children[0].order : node.order; + bool separatorBefore = false; + foreach (Placement placement in node.placements) + { + if (placement.actionId is string action) + { + EditorActionState candidate = actions.Query(action, context.CreateActionContext(placement.argument)); + if (!candidate.isVisible) + continue; + actionPlacement = placement; + state = candidate; + } + order = placement.order; + separatorBefore = placement.separatorBefore; + } + if (actionPlacement is null) + { + if (children.Count == 0) + continue; + state = EditorActionState.enabled; + } + string label = string.IsNullOrWhiteSpace(state.displayName) ? node.label : state.displayName!; + result.Add(new EditorMenuItem(label, actionPlacement?.actionId ?? string.Empty, order, + separatorBefore, state, children, actionPlacement?.argument)); + } + for (int index = 1; index < result.Count; index++) { - AddPlacement(root, placement, context); + if (result[index - 1].order <= result[index].order) + continue; + result.Sort(static ( + left, + right + ) => left.order != right.order ? left.order.CompareTo(right.order) : StringComparer.Ordinal.Compare(left.label, right.label)); + break; } + return result.AsReadOnly(); + } - return new EditorMenuModel(Freeze(root.children.Values)); + private static Placement CreatePlacement( + string path, + string? actionId, + int order, + bool separatorBefore, + object? argument + ) { + string[] segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (segments.Length == 0) + throw new InvalidOperationException("An editor menu path must contain at least one segment."); + return new Placement(string.Join('/', segments), segments, actionId, order, separatorBefore, argument); } - private void AddPlacement( + private static void AddPlacement( MutableNode root, - Placement placement, - EditorMenuContext context + Placement placement ) { - EditorActionContext actionContext = context.CreateActionContext(placement.argument); - EditorActionState state = placement.isGroup ? EditorActionState.enabled : placement.status ?? actions.Query(placement.actionId!, actionContext); - if (!state.isVisible) - return; - string[] segments = placement.path.Split('/'); MutableNode current = root; - for (int i = 0; i < segments.Length; i++) + for (int index = 0; index < placement.segments.Length; index++) { - string segment = segments[i]; + string segment = placement.segments[index]; if (!current.children.TryGetValue(segment, out MutableNode? child)) { - child = new MutableNode(segment, placement.order, i == segments.Length - 1 && placement.separatorBefore); + child = new MutableNode(segment, placement.order, + index == placement.segments.Length - 1 && placement.separatorBefore); current.children.Add(segment, child); } - current = child; } - current.order = placement.order; current.separatorBefore = placement.separatorBefore; - if (placement.isGroup) - return; - current.actionId = placement.actionId; - current.argument = placement.argument; - current.state = state; - if (!string.IsNullOrWhiteSpace(state.displayName)) - current.label = state.displayName!; + current.placements.Add(placement); } - private static IReadOnlyList Freeze(IEnumerable nodes) - { - var result = new List(); - foreach (MutableNode node in nodes.OrderBy(static value => value.order).ThenBy(static value => value.label, StringComparer.Ordinal)) + private static TemplateNode[] FreezeTemplate(IEnumerable nodes) + => nodes.OrderBy(static value => value.order).ThenBy(static value => value.label, StringComparer.Ordinal) + .Select(static node => new TemplateNode(node.label, node.order, node.separatorBefore, + node.placements.OrderBy(static placement => placement.order).ToArray(), FreezeTemplate(node.children.Values))).ToArray(); + + private static TemplateNode[] Merge( + TemplateNode[] nodes, + Placement placement, + int depth + ) { + string label = placement.segments[depth]; + int index = -1; + for (int candidate = 0; candidate < nodes.Length; candidate++) { - IReadOnlyList children = Freeze(node.children.Values); - EditorActionState state = node.actionId is null ? new EditorActionState(children.Count > 0, children.Count > 0) : node.state; - if (!state.isVisible && children.Count == 0) + if (!string.Equals(nodes[candidate].label, label, StringComparison.Ordinal)) continue; - result.Add(new EditorMenuItem(node.label, node.actionId ?? string.Empty, node.order, node.separatorBefore, state, children, node.argument)); + index = candidate; + break; } - + TemplateNode current = index >= 0 ? nodes[index] : new TemplateNode(label, placement.order, false, [], []); + bool leaf = depth == placement.segments.Length - 1; + Placement[] placements = current.placements; + if (leaf) + { + int insertion = 0; + while (insertion < placements.Length && placements[insertion].order <= placement.order) + insertion++; + placements = new Placement[current.placements.Length + 1]; + Array.Copy(current.placements, placements, insertion); + placements[insertion] = placement; + Array.Copy(current.placements, insertion, placements, insertion + 1, current.placements.Length - insertion); + } + TemplateNode replacement = new(label, current.order, current.separatorBefore, placements, + leaf ? current.children : Merge(current.children, placement, depth + 1)); + var result = new TemplateNode[nodes.Length + (index < 0 ? 1 : 0)]; + Array.Copy(nodes, result, nodes.Length); + result[index >= 0 ? index : nodes.Length] = replacement; + Array.Sort(result, static ( + left, + right + ) => left.order != right.order ? left.order.CompareTo(right.order) : StringComparer.Ordinal.Compare(left.label, right.label)); return result; } - private static string NormalizePath(string path) - { - string normalized = string.Join('/', path.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); - if (string.IsNullOrEmpty(normalized)) - throw new InvalidOperationException("An editor menu path must contain at least one segment."); - return normalized; - } - private sealed record Placement( string path, + string[] segments, string? actionId, int order, bool separatorBefore, - object? argument, - EditorActionState? status, - bool isGroup + object? argument + ); + + private sealed record TemplateNode( + string label, + int order, + bool separatorBefore, + Placement[] placements, + TemplateNode[] children ); + private sealed class MutableNode( string label, int order, @@ -133,9 +241,7 @@ bool separatorBefore internal string label = label; internal int order = order; internal bool separatorBefore = separatorBefore; + internal readonly List placements = []; internal readonly Dictionary children = new(StringComparer.Ordinal); - internal string? actionId; - internal object? argument; - internal EditorActionState state = EditorActionState.hidden; } } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs index 226a2c57a..98da7c301 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs @@ -483,6 +483,7 @@ internal void RollbackGenerationTransition() internal void CompleteGenerationTransition() { Actions.InvalidateDiscovery(); + m_menus?.InvalidateDiscovery(); selection.Rebind(); m_focus = m_focus?.Rebind(this); m_previousGenerationSelection = null; @@ -494,6 +495,7 @@ internal void CompleteGenerationTransition() internal void Shutdown(IReadOnlyList actions) { Actions.Clear(actions); + m_menus?.InvalidateDiscovery(); Drops.Cancel(); m_catalog = null; selection.Clear(); diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Commands.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Commands.cs new file mode 100644 index 000000000..e27c65125 --- /dev/null +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Commands.cs @@ -0,0 +1,117 @@ +using System; +using System.Linq; +using Inno.Core.Input; +using Inno.Editor.Interactions; +using Xunit; + +namespace Inno.Editor.Interactions.Tests; + +public sealed partial class EditorRuntimeTests +{ + [Fact] + public void StaticMenuTemplatesKeepAvailabilityLabelsAndReasonsFresh() + { + var target = new MenuStateTarget(); + var interaction = m_runtime.interactions.For("tests/static-menu", target); + for (int index = 0; index < 3; index++) + { + target.label = "Current " + index; + target.enabled = index != 1; + target.isChecked = index == 2; + EditorMenuItem leaf = Assert.Single(Assert.Single(interaction.BuildMenu().items).children); + Assert.Equal(target.label, leaf.label); + Assert.Equal(target.enabled, leaf.status.isEnabled); + Assert.Equal(target.isChecked, leaf.status.isChecked); + Assert.Equal(target.enabled ? null : "Current owner is read-only.", leaf.status.unavailableReason); + } + target.visible = false; + Assert.Empty(interaction.BuildMenu().items); + m_types.Rebuild(); + target.visible = true; + target.label = "After registry refresh"; + Assert.Equal(target.label, Assert.Single(Assert.Single(interaction.BuildMenu().items).children).label); + GC.KeepAlive(target); + } + + [Fact] + public void HiddenHigherOrderPlacementDoesNotEraseTheVisibleAction() + { + var target = new MenuStateTarget { visible = false }; + var interaction = m_runtime.interactions.For("tests/shared-menu", target); + Assert.Equal("tests.menu-fallback", Assert.Single(interaction.BuildMenu().items).actionId); + target.visible = true; + Assert.Equal("tests.menu-conditional", Assert.Single(interaction.BuildMenu().items).actionId); + target.visible = false; + Assert.Equal("tests.menu-fallback", Assert.Single(interaction.BuildMenu().items).actionId); + GC.KeepAlive(target); + } + + [Fact] + public void FailedDynamicSourceCannotPublishItsPartialMenu() + { + Assert.Empty(m_runtime.interactions.For("tests/failed-menu").BuildMenu().items); + Assert.NotEmpty(m_runtime.interactions.For("tests/menu").BuildMenu().items); + } + + [Fact] + public void ShortcutIndexRefreshPreservesDispatchAndRejectsRepeats() + { + m_runtime.interactions.For("tests/multiple-shortcuts").Focus(); + m_runtime.HandleKeyPressed(new(0, KeyCode.F3)); + Assert.Equal(1, MultiShortcutAction.executeCount); + m_types.Rebuild(); + m_runtime.HandleKeyPressed(new(0, KeyCode.F4)); + Assert.Equal(2, MultiShortcutAction.executeCount); + m_runtime.HandleKeyPressed(new(0, KeyCode.F4, repeat: true)); + Assert.Equal(2, MultiShortcutAction.executeCount); + } +} + +public sealed class MenuStateTarget +{ + public bool visible = true; + public bool enabled = true; + public bool isChecked; + public string label = "Current"; +} + +[EditorAction("tests.menu-state", "tests/static-menu")] +[EditorMenu("tests/static-menu", "Tools/State")] +public sealed class MenuStateAction : EditorAction +{ + protected override EditorActionState Query(EditorActionContext context) + => context.target is MenuStateTarget target + ? new(target.visible, target.enabled, target.isChecked, target.label, "Current owner is read-only.") + : EditorActionState.hidden; + + protected override void Execute(EditorActionContext context) { } +} + +[EditorAction("tests.menu-fallback", "tests/shared-menu")] +[EditorMenu("tests/shared-menu", "Shared", order: 100)] +public sealed class MenuFallbackAction : EditorAction +{ + protected override void Execute(EditorActionContext context) { } +} + +[EditorAction("tests.menu-conditional", "tests/shared-menu")] +[EditorMenu("tests/shared-menu", "Shared", order: 200)] +public sealed class MenuConditionalAction : EditorAction +{ + protected override EditorActionState Query(EditorActionContext context) + => context.target is MenuStateTarget { visible: true } ? EditorActionState.enabled : EditorActionState.hidden; + + protected override void Execute(EditorActionContext context) { } +} + +[EditorMenuSource("tests/failed-menu")] +public sealed class RejectedMenuSource : EditorMenuSource +{ + public override void Build( + EditorMenuContext context, + EditorMenuBuilder builder + ) { + builder.Add("Partial", "tests.menu"); + throw new InvalidOperationException("Candidate menu source failed."); + } +} diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Performance.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Performance.cs new file mode 100644 index 000000000..2b123d77b --- /dev/null +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Performance.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics; +using System.Text.Json; +using Inno.Editor.Core; +using Xunit; + +namespace Inno.Editor.Interactions.Tests; + +public sealed partial class EditorRuntimeTests +{ + [Fact] + [Trait("Category", "Performance")] + public void PublicCommandAndStatisticsPathsProduceRepeatableSamples() + { + var interaction = m_runtime.interactions.For("tests/menu"); + Measure("menu-open", () => Assert.NotEmpty(interaction.BuildMenu().items)); + var statistics = new EditorStatistics(); + for (int index = 0; index < 100; index++) + { + statistics.Publish(new EditorStatistic( + new("metric/" + index), + new("group/" + index / 10), + "Group " + index / 10, + "Metric " + index, + index.ToString(), + order: index)); + } + Measure("statistics-unchanged", () => Assert.Equal(100, statistics.GetSnapshot().Count)); + } + + private static void Measure( + string name, + Action operation + ) { + const int iterations = 2_000; + for (int warmup = 0; warmup < 200; warmup++) + operation(); + for (int sample = 0; sample < 5; sample++) + { + long before = GC.GetAllocatedBytesForCurrentThread(); + long start = Stopwatch.GetTimestamp(); + for (int iteration = 0; iteration < iterations; iteration++) + operation(); + double milliseconds = Stopwatch.GetElapsedTime(start).TotalMilliseconds; + long bytes = GC.GetAllocatedBytesForCurrentThread() - before; + Console.WriteLine(JsonSerializer.Serialize(new { workload = name, sample, iterations, milliseconds, bytes })); + } + } +} From 6a656b674a90ebdda740ba6cb407f2f974564619 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 21:01:48 +0800 Subject: [PATCH 08/54] fix(build): unify compiler tool references across SDK graphs --- Directory.Build.targets | 19 +++ docs/build/Inno.Build.TaskHosting.md | 2 + .../CompilerToolBuildScopeTests.cs | 110 ++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs diff --git a/Directory.Build.targets b/Directory.Build.targets index b452cc3b9..f8201e3e9 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -76,6 +76,25 @@ + + + + + + + + <_InnoCompilerToolPropertyPattern>(?i)(^|;)(?:$([System.String]::Copy('$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner').Replace(';', '|')))=[^;]* + + + <_MSBuildProjectReferenceExistent Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(_MSBuildProjectReferenceExistent.AdditionalPropertiesFromProject)', '<IsRoslynComponent>\s*(?i:true)\s*</IsRoslynComponent>'))"> + true + $([System.Text.RegularExpressions.Regex]::Replace('%(_MSBuildProjectReferenceExistent.AdditionalProperties)', '$(_InnoCompilerToolPropertyPattern)', '')) + %(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner + + + + true diff --git a/docs/build/Inno.Build.TaskHosting.md b/docs/build/Inno.Build.TaskHosting.md index 65c2e34b7..5d6d269e4 100644 --- a/docs/build/Inno.Build.TaskHosting.md +++ b/docs/build/Inno.Build.TaskHosting.md @@ -21,6 +21,8 @@ Solution 命令构建通过 `Directory.Solution.targets` 为同一次构建传 owner 表示本次执行的所有权,其随机值本身不进入完整 Task runtime 的输入指纹。请求求值仍保留该属性;若导入文件、源码选择或 `DefineConstants` 等实际编译输入由它改变,这些求值结果仍参与指纹,不能误用上次结果。Design-time Build 不准备 publisher。 IDE 可以把已经求值的 `ProjectInstance` 交给另一个 MSBuild worker。worker 重建 `UsingTask` 注册时可能再次求值,因此不能假设其随机加载身份仍等于传入的普通属性。共同 targets 使用 SDK inline task 捕获任务注册阶段的加载身份,再令 publisher 复制、runtime 发布与后续输入使用该身份。加载路径仍按实际注册隔离,不依赖 IDE 设置、固定机器路径或预先存在的缓存。 最小 owner 引导消费 SDK Build 实际返回的 Foundation 程序集路径,并保留调用方明确选择的 `ArtifactsPath`,不拼接目标框架或强制写入普通 bin。它从已构建文件读取字节执行公开协议,不让长寿命 IDE 构建宿主锁住 SDK 输出;下一次编译可以正常更新该文件。此阶段只交换 BCL 字符串与 Task,不把 Foundation 类型或委托保存到共享注册表。 + +生成器通过 SDK `AdditionalTargetFrameworkInfoProperty` 声明自身的 `IsRoslynComponent` 角色。同一工具无论经 Analyzer、脚本编译器的私有运行引用,还是 SDK 追加的传递引用到达,都使用同一中立工具求值;产品参数、父级 `BuildProjectReferences` 和 Task 操作 owner 不属于工具编译输入。`ArtifactsPath` 和 Configuration 仍保留,正式源码与私有 Task 输出继续隔离。直接对生成器显式传入的 RID 仍由 SDK 正常求值,此规则只规范消费工具的项目引用,不改写用户的直接请求。 `BuildTaskRuntimeTask` 使用同一 `artifacts/build-tools/tasks/workspaces/shared` 根的 `FileLease` 协调完整 Task 项目编译;等待时通过 MSBuild `Yield`/`Reacquire` 释放节点,失败、取消或异常都释放写 lease。 私有 Task 编译明确禁用共享 C# compiler server;可复用源码 workspace 的 SDK 子进程同样使用 `--disable-build-servers`。编译完成后不会让另一个长寿命编译宿主持有 workspace 中的生成器 DLL。普通 IDE 的编译服务器配置不由此改变。 SDK 与项目构建仍由当前 MSBuild engine 执行,保留明确属性和移除产品属性的机制,不创建第二个 CLI 或通过 PATH 重新选择 SDK。完整 Task 项目返回实际运行文件的逻辑路径及 SHA-256。 diff --git a/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs new file mode 100644 index 000000000..1342f6217 --- /dev/null +++ b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Inno.Build.Toolchains; +using Inno.Testing.Source; +using Microsoft.Build.Logging; +using Xunit; + +namespace Inno.Build.Tests; + +public sealed class CompilerToolBuildScopeTests +{ + [Fact] + public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild() + { + string repository = TestRepository.GetRoot(typeof(CompilerToolBuildScopeTests).Assembly); + string root = Path.Combine(Path.GetTempPath(), "InnoCompilerToolScope", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(root); + try + { + foreach (string suffix in new[] { "props", "targets" }) + File.WriteAllText(Path.Combine(root, "Directory.Build." + suffix), + ""); + WriteProject(root, "Tool", """ + + true + + netstandard2.0 + + + """); + WriteProject(root, "Runtime", """ + + net10.0 + + + """); + WriteProject(root, "Analyzer", """ + + net10.0 + + + """); + WriteProject(root, "Root", """ + + net10.0 + + + + + + """); + string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(3)); + for (int run = 0; run < 2; run++) + { + string binlog = Path.Combine(root, run + ".binlog"); + await ToolchainEnvironment.RunAsync(dotnet, + ["build", "Root/Root.csproj", "--nologo", "-m:4", "-nodeReuse:false", + "-p:ArtifactsPath=" + Path.Combine(root, "managed"), "-p:InnoNativeTarget=fixture-target", "-bl:" + binlog], + root, timeout.Token, DotNetSdkEnvironment.Create(dotnet)); + int compilations = 0; + var identities = new HashSet(StringComparer.Ordinal); + var events = new BinaryLogReplayEventSource(); + events.ProjectStarted += ( + _, + project + ) => { + if (Path.GetFileName(project.ProjectFile) != "Tool.csproj" || !string.IsNullOrEmpty(project.TargetNames)) + return; + Assert.NotNull(project.GlobalProperties); + Assert.DoesNotContain("InnoNativeTarget", project.GlobalProperties!.Keys); + Assert.DoesNotContain("BuildProjectReferences", project.GlobalProperties.Keys); + Assert.DoesNotContain("InnoTaskBootstrapOwner", project.GlobalProperties.Keys); + identities.Add(string.Join(";", project.GlobalProperties.OrderBy(static pair => pair.Key) + .Select(static pair => pair.Key + "=" + pair.Value))); + }; + events.TaskStarted += ( + _, + task + ) => { + if (task.TaskName == "Csc" && Path.GetFileName(task.ProjectFile) == "Tool.csproj") + compilations++; + }; + events.Replay(binlog); + Assert.Single(identities); + Assert.Equal(run == 0 ? 1 : 0, compilations); + } + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + private static void WriteProject( + string root, + string name, + string source + ) { + string directory = Path.Combine(root, name); + Directory.CreateDirectory(directory); + File.WriteAllText(Path.Combine(directory, name + ".csproj"), source); + File.WriteAllText(Path.Combine(directory, name + ".cs"), "public sealed class " + name + "Marker { }"); + } +} From b7a8924dfb0b9611ad8725f508a1d43cc30e6bfe Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Tue, 27 Oct 2026 21:16:14 +0800 Subject: [PATCH 09/54] perf(editor): reuse unchanged statistic snapshots --- docs/editor/Inno.Editor.Core.md | 2 + .../Runtime/EditorStatistics.cs | 39 +++++++++++-- .../EditorRuntimeTests.Statistics.cs | 58 +++++++++++++++++++ 3 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Statistics.cs diff --git a/docs/editor/Inno.Editor.Core.md b/docs/editor/Inno.Editor.Core.md index 760c78478..8bdaf36ed 100644 --- a/docs/editor/Inno.Editor.Core.md +++ b/docs/editor/Inno.Editor.Core.md @@ -45,6 +45,8 @@ Commands、Menus、DragDrop、Selection 和 Rename 状态属于 `Inno.Editor.Int | `EditorStatistics` | 当前帧发布、同 ID 替换和上一完成帧 handoff 的顺序无关交换。 | | `EditorRuntime` | 表现无关的 `Start`、`Update(EditorFrame)`、`Dispose` 抽象。 | +`EditorStatistics.GetSnapshot()` 返回按稳定 group/item 顺序排列的不可变快照。内容和排序不变时复用同一实例,旧快照不会被后续发布修改。重复发布相同值不会迫使消费 Panel 重建分组;当前帧与上一完成帧的 handoff 仍保持,停止发布的项按原帧生命周期退出。发布和快照操作由同一内部同步边界保护,不保存扩展类型或回调。 + `EditorContext` 是由 Application host 创建并注入扩展的中立数据,不承担路由: ```csharp diff --git a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs index a31c8a076..4b46f6946 100644 --- a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs +++ b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs @@ -185,8 +185,10 @@ public EditorStatistic( public sealed class EditorStatistics { private readonly object m_sync = new(); - private readonly Dictionary m_current = []; + private Dictionary m_current = []; private Dictionary m_completed = []; + private IReadOnlyList? m_snapshot; + private bool m_snapshotDirty = true; /// /// Publishes or replaces one statistic for the current editor frame. @@ -201,7 +203,12 @@ public void Publish(EditorStatistic statistic) { ArgumentNullException.ThrowIfNull(statistic); lock (m_sync) + { + EditorStatistic? previous = m_current.GetValueOrDefault(statistic.id) ?? m_completed.GetValueOrDefault(statistic.id); + if (previous is null || !SameValue(previous, statistic)) + m_snapshotDirty = true; m_current[statistic.id] = statistic; + } } /// @@ -225,22 +232,28 @@ public void Publish(IEnumerable statistics) /// frame for providers that draw after their consumer. /// /// - /// A detached, sorted snapshot containing at most one value for each stable statistic ID. + /// An immutable, sorted snapshot containing at most one value for each stable statistic ID. + /// Unchanged content reuses the same snapshot; previously returned snapshots remain valid. /// public IReadOnlyList GetSnapshot() { lock (m_sync) { + if (!m_snapshotDirty && m_snapshot is not null) + return m_snapshot; var merged = new Dictionary(m_completed); foreach ((EditorStatisticId id, EditorStatistic statistic) in m_current) merged[id] = statistic; - return merged.Values + EditorStatistic[] values = merged.Values .OrderBy(static value => value.groupOrder) .ThenBy(static value => value.groupName, StringComparer.OrdinalIgnoreCase) .ThenBy(static value => value.order) .ThenBy(static value => value.label, StringComparer.OrdinalIgnoreCase) .ThenBy(static value => value.id.value, StringComparer.Ordinal) .ToArray(); + m_snapshot = Array.AsReadOnly(values); + m_snapshotDirty = false; + return m_snapshot; } } @@ -248,8 +261,26 @@ internal void AdvanceFrame() { lock (m_sync) { - m_completed = new Dictionary(m_current); + foreach (EditorStatisticId id in m_completed.Keys) + { + if (m_current.ContainsKey(id)) + continue; + m_snapshotDirty = true; + break; + } + (m_completed, m_current) = (m_current, m_completed); m_current.Clear(); } } + + private static bool SameValue( + EditorStatistic first, + EditorStatistic second + ) => ReferenceEquals(first, second) + || first.groupId == second.groupId + && first.groupOrder == second.groupOrder + && first.order == second.order + && string.Equals(first.groupName, second.groupName, StringComparison.Ordinal) + && string.Equals(first.label, second.label, StringComparison.Ordinal) + && string.Equals(first.value, second.value, StringComparison.Ordinal); } diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Statistics.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Statistics.cs new file mode 100644 index 000000000..e3ca8b7f4 --- /dev/null +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Statistics.cs @@ -0,0 +1,58 @@ +using System.Linq; +using Inno.Editor.Core; +using Xunit; + +namespace Inno.Editor.Interactions.Tests; + +public sealed partial class EditorRuntimeTests +{ + [Fact] + public void UnchangedStatisticsReuseAnImmutableSnapshot() + { + var statistics = new EditorStatistics(); + statistics.Publish(new EditorStatistic(new("frame"), new("timing"), "Timing", "Frame", "16 ms")); + var first = statistics.GetSnapshot(); + Assert.Same(first, statistics.GetSnapshot()); + statistics.Publish(new EditorStatistic(new("frame"), new("timing"), "Timing", "Frame", "16 ms")); + Assert.Same(first, statistics.GetSnapshot()); + statistics.Publish(new EditorStatistic(new("frame"), new("timing"), "Timing", "Frame", "8 ms")); + var changed = statistics.GetSnapshot(); + Assert.NotSame(first, changed); + Assert.Equal("16 ms", Assert.Single(first).value); + Assert.Equal("8 ms", Assert.Single(changed).value); + } + + [Fact] + public void StatisticFrameHandoffRetainsLateProvidersAndRemovesExpiredContributions() + { + EditorStatistics statistics = m_runtime.context.statistics; + statistics.Publish(new EditorStatistic(new("first"), new("sampling"), "Sampling", "First", "1")); + var first = statistics.GetSnapshot(); + m_runtime.Update(new EditorFrame(0.016f, 1f, isFocused: true)); + Assert.Same(first, statistics.GetSnapshot()); + statistics.Publish(new EditorStatistic(new("second"), new("sampling"), "Sampling", "Second", "2")); + var combined = statistics.GetSnapshot(); + Assert.Equal(2, combined.Count(value => value.groupId == new EditorStatisticGroupId("sampling"))); + m_runtime.Update(new EditorFrame(0.016f, 2f, isFocused: true)); + Assert.Equal("second", Assert.Single(statistics.GetSnapshot().Where(value => value.groupId == new EditorStatisticGroupId("sampling"))).id.value); + Assert.Equal(2, combined.Count); + m_runtime.Update(new EditorFrame(0.016f, 3f, isFocused: true)); + Assert.DoesNotContain(statistics.GetSnapshot(), value => value.groupId == new EditorStatisticGroupId("sampling")); + } + + [Fact] + public void StatisticGroupingAndOrderChangesInvalidateTheSnapshot() + { + var statistics = new EditorStatistics(); + statistics.Publish(new EditorStatistic(new("a"), new("old"), "Old", "First", "1", groupOrder: 1)); + statistics.Publish(new EditorStatistic(new("b"), new("second"), "Second", "Second", "2", groupOrder: 2)); + var original = statistics.GetSnapshot(); + statistics.Publish(new EditorStatistic(new("b"), new("new"), "New", "Renamed", "2", groupOrder: 0)); + var changed = statistics.GetSnapshot(); + Assert.NotSame(original, changed); + Assert.Equal("b", changed[0].id.value); + Assert.Equal("New", changed[0].groupName); + Assert.Equal("Renamed", changed[0].label); + Assert.Equal("a", original[0].id.value); + } +} From 273c42ad32be511c21fa21b1e99626af708f29c8 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 04:18:58 +0800 Subject: [PATCH 10/54] fix(assets): preserve source identities during filesystem refresh Prepare complete source entries before updating the active index, reuse unchanged Core identities, and retire removed entries after successful preparation. Cover identity stability and failed enumeration through public filesystem contracts. Refs #43. --- .../IDENTITY_REFERENCE_RELOAD_STANDARD.md | 2 + docs/assets/Inno.Assets.Pipeline.md | 1344 +++++++---------- .../Sources/AssetFileSystem.cs | 147 +- .../AssetFileSystemTests.cs | 85 ++ 4 files changed, 757 insertions(+), 821 deletions(-) diff --git a/docs/architecture/IDENTITY_REFERENCE_RELOAD_STANDARD.md b/docs/architecture/IDENTITY_REFERENCE_RELOAD_STANDARD.md index 2f8a07077..941957dce 100644 --- a/docs/architecture/IDENTITY_REFERENCE_RELOAD_STANDARD.md +++ b/docs/architecture/IDENTITY_REFERENCE_RELOAD_STANDARD.md @@ -108,6 +108,8 @@ Editor 的 identity-aware API 必须从当前明确 scope 解析 allocator;不 ### 3.3 Registry 的唯一权威性 +资产文件索引的无内容变化刷新不能无条件注销、重建全部 runtime identity。`AssetFileSystem` 以完整候选对账当前 persistent entry ID,复用有效条目并退休真正移除的条目;普通枚举失败保留原索引。UI 继续通过 Core Identity 解析,不另建 path → live object 表,也不依赖每帧全量重建来维持正确性。 + - live object 的 `persistentId/runtimeId → instance` 解析以 `IdentityAllocator` 为唯一权威。 - 领域可以建立 `path/semantic ID → persistentId` 的二级索引,但不得再维护长期的 `path/semantic ID → collectible object` 权威表。 - 内部 dense/sparse array 可以优化 allocator;slot 必须带 generation,slot 复用后旧 runtime ID 必须失效。 diff --git a/docs/assets/Inno.Assets.Pipeline.md b/docs/assets/Inno.Assets.Pipeline.md index 29cd4e12d..f313e08b5 100644 --- a/docs/assets/Inno.Assets.Pipeline.md +++ b/docs/assets/Inno.Assets.Pipeline.md @@ -9,6 +9,8 @@ Catalog 文件提升在 journal 删除失败时先恢复旧 snapshot;外层 me ## Missing 与只读失败记录 +`AssetFileSystem.Refresh()` 先枚举和验证完整候选,再更新现有索引。persistent entry ID 未改变的源条目保留同一实例和 runtime identity;普通 Rescan 不会让当前 Browser 选择及派生视图失效。新增条目通过 Core Identity 注册,移除条目在候选准备成功后退休。枚举、权限、重复 identity 或注册失败保留上一次完整索引,不发布部分文件树;只读 Mount 和 source 过滤规则保持由各自 owner 管理。 + Asset reference 和 dependency record 只按 persistent ID 解析;last-known path 仅作诊断。旧路径出现新 identity 时原引用保持 Missing。 Tombstone 保留中立 property bytes、依赖描述与 last-successful artifact key;释放 live payload/强引用,不将旧 artifact 视为当前可加载内容。Tombstone 的旧 key 不在活动 path 集合中,因此仍允许正常 CAS 回收;保留 key 不等于永久保留缓存文件。 @@ -235,208 +237,143 @@ static bool AdvanceSampleImport( 示例中的 `assets` 已由宿主初始化并持有,调用须在其 owner thread。Sample 工作流调用 `PrepareSampleImport` 后保留事务和初始为 false 的 `validationStarted`,逐帧调用示例;返回 true 后不再推进。失败或取消进入 Rollback,遇到退休未完成时继续持有事务与依赖,不能以 Dispose 抛异常为由提前释放 AssetPipeline。 -## 当前源码公开 API 清单 - -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Assets.Pipeline.AssetBuildContext`1` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetBuildContext.AssetBuildContext(TDefinition definition, System.Collections.Generic.IReadOnlyList inputs)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L25) | Creates a build context.
`definition`:The build definition.
`inputs`:The immutable input catalog snapshots. | — | — | -| [`public TDefinition Inno.Assets.Pipeline.AssetBuildContext.definition { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L36) | Gets the build definition. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetBuildContext.inputs { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L41) | Gets the input catalog snapshots. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetBuildContext where TDefinition : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L14) | Provides a stable input snapshot to an aggregate asset build. | — | — | - -### `Inno.Assets.Pipeline.AssetBuildProcessor` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Assets.Pipeline.AssetBuildProcessor.AssetBuildProcessor()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L37) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public abstract System.Type Inno.Assets.Pipeline.AssetBuildProcessor.definitionType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L51) | Gets the definition type accepted by this processor. | — | — | -| [`public string Inno.Assets.Pipeline.AssetBuildProcessor.processorId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L44) | Gets the stable processor identifier used by build cache keys. | — | — | -| [`public abstract class Inno.Assets.Pipeline.AssetBuildProcessor`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L37) | Defines an automatically discovered aggregate asset build processor. | — | — | -### `Inno.Assets.Pipeline.AssetBuildProcessor`1` +## Artifact 与部署摘要补充契约 - - - - + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| 声明 | 行为与结果 | 所有权 / 线程 | 失败或限制 | | --- | --- | --- | --- | -| [`protected Inno.Assets.Pipeline.AssetBuildProcessor.AssetBuildProcessor()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L78) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected abstract System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetBuildProcessor.BuildAsync(Inno.Assets.Pipeline.AssetBuildContext context, Inno.Assets.Pipeline.AssetArtifactWriter output, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L101) | Builds immutable outputs from a consistent asset snapshot.
返回:An operation that completes when all outputs are staged.
`context`:The build context.
`output`:The candidate output writer.
`cancellationToken`:Cancellation for the build. | — | — | -| [`public override sealed System.Type Inno.Assets.Pipeline.AssetBuildProcessor.definitionType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L84) | Gets the concrete type handled by this extension implementation. | — | — | -| [`public abstract class Inno.Assets.Pipeline.AssetBuildProcessor where TDefinition : Inno.Assets.AssetObject : Inno.Assets.Pipeline.AssetBuildProcessor`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L78) | Provides a strongly typed aggregate asset build processor. | — | — | - -### `Inno.Assets.Pipeline.AssetBuildProcessorAttribute` - - - - +| [`public sealed class Inno.Assets.Pipeline.AssetArtifactWriter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | 建立本次 aggregate build 的输出/诊断收集器。 | 内部结果由 Pipeline 使用;公开 Writer 不提供物理路径。 | 输出不能重复写入。 | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetArtifactWriter.WriteAsync(string outputName, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope = Inno.Assets.Pipeline.AssetDeploymentScope.Runtime)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L36) | 复制 bytes,暂存唯一的命名输出;返回已完成的 ValueTask。AuthoringOnly 输出不进入运行闭包。 | Writer 属于当前 aggregate build;调用后可释放/修改传入缓冲区。 | 空名称、非法 scope、将 runtime/asset-state 标为 AuthoringOnly、重复名称或预取消均失败。 | +| [`public void Inno.Assets.Pipeline.AssetArtifactWriter.ReportDiagnostic(string message)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L60) | 向本次构建加入诊断文本。 | 不开始导入、发布或文件 IO。 | 空白诊断会抛 ArgumentException。 | +| [`public Inno.Assets.Pipeline.AssetArtifactWriter.AssetArtifactWriter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | 建立本次 aggregate build 的输出/诊断收集器。 | 内部结果由 Pipeline 使用;公开 Writer 不提供物理路径。 | 输出不能重复写入。 | -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetBuildProcessorAttribute.AssetBuildProcessorAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L22) | Creates build-processor discovery metadata.
`id`:Globally stable build processor identifier. | — | — | -| [`public string Inno.Assets.Pipeline.AssetBuildProcessorAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L31) | Gets the globally stable build processor identifier. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetBuildProcessorAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L14) | Declares the immutable cache protocol identity of an automatically discovered asset build processor. | — | — | +## 当前源码公开 API 清单 -### `Inno.Assets.Pipeline.AssetCacheOptions` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Assets.Pipeline.AssetCacheOptions Inno.Assets.Pipeline.AssetCacheOptions.CreateDefault()`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L26) | Creates the default cache policy.
返回:The default cache options. | — | — | -| [`public System.TimeSpan Inno.Assets.Pipeline.AssetCacheOptions.garbageCollectionGracePeriod { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L18) | Gets the minimum age of an unreachable artifact before it can be collected. | — | — | -| [`public long Inno.Assets.Pipeline.AssetCacheOptions.maximumSizeBytes { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L13) | Gets the maximum artifact cache size in bytes, or zero for no size limit. | — | — | -| [`public readonly struct Inno.Assets.Pipeline.AssetCacheOptions`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L8) | Controls cleanup of rebuildable asset database data. | — | — | - -### `Inno.Assets.Pipeline.AssetCatalogCandidate` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Assets.Pipeline.AssetCatalogCandidate.Commit()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L48) | Publishes staged source metadata and atomically promotes the validated catalog, compensating metadata on failure. | — | `System.InvalidOperationException`:Thrown when the candidate has already been committed or disposed.
`System.IO.IOException`:Source metadata changed externally, a sidecar cannot be written, or the catalog cannot be promoted.
`System.UnauthorizedAccessException`:The operating system rejects a source sidecar or catalog write, including a directory at a file destination. | -| [`public void Inno.Assets.Pipeline.AssetCatalogCandidate.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L60) | Removes the candidate catalog staging storage without disposing the candidate loader. | — | — | -| [`public Inno.Assets.Pipeline.AssetLoader Inno.Assets.Pipeline.AssetCatalogCandidate.loader { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L34) | Gets the isolated loader whose catalog and in-memory records represent the candidate generation. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetCatalogCandidate : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L14) | Owns the isolated catalog storage used to validate one candidate Asset Pipeline generation. | The caller owns Inno.Assets.Pipeline.AssetCatalogCandidate.loader and must dispose it after the candidate has either been committed or discarded. Disposing this object removes only its rebuildable staging storage. | — | - -### `Inno.Assets.Pipeline.AssetChangedEvent` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetChangedEvent.AssetChangedEvent(string relativePath, System.IO.WatcherChangeTypes changeType, string oldRelativePath = "")`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L17) | Batched file-system change event for asset source files.
`relativePath`:Changed path relative to watched root.
`changeType`:File-system change type.
`oldRelativePath`:Old path for rename operations. | — | — | -| [`public System.IO.WatcherChangeTypes Inno.Assets.Pipeline.AssetChangedEvent.changeType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L30) | Underlying file-system change type. | — | — | -| [`public string Inno.Assets.Pipeline.AssetChangedEvent.oldRelativePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L36) | Old path relative to watched root for rename operations. Empty for non-rename changes. | — | — | -| [`public string Inno.Assets.Pipeline.AssetChangedEvent.relativePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L25) | Changed path relative to watched root. | — | — | -| [`public readonly struct Inno.Assets.Pipeline.AssetChangedEvent`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L17) | Batched file-system change event for asset source files.
`relativePath`:Changed path relative to watched root.
`changeType`:File-system change type.
`oldRelativePath`:Old path for rename operations. | — | — | - -### `Inno.Assets.Pipeline.AssetDeploymentScope` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Assets.Pipeline.AssetDeploymentScope.AuthoringOnly = 1`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L16) | The asset exists only for authoring workflows and is omitted from deployed catalogs. | — | — | -| [`Inno.Assets.Pipeline.AssetDeploymentScope.Runtime = 0`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L11) | The asset is deployed and must produce a named runtime artifact output. | — | — | -| [`public enum Inno.Assets.Pipeline.AssetDeploymentScope : int`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L6) | Defines whether an imported asset participates in deployed runtime content. | — | — | - -### `Inno.Assets.Pipeline.AssetExportContext` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.IAssetArtifactLookup Inno.Assets.Pipeline.AssetExportContext.artifacts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L46) | Gets the owner-bound immutable outputs used to reconstruct editable sources. | — | — | -| [`public Inno.Core.Serialization.SerializationRegistry Inno.Assets.Pipeline.AssetExportContext.serialization { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L35) | Gets the serialization registry bound to the active export generation. | — | — | -| [`public Inno.Assets.Pipeline.AssetSerializationServices Inno.Assets.Pipeline.AssetExportContext.services { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L40) | Gets the narrow structured serialization API bound to this export generation. | — | — | -| [`public Inno.Extensibility.Types.TypeCatalog Inno.Assets.Pipeline.AssetExportContext.types { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L29) | Gets the type catalog bound to the active export generation. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetExportContext`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L12) | Provides generation-bound services for one editable asset source export. | — | — | - -### `Inno.Assets.Pipeline.AssetFileEntry` - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetFileEntry.AssetFileEntry()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L11) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetFileEntry.assetPath { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L30) | Gets the isolated source path. | — | — | -| [`public string Inno.Assets.Pipeline.AssetFileEntry.extension { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L64) | Gets the normalized lower-case file extension. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isDirectory { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L60) | Gets whether the entry represents a directory. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isReadOnly { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L40) | Gets whether source mutations are forbidden. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isSample { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L45) | Gets whether this entry is the root of an installed Plugin sample awaiting Project import. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isSampleContent { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L50) | Gets whether this entry belongs to an installed Plugin sample subtree awaiting Project import. | — | — | -| [`public string Inno.Assets.Pipeline.AssetFileEntry.name { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L16) | Gets the final source path segment, or the semantic mount-root label when this entry is a source root. | — | — | -| [`public string Inno.Assets.Pipeline.AssetFileEntry.nameWithoutExtension { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L25) | Gets the final source path segment without its last extension. | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetFileEntry.parentAssetPath { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L55) | Gets the isolated parent directory path. | — | — | -| [`public Inno.Assets.AssetSourceId Inno.Assets.Pipeline.AssetFileEntry.source { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L35) | Gets the owning source mount identity. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetFileEntry : Inno.Core.Identity.IdentityObject`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L11) | One node in the asset source filesystem index. | — | — | - -### `Inno.Assets.Pipeline.AssetFileSystem` - - - - - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(System.Collections.Generic.IReadOnlyList mounts, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool requireWritableProject = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L96) | Creates an indexed file system over isolated source mounts.
`mounts`:Complete source mount snapshot.
`autoStart`:Whether writable project source watching starts immediately.
`flushDelayMs`:Watcher batch delay in milliseconds.
`sourcePolicy`:Source filtering policy, or null for defaults.
`requireWritableProject`:Whether the project mount must support authoring mutations. Runtime catalogs may set this to false when every mounted identity directory is read-only. | — | — | -| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(string assetRoot, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L59) | Creates an indexed source file system.
`assetRoot`:The absolute source root.
`autoStart`:Whether file watching should start immediately.
`flushDelayMs`:The watcher batch delay in milliseconds.
`sourcePolicy`:The source filtering policy, or null for defaults. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L366) | Releases the resources owned by this implementation. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.Exists(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L210) | Determines whether an indexed source entry exists.
返回:true when the entry exists.
`path`:The isolated source path. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L285) | Gets immediate children of an indexed directory.
返回:The immediate child entries.
`parent`:The isolated parent directory path. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L261) | Gets a stable snapshot of indexed entries.
返回:The indexed entries.
`includeDirectories`:Whether directory entries should be included. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L308) | Polls normalized changes and refreshes the indexed source snapshot.
返回:The changes observed since the previous poll. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L319) | Polls changes and reports whether watcher recovery requires a full rescan.
返回:The changes observed since the previous poll.
`requiresFullRescan`:Whether the watcher reported an unreliable event stream. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Refresh()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L188) | Rebuilds the indexed source file snapshot. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Start()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L168) | Starts source file watching. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Stop()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L177) | Stops source file watching. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.TryGetEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L232) | Tries to resolve an indexed source entry.
返回:true when the entry exists.
`path`:The isolated source path.
`entry`:The resolved entry when available. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L335) | Waits for a quiet watcher window, refreshes the index, and returns queued changes.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L346) | Waits for queued changes and reports whether a full rescan is required.
返回:The normalized queued changes.
`requiresFullRescan`:Whether the watcher reported an unreliable event stream. | — | — | -| [`public string Inno.Assets.Pipeline.AssetFileSystem.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L37) | Gets the absolute source asset root. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.isWatching { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L42) | Gets whether source file watching is active. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetFileSystem : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L18) | Indexed asset source filesystem model backed by Inno.Assets.Pipeline.AssetWatcher. | — | — | - -### `Inno.Assets.Pipeline.AssetImportContext` - @@ -459,34 +396,9 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetImportContext.AcquireArtifact(System.Guid assetId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L106) | Acquires an immutable dependency output and records its invalidation dependency automatically.
返回:A caller-owned lease that must be disposed after reading.
`assetId`:Persistent dependency identity.
`outputName`:Stable named output protocol. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnArtifact(System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L301) | Declares an imported artifact that invalidates this imported asset.
`persistentId`:The persistent identity of the artifact owner. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnAsset(Inno.Assets.AssetDependency dependency)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L243) | Declares a direct runtime dependency by persistent descriptor.
`dependency`:The persistent dependency descriptor. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnAsset(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L230) | Declares a direct runtime dependency by isolated source path.
`path`:The isolated dependency path. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnCustomInput(string key, string fingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L320) | Declares a custom deterministic input that invalidates this asset.
`key`:The input identifier.
`fingerprint`:The current deterministic input fingerprint. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnSource(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L285) | Declares a source file that invalidates this imported asset.
`path`:The isolated dependency path. | — | — | -| [`public System.ReadOnlyMemory Inno.Assets.Pipeline.AssetImportContext.ReadSourceBytes(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L199) | Reads another source from the same candidate mount snapshot and records it as an import dependency.
返回:An immutable source snapshot owned by the import operation.
`path`:An isolated path whose mount dependency was declared when it crosses Plugin boundaries. | — | `System.InvalidOperationException`:Thrown when the source crosses an undeclared mount boundary or references the writable Project from a Plugin.
`System.IO.FileNotFoundException`:Thrown when the resolved source does not exist. | -| [`public string Inno.Assets.Pipeline.AssetImportContext.ReadSourceUtf8Text(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L216) | Reads another source as UTF-8 from the current candidate mount snapshot and records the dependency.
返回:Decoded UTF-8 text without an optional byte-order mark.
`path`:An isolated source path. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportContext.ReadUtf8Text()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L179) | Reads the source bytes as UTF-8 text.
返回:The decoded text without an optional byte-order mark. | — | — | -| [`public TAsset Inno.Assets.Pipeline.AssetImportContext.ResolveDependency(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L263) | Resolves and declares a strongly typed runtime asset dependency during import.
返回:The currently committed dependency asset.
`path`:Isolated dependency path. | — | `System.InvalidOperationException`:Thrown when the dependency cannot be imported or has another type. | -| [`public string Inno.Assets.Pipeline.AssetImportContext.absolutePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L122) | Gets the absolute source path. | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetImportContext.assetPath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L117) | Gets the isolated source path. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportContext.extension { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L171) | Gets the normalized lower-case source extension. | — | — | -| [`public Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImportContext.importSettings { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L166) | Gets the settings restored from this source's sidecar against the isolated import generation. The value is detached and changes made during import are not saved to the sidecar. | — | — | -| [`public System.Guid Inno.Assets.Pipeline.AssetImportContext.persistentId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L137) | Gets the persistent identity assigned to the source asset. | — | — | -| [`public Inno.Assets.IAssetReferenceResolver Inno.Assets.Pipeline.AssetImportContext.references { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L155) | Gets the asset-reference resolver bound to the isolated candidate generation. | — | — | -| [`public Inno.Core.Serialization.SerializationRegistry Inno.Assets.Pipeline.AssetImportContext.serialization { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L149) | Gets the serialization registry bound to the active import generation. | — | — | -| [`public Inno.Assets.Pipeline.AssetSerializationServices Inno.Assets.Pipeline.AssetImportContext.services { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L160) | Gets the narrow structured serialization API bound to this importer candidate. | — | — | -| [`public System.ReadOnlyMemory Inno.Assets.Pipeline.AssetImportContext.sourceBytes { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L127) | Gets the raw source bytes. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportContext.sourceHash { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L132) | Gets the deterministic source hash. | — | — | -| [`public Inno.Extensibility.Types.TypeCatalog Inno.Assets.Pipeline.AssetImportContext.types { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L143) | Gets the type catalog bound to the active import generation. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImportContext`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L16) | Collects source data and dependency declarations for one import operation. | — | — | - -### `Inno.Assets.Pipeline.AssetImporter` - + + + @@ -494,61 +406,17 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Assets.Pipeline.AssetImporter.AssetImporter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L38) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public virtual Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImporter.CreateImportSettings()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L71) | Creates a detached settings value for this importer in the current extension generation.
返回:A fresh serializable value with a registered stable type identity, or null when this importer has no settings. Defaults must be deterministic; implementations must not retain the returned instance. | — | — | -| [`public virtual Inno.Assets.Pipeline.AssetDeploymentScope Inno.Assets.Pipeline.AssetImporter.deploymentScope { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L52) | Gets whether imported assets are deployed or retained only for authoring workflows. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImporter.importerId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L45) | Gets the stable importer implementation identifier. | — | — | -| [`public abstract System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetImporter.supportedExtensions { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L62) | Gets the normalized source extensions accepted by this importer. | — | — | -| [`public abstract System.Type Inno.Assets.Pipeline.AssetImporter.targetAssetType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L57) | Gets the concrete asset type produced by this importer. | — | — | -| [`public abstract class Inno.Assets.Pipeline.AssetImporter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L38) | Defines metadata shared by automatically discovered asset importers. | — | — | - -### `Inno.Assets.Pipeline.AssetImporter`1` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Assets.Pipeline.AssetImporter.AssetImporter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L101) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected virtual System.Threading.Tasks.ValueTask?> Inno.Assets.Pipeline.AssetImporter.ExportAsync(Inno.Assets.Pipeline.AssetExportContext context, TAsset asset, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L144) | Exports an asset back into source bytes.
返回:The source bytes, or null when export is unsupported.
`context`:The generation-bound source export services.
`asset`:The asset to export.
`cancellationToken`:Cancellation for the export. | — | — | -| [`protected abstract System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetImporter.ImportAsync(Inno.Assets.Pipeline.AssetImportContext context, Inno.Assets.Pipeline.AssetImportWriter output, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L123) | Imports one source into a managed asset and named artifact outputs.
返回:An operation that completes when the candidate has been fully staged.
`context`:The import transaction context.
`output`:The candidate output writer.
`cancellationToken`:Cancellation for the import. | — | — | -| [`public override sealed System.Type Inno.Assets.Pipeline.AssetImporter.targetAssetType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L106) | Gets the runtime asset type accepted by this importer implementation. | — | — | -| [`public abstract class Inno.Assets.Pipeline.AssetImporter where TAsset : Inno.Assets.AssetObject : Inno.Assets.Pipeline.AssetImporter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L101) | Provides the strongly typed implementation base for an asset importer. | — | — | - -### `Inno.Assets.Pipeline.AssetImporterAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetImporterAttribute.AssetImporterAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L23) | Creates importer discovery metadata.
`id`:Globally stable importer protocol identifier. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImporterAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L32) | Gets the globally stable importer protocol identifier. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImporterAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L15) | Declares the immutable protocol identity of an automatically discovered asset importer. | — | — | - -### `Inno.Assets.Pipeline.AssetImportExtensionUnavailableException` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.AssetImportExtensionUnavailableException(string extensionKind, string extensionId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L22) | Describes a missing extension using generation-neutral identities.
`extensionKind`:The stable extension protocol identity.
`extensionId`:The required implementation's stable identity. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.extensionId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L42) | Gets the required implementation's stable identity. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.extensionKind { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L37) | Gets the stable extension protocol identity. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImportExtensionUnavailableException : System.InvalidOperationException`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L11) | Reports a required authoring extension absent from the current generation. | The host may defer this condition during discovery; it remains an import failure once discovery is complete. | — | - -### `Inno.Assets.Pipeline.AssetImportFailure` - + + @@ -560,73 +428,9 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetImportFailure.AssetImportFailure(string assetPath, string diagnostics)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Describes a writable-source import failure introduced after an earlier health snapshot.
`assetPath`:The isolated path of the source that failed to import.
`diagnostics`:The deterministic importer diagnostics associated with the failure. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportFailure.Deconstruct(out string assetPath, out string diagnostics)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Assets.Pipeline.AssetImportFailure.Equals(Inno.Assets.Pipeline.AssetImportFailure other)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Assets.Pipeline.AssetImportFailure.Equals(object obj)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Assets.Pipeline.AssetImportFailure.GetHashCode()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetImportFailure.operator ==(Inno.Assets.Pipeline.AssetImportFailure left, Inno.Assets.Pipeline.AssetImportFailure right)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetImportFailure.operator !=(Inno.Assets.Pipeline.AssetImportFailure left, Inno.Assets.Pipeline.AssetImportFailure right)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Assets.Pipeline.AssetImportFailure.ToString()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportFailure.assetPath { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L45) | The isolated path of the source that failed to import. | — | — | -| [`public string Inno.Assets.Pipeline.AssetImportFailure.diagnostics { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L46) | The deterministic importer diagnostics associated with the failure. | — | — | -| [`public readonly record struct Inno.Assets.Pipeline.AssetImportFailure : System.IEquatable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Describes a writable-source import failure introduced after an earlier health snapshot.
`assetPath`:The isolated path of the source that failed to import.
`diagnostics`:The deterministic importer diagnostics associated with the failure. | — | — | - -### `Inno.Assets.Pipeline.AssetImportHealthSnapshot` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Assets.Pipeline.AssetImportHealthSnapshot Inno.Assets.Pipeline.AssetImportHealthSnapshot.empty { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L32) | Gets the immutable snapshot used when no Asset Pipeline generation is active. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImportHealthSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L14) | Represents an immutable observation of writable-source import failures at one point in time. | The snapshot deliberately keeps importer fingerprints private to the Asset Pipeline. Callers can compare snapshots without depending on catalog records, importer implementation names, or storage details. | — | - -### `Inno.Assets.Pipeline.AssetImportSettingsSnapshot` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Assets.Pipeline.AssetImportSettingsSnapshot.fingerprint { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L30) | Gets the source metadata fingerprint required when saving this copy. | — | — | -| [`public Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImportSettingsSnapshot.value { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L25) | Gets the editable settings copy, or null for an importer without settings. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImportSettingsSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L12) | Provides a detached settings value and an optimistic concurrency token for its source sidecar. | The value belongs to the current importer generation. Editors must retain neutral serialized state, not this instance, across extension reloads. Reading this snapshot never edits source metadata. | — | - -### `Inno.Assets.Pipeline.AssetImportWriter`1` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnArtifact(System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L132) | Declares an asset artifact input that invalidates this import.
`persistentId`:The stable persistent identity used for lookup. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnAsset(Inno.Assets.AssetDependency dependency)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L116) | Declares a runtime dependency by persistent descriptor.
`dependency`:The dependency consumed by depends on asset; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnAsset(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L108) | Declares a runtime dependency by isolated source path.
`path`:The normalized file-system path used by this operation. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnCustomInput(string key, string fingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L143) | Declares a custom deterministic import input.
`key`:The input identifier.
`fingerprint`:The deterministic input fingerprint. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnSource(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L124) | Declares a source input that invalidates this import.
`path`:The normalized file-system path used by this operation. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.ReportDiagnostic(string message)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L154) | Adds a non-fatal import diagnostic.
`message`:The diagnostic message. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.SetAsset(TAsset asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L58) | Assigns the managed asset produced by the importer.
`asset`:The imported asset. | — | — | -| [`public void Inno.Assets.Pipeline.AssetImportWriter.SetDeploymentScope(Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L43) | Selects the deployment scope for this imported asset. This permits one importer to produce runtime assets and editor-only assets from different source documents without changing their asset type.
`deploymentScope`:The scope of the complete imported asset, including its generated state. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetImportWriter.WriteArtifactAsync(string outputName, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope = Inno.Assets.Pipeline.AssetDeploymentScope.Runtime)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L84) | Writes one immutable named artifact output.
返回:A completed operation after the output has been staged.
`outputName`:The stable output name.
`bytes`:The output content.
`cancellationToken`:Cancellation for the write operation.
`deploymentScope`:Whether this output is retained in runtime packages. Runtime payload and asset state cannot be authoring-only. | — | — | -| [`public TAsset? Inno.Assets.Pipeline.AssetImportWriter.asset { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L33) | Gets the candidate asset assigned by the importer. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetImportWriter where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L16) | Collects the complete candidate output of one source import. | — | — | - -### `Inno.Assets.Pipeline.AssetLoader` - @@ -670,488 +474,474 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Action? Inno.Assets.Pipeline.AssetLoader.AssetReloaded`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L308) | Occurs after a loaded canonical asset is updated in place. | — | — | -| [`public Inno.Assets.Pipeline.AssetLoader.AssetLoader(Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IReadOnlyList mounts, string libraryRoot, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool runtimeArtifactsOnly = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L197) | Creates an asset loader over one project source and zero or more isolated sources.
`types`:The immutable type-generation owner used to discover importers and resolve asset types.
`serialization`:The serialization registry used for catalog, metadata, and asset state persistence.
`identities`:The identity allocator that owns every canonical asset loaded by this instance.
`logs`:The explicitly owned router receiving importer and catalog diagnostics.
`diagnostics`:The diagnostic hub that owns import, build, catalog, and reference reports.
`mounts`:Complete isolated source mount snapshot.
`libraryRoot`:Absolute rebuildable Library root.
`sourcePolicy`:Source filtering policy, or null for defaults.
`runtimeArtifactsOnly`:Whether to trust a deployed read-only catalog and skip every source reconciliation operation. | — | — | -| [`public Inno.Assets.Pipeline.AssetLoader.AssetLoader(Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, string assetRoot, string libraryRoot, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L145) | Creates an asset loader for one source and Library root pair.
`types`:The immutable type-generation owner used to discover importers and resolve asset types.
`serialization`:The serialization registry used for catalog, metadata, and asset state persistence.
`identities`:The identity allocator that owns every canonical asset loaded by this instance.
`logs`:The explicitly owned router receiving importer and catalog diagnostics.
`diagnostics`:The diagnostic hub that owns import, build, catalog, and reference reports.
`assetRoot`:The absolute source root.
`libraryRoot`:The absolute rebuildable Library root.
`sourcePolicy`:The source filtering policy, or null for defaults. | — | — | -| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetLoader.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L109) | Acquires an immutable output while excluding concurrent artifact collection.
返回:A lease retaining this exact artifact key, even after reimport or source deletion.
`persistentId`:The source asset's persistent identity.
`outputName`:The required output protocol. | — | `System.InvalidOperationException`:No current output can be resolved. | -| [`public void Inno.Assets.Pipeline.AssetLoader.ApplySourceChanges(System.Collections.Generic.IReadOnlyList changes)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceChanges.cs#L60) | Applies normalized source file changes to the persistent catalog.
`changes`:The normalized source changes. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.BuildAsync(Inno.Assets.AssetObject definition, System.Collections.Generic.IReadOnlyList inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Importing.cs#L55) | Builds and validates the requested artifact asynchronously before publishing it.
返回:The content-addressed output bundle key.
`definition`:The build definition asset.
`inputs`:The immutable input catalog snapshots.
`cancellationToken`:Cancellation for the candidate build. | — | — | -| [`public Inno.Assets.Pipeline.AssetImportHealthSnapshot Inno.Assets.Pipeline.AssetLoader.CaptureWritableImportHealth()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Diagnostics.cs#L35) | Captures the current failure identity of every writable source without exposing catalog internals.
返回:An immutable health snapshot suitable for validating a later candidate generation. | — | — | -| [`public int Inno.Assets.Pipeline.AssetLoader.CollectArtifacts(System.TimeSpan gracePeriod, long maximumSizeBytes)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L41) | Collects unreachable content-addressed artifacts.
返回:The number of removed artifact bundles.
`gracePeriod`:The minimum age of an unreachable bundle.
`maximumSizeBytes`:The cache size limit, or zero for no limit. | — | — | -| [`public void Inno.Assets.Pipeline.AssetLoader.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Retirement.cs#L38) | Stops new loads and releases canonical assets before their registries and diagnostics. | — | `Inno.Core.Execution.RetirementPendingException`:A load, serialized operation or unload hook is still active. Retain the loader and retry after quiescence.
`System.AggregateException`:Completed releases failed; all quiescent resources have still been attempted. | -| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetLoader.ExportRuntimeArtifacts(string destinationLibraryRoot, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.RuntimeExport.cs#L66) | Exports the validated runtime artifact closure while observing cooperative cancellation between files.
返回:Counts and source identities represented by the exported snapshot.
`destinationLibraryRoot`:The empty directory that receives the runtime-only asset database.
`cancellationToken`:The token checked before every artifact file is copied. | — | `System.InvalidOperationException`:Thrown when artifacts are incomplete or transitive source inputs no longer certify the imported snapshot.
`System.OperationCanceledException`:Thrown when cancellation is requested before the snapshot finishes. | -| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetLoader.ExportRuntimeArtifacts(string destinationLibraryRoot)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.RuntimeExport.cs#L45) | Writes a source-free runtime catalog and its exact immutable artifact closure.
返回:Counts and source identities for the exported runtime snapshot.
`destinationLibraryRoot`:Empty destination that becomes the deployed content root. | — | `System.InvalidOperationException`:Thrown when runtime artifacts are incomplete, a deployed reference is authoring-only, or a transitive authoring input has failed or become stale.
`System.IO.IOException`:Thrown when the destination is not empty or content cannot be copied. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.FindIntroducedImportFailures(Inno.Assets.Pipeline.AssetImportHealthSnapshot baseline)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Diagnostics.cs#L59) | Finds writable-source failures that are new or changed relative to an earlier health snapshot.
返回:A deterministic path-ordered collection containing only introduced or changed failures.
`baseline`:The health snapshot captured before the candidate generation was activated. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L41) | Gets direct or transitive runtime dependencies of an asset.
返回:The persistent dependency descriptors.
`asset`:The asset to query.
`recursive`:Whether transitive dependencies should be included. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetImportDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L61) | Gets source import dependencies that invalidate an asset artifact.
返回:Canonical isolated source paths in stable order.
`asset`:The asset to query.
`recursive`:Whether transitive source dependencies should be included. | — | — | -| [`public Inno.Assets.Pipeline.AssetImportSettingsSnapshot Inno.Assets.Pipeline.AssetLoader.GetImportSettings(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L31) | Reads a detached importer settings value without changing source metadata.
返回:The current settings and the metadata fingerprint required for a subsequent save.
`path`:The isolated source path, not its sidecar path. | — | `System.NotSupportedException`:The source has no registered importer.
`System.IO.InvalidDataException`:The persisted settings cannot be restored by the current importer. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetLoadedPaths()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L209) | Gets isolated source paths of all canonical loaded assets.
返回:A stable isolated path snapshot. | — | — | -| [`public Inno.Assets.AssetReferenceInfo Inno.Assets.Pipeline.AssetLoader.GetReferenceInfo(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L86) | Gets an engine-known reference diagnostic snapshot.
返回:The reference diagnostic snapshot.
`asset`:The asset to inspect. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.Import(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Importing.cs#L38) | Imports one isolated source file into metadata and a runtime artifact.
返回:true when an importer handled the source.
`path`:The isolated source path. | — | — | -| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(Inno.Assets.AssetPath path, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L41) | Loads a canonical asset by isolated source path.
返回:The canonical asset, or null when unavailable or incompatible.
`path`:The isolated source path.
`requestedAssetType`:The required assignable asset type. | — | — | -| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(System.Guid persistentId, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L85) | Loads a canonical asset by persistent identity.
返回:The canonical asset, or null when unavailable or incompatible.
`persistentId`:The persistent asset identity.
`requestedAssetType`:The required assignable asset type. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(Inno.Assets.AssetPath path, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L132) | Asynchronously loads a canonical asset by isolated source path.
返回:The canonical asset, or null when unavailable or incompatible.
`path`:The isolated source path.
`requestedAssetType`:The required assignable asset type.
`cancellationToken`:Cancellation for the current caller's wait. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(System.Guid persistentId, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L172) | Asynchronously loads a canonical asset by persistent identity.
返回:The canonical asset, or null when unavailable or incompatible.
`persistentId`:The persistent asset identity.
`requestedAssetType`:The required assignable asset type.
`cancellationToken`:Cancellation for the current caller's wait. | — | — | -| [`public Inno.Assets.Pipeline.AssetCatalogCandidate Inno.Assets.Pipeline.AssetLoader.PrepareCatalogCandidate(System.Collections.Generic.IReadOnlyList mounts, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L47) | Creates an isolated source-mount and catalog candidate without changing the active catalog.
返回:A candidate that owns its catalog staging storage and exposes the isolated loader to its owner.
`mounts`:The complete source-mount snapshot to validate.
`sourcePolicy`:The source policy for the candidate, or null to reuse this loader's policy. | — | `System.ArgumentException`:Thrown when the mount snapshot is empty, duplicated, or has no writable project source.
`System.IO.IOException`:Thrown when the active catalog cannot be copied into candidate storage. | -| [`public bool Inno.Assets.Pipeline.AssetLoader.RefreshRegistries()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L344) | Refreshes extension registries and reimports affected sources when their snapshot changed.
返回:true when the importer registry changed. | — | — | -| [`public void Inno.Assets.Pipeline.AssetLoader.Rescan(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceChanges.cs#L38) | Reconciles source files, metadata, artifacts and the in-memory catalog.
`cancellationToken`:Cancellation for reconciliation and importer work; unpublished candidates can be discarded after it drains. | — | `System.OperationCanceledException`:Reconciliation or an importer observes cancellation. | -| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetLoader.ResolveReference(System.Guid persistentId, System.Guid stableTypeId, string lastKnownPath, System.Type expectedType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L214) | Resolves a serialized reference or creates a persistent missing placeholder.
返回:A compatible canonical asset or missing placeholder.
`persistentId`:The referenced persistent identity.
`stableTypeId`:The referenced stable asset type identity.
`lastKnownPath`:The last known source-relative path.
`expectedType`:The declared destination type. | — | — | -| [`public void Inno.Assets.Pipeline.AssetLoader.RestoreProperties(System.Guid stableTypeId, byte[] propertyData, TValue target) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L101) | Restores serialized properties to the existing asset object.
`stableTypeId`:The stable type id consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`propertyData`:The property data consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`target`:The existing target that receives the validated result. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.Save(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Saving.cs#L38) | Saves an asset back to its current source path.
返回:true when an importer exported the asset.
`asset`:The asset to export. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Saving.cs#L58) | Saves an asset to its initial or existing isolated source path while preserving an existing destination identity.
返回:true when an importer exported the asset.
`path`:The isolated source path.
`asset`:The asset to export. Detached replacement values do not replace an existing source's persistent identity. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.SaveImportSettings(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable? settings, string expectedFingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L68) | Atomically saves importer settings and reimports the source. Failed imports retain both the saved settings and the previous successful artifact; callers must inspect the returned import status separately from saving.
返回:True when reimport succeeds; false when settings were saved but reimport failed.
`path`:The writable isolated source path.
`settings`:A value of the current importer's settings type, or null to reset to defaults.
`expectedFingerprint`:The fingerprint returned when the settings were read. | — | `System.IO.IOException`:The sidecar changed externally or could not be written.
`System.InvalidOperationException`:The source is read-only or belongs to an isolated candidate.
`System.ArgumentException`:The settings have a different type from the current importer. | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L74) | Tries to resolve a named output from the current artifact bundle.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`persistentId`:The stable persistent identity used for lookup.
`outputName`:The stable artifact output name used for lookup.
`artifact`:The resolved immutable artifact payload returned to the caller. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetAssetType(Inno.Assets.AssetPath path, out System.Type? assetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L189) | Tries to resolve the concrete asset type without loading it.
返回:true when the type can be resolved.
`path`:The isolated source path.
`assetType`:The resolved type. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L114) | Tries to get a catalog snapshot by isolated source path.
返回:true when the source is cataloged.
`path`:The isolated source path.
`info`:The immutable catalog snapshot when found. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetInfo(System.Guid persistentId, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L140) | Tries to get a catalog snapshot by persistent identity.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`persistentId`:The stable persistent identity used for lookup.
`info`:The resolved immutable metadata returned to the caller. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetPersistentId(Inno.Assets.AssetPath path, out System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L163) | Tries to resolve a persistent identity without loading the asset.
返回:true when catalog metadata exists.
`path`:The isolated source path.
`persistentId`:The resolved identity. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(Inno.Assets.AssetPath path, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L64) | Tries to load a canonical asset by isolated source path.
返回:true when a compatible asset was loaded.
`path`:The isolated source path.
`requestedAssetType`:The required assignable asset type.
`asset`:The canonical asset when successful. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(System.Guid persistentId, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L108) | Tries to load a canonical asset by persistent identity.
返回:true when a compatible asset was loaded.
`persistentId`:The persistent asset identity.
`requestedAssetType`:The required assignable asset type.
`asset`:The canonical asset when successful. | — | — | -| [`public int Inno.Assets.Pipeline.AssetLoader.UnloadUnusedAssets()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L126) | Collects canonical assets that have no external managed references.
返回:The number of released canonical assets. | — | — | -| [`public void Inno.Assets.Pipeline.AssetLoader.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L336) | Waits for pending import and build work. | — | — | -| [`public string Inno.Assets.Pipeline.AssetLoader.artifactRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L303) | Gets the derived content-addressed artifact root. | — | — | -| [`public string Inno.Assets.Pipeline.AssetLoader.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L293) | Gets the absolute source root. | — | — | -| [`public string Inno.Assets.Pipeline.AssetLoader.libraryRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L298) | Gets the absolute rebuildable Library root. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetLoader : System.IDisposable, Inno.Assets.IAssetReferenceResolver, Inno.Assets.IAssetArtifactLookup, Inno.Assets.IAssetPropertyStateResolver`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L27) | Coordinates importing, persistent cataloging, canonical loading, reloading and collection for one source and artifact root pair. | — | — | - -### `Inno.Assets.Pipeline.AssetPipeline` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.AssetReloaded`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L163) | Occurs after a canonical loaded asset has been updated in place. | — | — | -| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.Changed`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L158) | Occurs after an asset database transaction has committed. | — | — | -| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.SourceMountsChanged`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L169) | Occurs after a complete isolated source-mount generation is atomically replaced. | — | — | -| [`public Inno.Assets.Pipeline.AssetPipeline.AssetPipeline(Inno.Extensibility.Modules.ModuleHost modules, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, Inno.Assets.Pipeline.AssetPipelineOptions options)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L199) | Creates one isolated authoring or deployed-runtime asset pipeline.
`modules`:The module host whose candidate generations coordinate importer refreshes.
`types`:The type catalog used to discover and resolve asset extensions.
`serialization`:The serialization registry used for metadata and canonical asset state.
`identities`:The identity allocator that owns every canonical asset in this pipeline.
`logs`:The explicitly owned router receiving asset pipeline diagnostics.
`diagnostics`:The diagnostic hub that owns source database and asset processing reports.
`options`:The asset source, artifact and watcher configuration. | — | `System.ArgumentNullException`:Thrown when a required service is null. | -| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetPipeline.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L61) | Acquires one verified authoring artifact generation for an explicit lifetime.
返回:A lease over the currently committed immutable artifact generation.
`persistentId`:Persistent identity of the artifact owner.
`outputName`:Stable artifact output name. | — | `System.InvalidOperationException`:Thrown when the requested artifact is unavailable. | -| [`public System.Threading.Tasks.ValueTask> Inno.Assets.Pipeline.AssetPipeline.AcquireAsync(Inno.Assets.AssetPath path, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L225) | Asynchronously acquires a canonical authoring asset by path for an explicit managed lifetime.
返回:A lease whose strong reference participates in the existing reachability sweep.
`path`:Mount-qualified logical asset path.
`cancellationToken`:Cancellation for this caller's asynchronous wait. | — | — | -| [`public System.Threading.Tasks.ValueTask> Inno.Assets.Pipeline.AssetPipeline.AcquireAsync(System.Guid persistentId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L250) | Asynchronously acquires a canonical authoring asset by persistent identity.
返回:A lease whose strong reference participates in the existing reachability sweep.
`persistentId`:Persistent asset identity.
`cancellationToken`:Cancellation for this caller's asynchronous wait. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.BuildAsync(Inno.Assets.AssetObject definition, System.Collections.Generic.IReadOnlyList inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L81) | Runs an aggregate asset build using the processor registered for a definition.
返回:The content-addressed output bundle key.
`definition`:The build definition asset.
`inputs`:The immutable input catalog snapshots.
`cancellationToken`:Cancellation for the candidate build. | — | — | -| [`public Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.AssetPipeline.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L116) | Captures native settings and nested asset dependencies through this explicit authoring owner.
返回:Complete stable properties and dependencies without writing or importing an asset.
`value`:Typed settings, never retained by the returned snapshot. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.CompleteExtensionDiscovery()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L31) | Ends initial authoring extension discovery and strictly retries dependent imports. | Call on the initialization thread after successful extension activation. Failed compilation must not call this method. | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.CreateDirectory(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L298) | Creates a tracked source directory and its persistent metadata.
`path`:New isolated directory path. | — | `System.IO.DirectoryNotFoundException`:Thrown when the parent directory does not exist.
`System.IO.IOException`:Thrown when the target already exists. | -| [`public Inno.Assets.Pipeline.AssetSourceStore Inno.Assets.Pipeline.AssetPipeline.CreateSourceStore()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L102) | Creates a detached source editor sharing this owner's mounts, native converters and references.
返回:A source store that must not outlive this asset pipeline. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.Delete(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L230) | Deletes a source asset and its metadata while retaining a Library tombstone for existing references.
`path`:Existing isolated file or directory path. | — | `System.IO.FileNotFoundException`:Thrown when the source does not exist. | -| [`public void Inno.Assets.Pipeline.AssetPipeline.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Retirement.cs#L27) | Releases watchers, catalog participants, canonical objects, and rebuildable staging state. | — | — | -| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetPipeline.ExportRuntimeArtifacts(string destinationContentRoot)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L108) | Exports the current source-free runtime catalog and its exact artifact closure.
返回:Counts and source identities for the deployed runtime snapshot.
`destinationContentRoot`:Empty destination for the deployed asset database. | — | `System.InvalidOperationException`:Thrown when runtime artifacts are incomplete, a deployed reference is authoring-only, or a transitive authoring input has failed or changed since import. Last-good inputs do not certify an export.
`System.IO.IOException`:Thrown when the destination is not empty or cannot be written. | -| [`public System.Threading.Tasks.Task Inno.Assets.Pipeline.AssetPipeline.ExportRuntimeArtifactsAsync(string destinationContentRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L144) | Exports a runtime-only artifact snapshot on a worker without loading artifact files into memory.
返回:A task that completes with counts and source identities for the captured runtime snapshot.
`destinationContentRoot`:The empty destination that receives the runtime asset database.
`cancellationToken`:The token checked while individual artifact files are copied. | Keeps generation admission and serialization pinned until the worker completes, fails or cancels. A pending or faulted generation cannot start a new export. | `System.InvalidOperationException`:Thrown when runtime artifacts are incomplete, transitive authoring inputs have failed or become stale, or the asset service is not available.
`System.OperationCanceledException`:Thrown when cancellation is requested before export completes. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L304) | Gets direct or transitive runtime dependencies of an asset.
返回:The dependency descriptors.
`asset`:The asset to query.
`recursive`:Whether transitive dependencies should be included. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetFileSystemChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L84) | Gets immediate indexed children of a source directory.
返回:The immediate child entry snapshot.
`parent`:The isolated parent path. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L72) | Gets indexed source entries.
返回:The stable source entry snapshot.
`includeDirectories`:Whether directories should be included. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetImportDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L322) | Gets source import dependencies that invalidate an asset artifact.
返回:Canonical isolated source paths in stable order.
`asset`:The asset to query.
`recursive`:Whether transitive source dependencies should be included. | — | — | -| [`public Inno.Assets.Pipeline.AssetImportSettingsSnapshot Inno.Assets.Pipeline.AssetPipeline.GetImportSettings(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L57) | Reads a detached settings copy for the currently registered importer.
返回:The settings copy and the fingerprint required for saving it.
`path`:The isolated source path. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetLoadedPaths()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L290) | Gets isolated source paths for all canonical loaded assets.
返回:A stable isolated path snapshot. | — | — | -| [`public Inno.Assets.AssetReferenceInfo Inno.Assets.Pipeline.AssetPipeline.GetReferenceInfo(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L337) | Gets an engine-known reference diagnostic snapshot.
返回:The reference diagnostic snapshot.
`asset`:The asset to inspect. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.Import(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L33) | Imports one source asset from an isolated source mount.
返回:true when an importer handled the source.
`path`:The isolated source path. | — | — | -| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetPipeline.Load(Inno.Assets.AssetPath path, System.Type assetType)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L65) | Loads a canonical asset using a runtime asset type selected by an authoring workflow.
返回:The canonical asset instance assignable to assetType.
`path`:The isolated source path.
`assetType`:The required concrete or base asset type. | — | `System.ArgumentNullException`:Thrown when assetType is null.
`System.ArgumentException`:Thrown when assetType does not derive from Inno.Assets.AssetObject.
`System.InvalidOperationException`:Thrown when no compatible canonical asset can be loaded. | -| [`public TAsset Inno.Assets.Pipeline.AssetPipeline.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L36) | Loads a canonical asset by isolated source path.
返回:The canonical asset instance.
`path`:The isolated source path. | — | — | -| [`public TAsset Inno.Assets.Pipeline.AssetPipeline.Load(System.Guid persistentId) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L95) | Loads a canonical asset by persistent identity.
返回:The canonical asset instance.
`persistentId`:The persistent asset identity. | — | `System.InvalidOperationException`:Thrown when no compatible asset can be loaded. | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.LoadAsync(Inno.Assets.AssetPath path, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L167) | Asynchronously loads a canonical asset by isolated source path.
返回:The canonical asset instance.
`path`:The isolated source path.
`cancellationToken`:Cancellation for the current caller's wait. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.LoadAsync(System.Guid persistentId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L196) | Asynchronously loads a canonical asset by persistent identity.
返回:The canonical asset instance.
`persistentId`:The persistent asset identity.
`cancellationToken`:Cancellation for the current caller's wait. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.Move(Inno.Assets.AssetPath source, Inno.Assets.AssetPath target)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L163) | Moves a source asset while preserving its persistent identity and generated metadata.
`source`:Existing isolated source path.
`target`:New isolated source path. | — | `System.IO.FileNotFoundException`:Thrown when the source does not exist.
`System.IO.IOException`:Thrown when the target source or metadata already exists. | -| [`public Inno.Assets.Pipeline.AssetSampleImportTransaction Inno.Assets.Pipeline.AssetPipeline.PrepareSampleImport(Inno.Assets.AssetPath source)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.Samples.cs#L48) | Starts a private background sample clone without waiting for file copying or transformation.
返回:The generation-pinned transaction to advance, validate and commit on this pipeline's owner thread.
`source`:An indexed installed Plugin sample directory whose final segment starts with ~. | — | `System.ArgumentException`:The source is not an installed sample directory.
`System.IO.IOException`:The destination name already exists.
`System.InvalidOperationException`:Another mutation or generation transaction is active, or this is not the initialization thread. | -| [`public Inno.Assets.Pipeline.AssetSourceMountTransaction Inno.Assets.Pipeline.AssetPipeline.PrepareSourceMounts(System.Collections.Generic.IReadOnlyList mounts)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.SourceMounts.cs#L172) | Builds and validates an isolated source-mount candidate without changing active AssetPipeline state.
返回:A transaction that can be inspected, activated, completed, or rolled back.
`mounts`:A writable project source followed by zero or more read-only sources. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.ReplaceSourceMounts(System.Collections.Generic.IReadOnlyList mounts)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.SourceMounts.cs#L155) | Validates and atomically replaces the complete source-mount generation while preserving the active generation after any candidate failure.
`mounts`:A writable project source followed by zero or more read-only sources. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.Rescan()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L27) | Reconciles source files, generated files and the persistent catalog. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.RestoreProperties(System.Guid stableTypeId, byte[] propertyData, TValue target) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L138) | Restores serialized properties to the existing asset object.
`stableTypeId`:The stable type id consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`propertyData`:The property data consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`target`:The existing target that receives the validated result. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.Save(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L107) | Saves an asset to its current source path.
返回:true when an importer exported the asset.
`asset`:The asset to save. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L125) | Saves an asset to a writable isolated source path, preserving the destination source identity when it exists.
返回:true when an importer exported the asset.
`path`:Writable isolated source path.
`asset`:Asset to save. A detached value replaces the destination content without replacing its persistent identity. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.SaveImportSettings(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable? settings, string expectedFingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L82) | Saves import settings with conflict detection and immediately attempts to reimport the source.
返回:True when settings were saved and reimport succeeded; false when the saved settings failed import.
`path`:The writable isolated source path.
`settings`:The edited settings, or null to reset to importer defaults.
`expectedFingerprint`:The fingerprint obtained when reading the settings. | — | `System.IO.IOException`:The settings changed externally or could not be written. | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L39) | Tries to resolve a named artifact output.
返回:true when the output exists.
`persistentId`:The artifact owner identity.
`outputName`:The named output.
`artifact`:The output information when available. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetAssetType(Inno.Assets.AssetPath path, out System.Type? assetType)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L51) | Tries to resolve an asset type without loading the asset.
返回:true when the type can be resolved.
`path`:The isolated source path.
`assetType`:The resolved concrete asset type. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetFileSystemEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L98) | Tries to resolve an indexed source entry.
返回:true when the entry exists and is not generated metadata.
`path`:The isolated source path.
`entry`:The resolved source entry. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L85) | Tries to get a catalog snapshot by source-relative path.
返回:true when the path is cataloged.
`path`:The isolated source path.
`info`:The catalog snapshot when available. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetInfo(System.Guid persistentId, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L102) | Tries to get a catalog snapshot by persistent identity.
返回:true when the identity is cataloged.
`persistentId`:The persistent asset identity.
`info`:The catalog snapshot when available. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetPersistentId(Inno.Assets.AssetPath path, out System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L68) | Tries to resolve a persistent identity without loading the asset.
返回:true when catalog metadata exists.
`path`:The isolated source path.
`persistentId`:The resolved persistent identity. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryLoad(Inno.Assets.AssetPath path, out TAsset? asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L117) | Tries to load a canonical asset by isolated source path.
返回:true when a compatible asset was loaded.
`path`:The isolated source path.
`asset`:The canonical asset when successful. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryLoad(System.Guid persistentId, out TAsset? asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L142) | Tries to load a canonical asset by persistent identity.
返回:true when a compatible asset was loaded.
`persistentId`:The persistent asset identity.
`asset`:The canonical asset when successful. | — | — | -| [`public int Inno.Assets.Pipeline.AssetPipeline.UnloadUnusedAssets()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Retirement.cs#L40) | Collects assets that have no external managed references.
返回:The number of released canonical assets. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.Update()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L39) | Applies queued source and build changes on the initialization thread. | — | — | -| [`public void Inno.Assets.Pipeline.AssetPipeline.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L294) | Waits until queued source watcher changes have been processed. | — | — | -| [`public string Inno.Assets.Pipeline.AssetPipeline.artifactRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L75) | Gets the absolute generated artifact root. | — | — | -| [`public string Inno.Assets.Pipeline.AssetPipeline.assetRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L65) | Gets the absolute source asset root. | — | — | -| [`public Inno.Core.Identity.IdentityAllocator Inno.Assets.Pipeline.AssetPipeline.identities { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L93) | Gets the authoring identity domain shared by canonical assets and addressable source entries. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipeline.isInitialized { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L60) | Gets whether asset services are initialized. | — | — | -| [`public string Inno.Assets.Pipeline.AssetPipeline.libraryRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L70) | Gets the absolute root containing rebuildable asset database data. | — | — | -| [`public long Inno.Assets.Pipeline.AssetPipeline.revision { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L81) | Gets the monotonic identity of the current committed asset and source-mount state. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.sourceMounts { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L87) | Gets the active isolated source mount snapshot. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetPipeline : Inno.Assets.AssetResidencyProvider, System.IDisposable, Inno.Assets.IAssetLookup, Inno.Assets.IAssetReferenceResolver, Inno.Assets.IAssetPropertyStateResolver, Inno.Assets.IAssetArtifactLookup, Inno.Assets.IAssetResidency, Inno.Assets.Pipeline.IAssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L22) | Provides the single application-level entry point for importing, loading, saving and collecting assets. | — | — | - -### `Inno.Assets.Pipeline.AssetPipelineMode` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Assets.Pipeline.AssetPipelineMode.Authoring = 0`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L11) | Reconciles writable source files and produces immutable artifacts. | — | — | -| [`Inno.Assets.Pipeline.AssetPipelineMode.RuntimeArtifacts = 1`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L16) | Loads a read-only deployed catalog and its content-addressed artifacts without source files. | — | — | -| [`public enum Inno.Assets.Pipeline.AssetPipelineMode : int`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L6) | Defines which side of the asset pipeline an Inno.Assets.Pipeline.AssetPipeline instance serves. | — | — | - -### `Inno.Assets.Pipeline.AssetPipelineOptions` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Assets.Pipeline.AssetPipelineOptions Inno.Assets.Pipeline.AssetPipelineOptions.Create(string assetRoot, string libraryRoot)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L73) | Creates options with sensible defaults for most projects.
返回:Initialized options value.
`assetRoot`:Source assets root folder.
`libraryRoot`:Rebuildable Library folder. | — | — | -| [`public string Inno.Assets.Pipeline.AssetPipelineOptions.assetRoot { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L29) | Gets the root folder containing source assets. | — | — | -| [`public Inno.Assets.Pipeline.AssetCacheOptions Inno.Assets.Pipeline.AssetPipelineOptions.cacheOptions { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L59) | Gets the rebuildable cache policy. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetPipelineOptions.deferUnavailableExtensions { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L19) | Gets whether the composition host must still publish its initial authoring extensions. | Only missing extensions are deferred. The host must call AssetPipeline.CompleteExtensionDiscovery after activation. | — | -| [`public bool Inno.Assets.Pipeline.AssetPipelineOptions.enableFileSystemWatcher { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L39) | Gets whether file-system watching is enabled. | — | — | -| [`public int Inno.Assets.Pipeline.AssetPipelineOptions.fileWatcherFlushDelayMs { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L44) | Gets the watcher change coalescing delay in milliseconds. | — | — | -| [`public string Inno.Assets.Pipeline.AssetPipelineOptions.libraryRoot { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L34) | Gets the root folder containing rebuildable project data. | — | — | -| [`public Inno.Assets.Pipeline.AssetPipelineMode Inno.Assets.Pipeline.AssetPipelineOptions.mode { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L24) | Gets whether the pipeline reconciles authoring sources or consumes a deployed artifact catalog. | — | — | -| [`public System.Collections.Generic.IReadOnlyList? Inno.Assets.Pipeline.AssetPipelineOptions.sourceMounts { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L54) | Gets the complete source mount snapshot, or null to mount only Inno.Assets.Pipeline.AssetPipelineOptions.assetRoot. | — | — | -| [`public Inno.Assets.Pipeline.AssetSourcePolicy? Inno.Assets.Pipeline.AssetPipelineOptions.sourcePolicy { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L49) | Gets the source filtering policy. | — | — | -| [`public readonly struct Inno.Assets.Pipeline.AssetPipelineOptions`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L11) | Initialization options for Inno.Assets.Pipeline.AssetPipeline. | — | — | - -### `Inno.Assets.Pipeline.AssetSample` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const string Inno.Assets.Pipeline.AssetSample.fileType = ".isample"`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L16) | Gets the logical File Browser type used for a sample directory. | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetSample.Contains(Inno.Assets.AssetPath path, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L64) | Determines whether a source path is a sample directory or is contained by one.
返回:true when the path belongs to an installed Plugin source and any directory segment starts with ~. Project content is never hidden from authoring import by this method.
`path`:The isolated source path to inspect.
`isDirectory`:Whether the path itself represents a directory. | — | — | -| [`public static string Inno.Assets.Pipeline.AssetSample.GetImportName(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L84) | Gets the original sample directory name for a writable Project copy.
返回:The unchanged final directory segment, including every leading ~.
`path`:A sample directory path whose final segment starts with ~. | — | `System.ArgumentException`:Thrown when path does not identify a sample directory or has no usable name after its prefix. | -| [`public static bool Inno.Assets.Pipeline.AssetSample.HasSampleDirectoryName(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L28) | Determines whether the final path segment uses the sample-directory naming convention.
返回:true when the final segment starts with ~, regardless of whether the path belongs to the writable Project source or an installed Plugin source.
`path`:The isolated source path to inspect. | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetSample.IsRoot(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L46) | Determines whether the final segment identifies a sample directory.
返回:true when the path belongs to an installed Plugin source and its final segment starts with ~. A Project directory with the same name remains normal writable authoring content.
`path`:The isolated source path to inspect. | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetSample.IsRuntimeExcluded(Inno.Assets.AssetPath path, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L108) | Determines whether a path belongs to an authoring-only sample subtree that must not enter a Player runtime closure.
返回:true when any directory segment starts with ~, including in the writable Project source.
`path`:The isolated source path to inspect.
`isDirectory`:Whether the path itself represents a directory. | — | — | -| [`public static class Inno.Assets.Pipeline.AssetSample`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L11) | Defines authoring sample directories and their installed Plugin behavior. | — | — | - -### `Inno.Assets.Pipeline.AssetSampleImportTransaction` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.Advance()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L113) | Starts background indexing after cloning, then adopts its completed catalog without waiting.
返回:True when the candidate is indexed and ready for BeginValidation; false while preparation is pending. | — | `System.InvalidOperationException`:The transaction is finished or the caller is not its initialization thread.
`System.OperationCanceledException`:Preparation or indexing observed cancellation before a candidate could be adopted. | -| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.BeginValidation(System.Func validate)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L167) | Starts one authoring preflight after indexing, retaining its dependencies until it drains.
`validate`:Owner-thread capture followed by asynchronous validation. Continuations must not mutate live Assets. | — | `System.ArgumentNullException`:The validation callback is null.
`System.InvalidOperationException`:The candidate is not indexed, validation already started, or the transaction is finished. | -| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Cancel()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L222) | Requests cancellation of copying and validation while retaining all dependencies. | Cancellation does not wait. Continue owner-thread retirement until Rollback or Dispose completes. | — | -| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Commit(System.Action? beforePublish = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L203) | Publishes a successfully validated import exactly once, without waiting for unfinished work.
`beforePublish`:Optional owner-thread finalization, such as recording neutral History data. Failure leaves the transaction open for Rollback and no Changed notification is published. | — | `System.InvalidOperationException`:Validation is unfinished, the transaction is finished, or the caller is not its owner thread.
`System.OperationCanceledException`:The import or its validation was canceled before publication. | -| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L271) | Retires an uncommitted import on its owner thread after cancellation has drained. | — | `Inno.Core.Execution.RetirementPendingException`:Canceled work has not drained; disposal must be retried without releasing its owner. | -| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Rollback()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L230) | Cancels work and removes an uncommitted copy after all background consumers have drained. | — | `Inno.Core.Execution.RetirementPendingException`:Background work is still draining; retain the transaction and retry on its owner thread. | -| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.isFaulted { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L94) | Gets whether failed publication or retirement has faulted the host and requires a restart. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.isValidationComplete { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L99) | Gets whether background validation has drained and Commit can inspect its outcome. | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleImportTransaction.target { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L89) | Gets the destination path with the original sample directory name preserved. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSampleImportTransaction : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L22) | Owns a cancellable sample clone, its pinned generation and its owner-thread publication. | Advance and Commit run on the AssetPipeline initialization thread. A prepared copy is indexed in an isolated catalog for validation without publishing Changed; ordinary mutations and generation changes are deferred. Rollback must finish before the pipeline or any validation dependency can be released. | — | - -### `Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute.AssetSampleSourceRewriterAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L144) | Creates one ordered source-language transformer declaration.
`id`:Stable extension identity used for diagnostics and ordering. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L153) | Gets the stable extension identity. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L136) | Registers a source-language sample transformer through the reloadable type catalog. | — | — | - -### `Inno.Assets.Pipeline.AssetSampleTransformContext` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Assets.Pipeline.AssetSampleTransformContext.MapType(System.Guid oldId, System.Guid newId)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L102) | Registers one source-language type replacement for subsequent structured asset rewriting.
`oldId`:The type identity stored in installed sample assets.
`newId`:The type identity declared by the cloned source. | — | `System.IO.InvalidDataException`:A source identity is reused inconsistently. | -| [`public bool Inno.Assets.Pipeline.AssetSampleTransformContext.TryGetSourceIdentity(string relativePath, out System.Guid oldId, out System.Guid newId)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L73) | Resolves the source and clone identity of one file relative to the sample directory.
返回:Whether both identities were recorded from sample metadata.
`relativePath`:Slash-separated source-local file path below the sample directory.
`oldId`:Receives the installed source identity.
`newId`:Receives the writable clone identity. | — | — | -| [`public System.Threading.CancellationToken Inno.Assets.Pipeline.AssetSampleTransformContext.cancellationToken { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L51) | Gets cancellation for this private, generation-pinned transformation. | — | — | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Assets.Pipeline.AssetSampleTransformContext.identityMap { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L56) | Gets cloned source identities, including directory and file metadata. | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleTransformContext.source { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L41) | Gets the installed sample source path. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSampleTransformContext.stagedRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L36) | Gets the absolute private staging directory; files here are committed only after all transforms succeed. | — | — | -| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleTransformContext.target { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L46) | Gets the writable project destination path. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSampleTransformContext`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L12) | Gives a source-language extension access to a staged sample clone before serialized references are remapped. | — | — | - -### `Inno.Assets.Pipeline.AssetSerializationServices` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.AssetSerializationServices.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L69) | Captures typed properties and their references as one neutral, owner-independent value.
返回:Frozen native properties with exact stable type identity and collected direct dependencies.
`value`:Current generation settings object, never retained. | — | — | -| [`public TValue Inno.Assets.Pipeline.AssetSerializationServices.Deserialize(System.ReadOnlySpan bytes) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L126) | Deserializes one structured value against the active importer candidate generation.
返回:A restored value owned by the caller.
`bytes`:Complete structured bytes produced by the common serialization system. | — | `System.InvalidOperationException`:Thrown when the value contains an asset reference but no candidate resolver is available. | -| [`public System.Guid Inno.Assets.Pipeline.AssetSerializationServices.GetStableTypeId()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L55) | Resolves the stable persistent type identity of a registered serializable type.
返回:The stable type identifier owned by the active extension generation. | — | `System.InvalidOperationException`:Thrown when TValue has no stable type registration. | -| [`public byte[] Inno.Assets.Pipeline.AssetSerializationServices.Serialize(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L96) | Serializes one structured value and declares every encountered asset reference as a runtime dependency.
返回:Deterministic structured bytes owned by the caller.
`value`:The value to serialize through the active converter generation. | — | `System.ArgumentNullException`:Thrown when value is null. | -| [`public sealed class Inno.Assets.Pipeline.AssetSerializationServices`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L12) | Provides generation-bound structured serialization without exposing host registries to importer extensions. | — | — | - -### `Inno.Assets.Pipeline.AssetSourceMount` - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetSourceMount.AssetSourceMount(Inno.Assets.AssetSourceId id, string rootPath, bool isReadOnly, System.Collections.Generic.IEnumerable? dependencies = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L31) | Creates an asset source mount.
`id`:Stable source identity.
`rootPath`:Absolute or resolvable physical source root.
`isReadOnly`:Whether all source mutations must be rejected.
`dependencies`:Other read-only sources this source may reference. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSourceMount.Resolve(string localPath)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L77) | Resolves a source-local path and rejects physical root escape.
返回:An absolute path contained by this mount.
`localPath`:Source-local path. | — | — | -| [`public System.Collections.Generic.IReadOnlySet Inno.Assets.Pipeline.AssetSourceMount.dependencySourceIds { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L66) | Gets explicitly declared cross-source dependencies. | — | — | -| [`public Inno.Assets.AssetSourceId Inno.Assets.Pipeline.AssetSourceMount.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L51) | Gets the stable source identity. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetSourceMount.isReadOnly { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L61) | Gets whether source mutations are forbidden. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSourceMount.rootPath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L56) | Gets the controlled physical source root. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSourceMount`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L14) | Maps one isolated asset source to a controlled physical root. | — | — | - -### `Inno.Assets.Pipeline.AssetSourceMountTransaction` - - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetSourceMountTransaction.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L136) | Retains one verified output so cache collection cannot remove an active generation.
返回:A caller-owned immutable artifact lease.
`persistentId`:The persistent asset identity.
`outputName`:The required output protocol. | — | `System.InvalidOperationException`:The requested output is unavailable or invalid. | -| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Activate()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L173) | Activates this candidate without releasing the previous generation or notifying observers. | — | — | -| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Complete()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L197) | Commits an activated candidate, notifies observers, and retires the previous generation. | — | `Inno.Core.Execution.RetirementPendingException`:Retirement exceeded its deadline. Both generations remain owned and the host must restart.
`System.AggregateException`:Publication or completed retirement failed; the host generation gate is faulted. | -| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L249) | Discards an unfinished candidate and releases its rebuildable staging storage. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L83) | Gets candidate source entries without publishing them to active AssetPipeline consumers.
返回:A stable candidate entry snapshot.
`includeDirectories`:Whether directory entries should be included. | — | — | -| [`public TAsset Inno.Assets.Pipeline.AssetSourceMountTransaction.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L105) | Loads one candidate asset by isolated source path.
返回:The candidate asset instance.
`path`:Candidate source path. | — | `System.InvalidOperationException`:Thrown when no compatible candidate asset exists. | -| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Rollback()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L223) | Discards the candidate or restores the previous generation after provisional activation. | — | `Inno.Core.Execution.RetirementPendingException`:Candidate retirement exceeded its deadline; its owners remain retained until host restart.
`System.AggregateException`:Compensation or completed retirement failed. | -| [`public bool Inno.Assets.Pipeline.AssetSourceMountTransaction.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L160) | Tries to resolve one named candidate artifact.
返回:True when the candidate output exists.
`persistentId`:Candidate asset identity.
`outputName`:Named artifact output.
`artifact`:Artifact information when available. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetSourceMountTransaction.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L126) | Tries to get candidate catalog information by isolated source path.
返回:True when the candidate path is cataloged.
`path`:Candidate source path.
`info`:Candidate catalog information when available. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.recoveryChanges { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L71) | Gets neutral canonical reference outcomes after activation, or an empty set before activation or after rollback. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.sourceMounts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L65) | Gets the complete isolated mount snapshot represented by this candidate. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSourceMountTransaction : System.IDisposable, Inno.Assets.Pipeline.IAssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L15) | Holds an isolated source-mount candidate that can be inspected before it atomically replaces the active Asset Database. | — | — | - -### `Inno.Assets.Pipeline.AssetSourcePolicy` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Assets.Pipeline.AssetSourcePolicy.AssetSourcePolicy()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L47) | Creates a source policy using the engine's default noise filters. | — | — | -| [`public Inno.Assets.Pipeline.AssetSourcePolicy.AssetSourcePolicy(System.Collections.Generic.IEnumerable? ignoredFileNames, System.Collections.Generic.IEnumerable? ignoredDirectoryNames, System.Collections.Generic.IEnumerable? ignoredPrefixes, System.Collections.Generic.IEnumerable? ignoredSuffixes)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L67) | Creates a source policy with additional ignored names and affixes.
`ignoredFileNames`:Additional case-insensitive file names excluded from every source mount.
`ignoredDirectoryNames`:Additional case-insensitive directory names excluded with their descendants.
`ignoredPrefixes`:Additional file-name prefixes treated as transient authoring noise.
`ignoredSuffixes`:Additional file-name suffixes treated as transient authoring noise. | — | — | -| [`public static bool Inno.Assets.Pipeline.AssetSourcePolicy.IsGeneratedPath(string relativePath)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L131) | Determines whether a path is generated asset metadata.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`relativePath`:The normalized path relative to its owning source mount. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetSourcePolicy.IsIgnored(string relativePath, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L98) | Determines whether an entry is excluded from the source database.
返回:true when the operation succeeds or its condition is satisfied; otherwise, false.
`relativePath`:The normalized path relative to its owning source mount.
`isDirectory`:Whether the source entry represents a directory. | — | — | -| [`public static Inno.Assets.Pipeline.AssetSourcePolicy Inno.Assets.Pipeline.AssetSourcePolicy.defaultPolicy { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L84) | Gets the default source policy. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSourcePolicy`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L10) | Defines which physical entries are excluded from an asset source tree. | — | — | - -### `Inno.Assets.Pipeline.AssetSourceSnapshot` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public byte[] Inno.Assets.Pipeline.AssetSourceSnapshot.bytes { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L161) | Gets a copy of the captured native source bytes. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSourceSnapshot.contentHash { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L165) | Gets the source fingerprint required by a subsequent save. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetSourceSnapshot.isReadOnly { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L169) | Gets whether the installation source cannot be edited in place. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L146) | Contains detached authoring bytes and conflict-detection state. | — | — | - -### `Inno.Assets.Pipeline.AssetSourceStore` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public TAsset Inno.Assets.Pipeline.AssetSourceStore.Decode(System.ReadOnlySpan bytes) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L67) | Restores a detached native value using current-generation asset references.
返回:A detached value; referenced assets remain canonical read-only inputs.
`bytes`:Native source bytes. | — | — | -| [`public byte[] Inno.Assets.Pipeline.AssetSourceStore.Encode(TAsset asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L53) | Encodes native asset properties with the owner's reference context and dependency capture.
返回:Native source bytes, with no canonical asset mutation.
`asset`:Detached editable value. | — | — | -| [`public Inno.Assets.Pipeline.AssetSourceSnapshot Inno.Assets.Pipeline.AssetSourceStore.Read(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L34) | Reads a mounted source without modifying its canonical asset or compiled artifacts.
返回:A detached source and optimistic concurrency fingerprint.
`path`:Exact mounted source identity. | — | — | -| [`public string Inno.Assets.Pipeline.AssetSourceStore.Save(Inno.Assets.AssetPath path, byte[] bytes, string? expectedHash)`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L85) | Atomically saves bytes after checking the source fingerprint; import is a separate operation.
返回:The saved fingerprint.
`path`:Writable mounted destination.
`bytes`:Complete serializable source, including invalid authoring states.
`expectedHash`:Last read/save fingerprint; null requires a nonexistent destination. | — | — | -| [`public sealed class Inno.Assets.Pipeline.AssetSourceStore`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L12) | Reads detached authoring bytes and saves them independently of successful import. | — | — | - -### `Inno.Assets.Pipeline.EditorAssets` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.EditorAssets.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L64) | Captures reload-safe settings using the currently bound authoring owner's converters and references.
返回:Native properties, stable type identity and automatically collected dependencies.
`value`:Settings to capture without saving or mutating their referenced assets. | — | — | -| [`public static TAsset Inno.Assets.Pipeline.EditorAssets.DecodeNative(byte[] bytes) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L43) | Decodes detached native asset bytes with the current authoring pipeline's converters and reference context.
返回:A detached asset value that can be edited without publishing it.
`bytes`:Bytes previously produced by the native asset source codec. | — | — | -| [`public static byte[] Inno.Assets.Pipeline.EditorAssets.EncodeNative(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L22) | Encodes a detached native asset with the current authoring pipeline's converters and reference context.
返回:Reload-safe native asset bytes suitable for draft state and History payloads.
`asset`:Complete detached value to encode without saving or importing it. | — | — | -| [`public static bool Inno.Assets.Pipeline.EditorAssets.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L88) | Creates or replaces a writable project asset source and imports the committed result.
返回:true when the source was committed and imported successfully.
`path`:The writable project asset path.
`asset`:The complete source value to persist. Saving over an existing source retains that source's persistent identity. | — | `System.ArgumentNullException`:Thrown when asset is null.
`System.InvalidOperationException`:Thrown when the current execution context is not bound to an authoring asset pipeline. | -| [`public static class Inno.Assets.Pipeline.EditorAssets`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L11) | Provides Editor-script asset mutations through the authoring pipeline bound by the current host. | — | — | - -### `Inno.Assets.Pipeline.IAssetSampleSourceRewriter` - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Assets.Pipeline.IAssetSampleSourceRewriter.Transform(Inno.Assets.Pipeline.AssetSampleTransformContext context)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L129) | Rewrites staged source files and registers any changed serialized type identities.
`context`:The transaction-owned staged clone. | Runs on a worker with a pinned extension generation. Access only transaction-owned files and context data, observe cancellation, and do not access live Assets or Editor state. | — | -| [`public interface Inno.Assets.Pipeline.IAssetSampleSourceRewriter`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L117) | Rewrites a source language in a private sample clone before source assets are published. | — | — | - -### `Inno.Assets.Pipeline.IAssetSourceSnapshot` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.IAssetSourceSnapshot.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L31) | Captures the indexed entries represented by this view.
返回:A stable entry collection for owner-thread input discovery.
`includeDirectories`:Whether directory entries are included. | — | — | -| [`TAsset Inno.Assets.Pipeline.IAssetSourceSnapshot.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L48) | Loads an authoring asset without publishing a candidate catalog.
返回:The resolved authoring asset.
`path`:The isolated source path. | — | `System.InvalidOperationException`:No compatible asset can be loaded from this view. | -| [`bool Inno.Assets.Pipeline.IAssetSourceSnapshot.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L62) | Resolves immutable catalog information by source path.
返回:True when catalog information exists.
`path`:The isolated source path.
`info`:The catalog information, or null when the source is absent. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.IAssetSourceSnapshot.sourceMounts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L20) | Gets the source mounts represented by this view. | — | — | -| [`public interface Inno.Assets.Pipeline.IAssetSourceSnapshot : Inno.Assets.IAssetArtifactLookup`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L15) | Provides read-only authoring source access for active or isolated candidate compilation. | Capture inputs on the owner thread and retain the owning generation transaction until all asynchronous consumers drain. This view neither activates nor commits its underlying catalog. | — | - -### `Inno.Assets.Pipeline.NativeAssetSourceSerialization` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static byte[] Inno.Assets.Pipeline.NativeAssetSourceSerialization.Export(TAsset asset, Inno.Assets.Pipeline.AssetSerializationServices services) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Serialization/NativeAssetSourceSerialization.cs#L30) | Serializes one asset's editable properties and direct asset dependencies.
返回:Deterministic native source bytes.
`asset`:Unsaved or loaded asset whose editable state should be captured.
`services`:The generation-bound serialization services supplied by the export context. | — | — | -| [`public static TAsset Inno.Assets.Pipeline.NativeAssetSourceSerialization.Import(System.ReadOnlySpan bytes, Inno.Assets.Pipeline.AssetSerializationServices services, out System.Collections.Generic.IReadOnlyList dependencies) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Serialization/NativeAssetSourceSerialization.cs#L73) | Restores one concrete asset and its declared direct dependencies.
返回:A detached asset ready for an import transaction.
`bytes`:Native source bytes.
`dependencies`:Receives persistent dependencies discovered during export.
`services`:The generation-bound serialization and reference services supplied by the import context. | — | `System.InvalidOperationException`:Thrown when the source type identity is incompatible. | +| [`public readonly struct Inno.Assets.Pipeline.AssetCacheOptions`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L8) | Controls cleanup of rebuildable asset database data. | — | — | +| [`public long Inno.Assets.Pipeline.AssetCacheOptions.maximumSizeBytes { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L13) | Gets the maximum artifact cache size in bytes, or zero for no size limit. | — | — | +| [`public System.TimeSpan Inno.Assets.Pipeline.AssetCacheOptions.garbageCollectionGracePeriod { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L18) | Gets the minimum age of an unreachable artifact before it can be collected. | — | — | +| [`public static Inno.Assets.Pipeline.AssetCacheOptions Inno.Assets.Pipeline.AssetCacheOptions.CreateDefault()`](../../src/content/assets/Inno.Assets.Pipeline/AssetCacheOptions.cs#L26) | Creates the default cache policy. | The default cache options. | — | +| [`public sealed class Inno.Assets.Pipeline.AssetPipeline : Inno.Assets.AssetResidencyProvider, System.IDisposable, Inno.Assets.IAssetLookup, Inno.Assets.IAssetReferenceResolver, Inno.Assets.IAssetPropertyStateResolver, Inno.Assets.IAssetArtifactLookup, Inno.Assets.IAssetResidency, Inno.Assets.Pipeline.IAssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L22) | Provides the single application-level entry point for importing, loading, saving and collecting assets. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L39) | Tries to resolve a named artifact output.
`persistentId`: The artifact owner identity.
`outputName`: The named output.
`artifact`: The output information when available. | when the output exists. | — | +| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetPipeline.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L61) | Acquires one verified authoring artifact generation for an explicit lifetime.
`persistentId`: Persistent identity of the artifact owner.
`outputName`: Stable artifact output name. | A lease over the currently committed immutable artifact generation. | `System.InvalidOperationException`: Thrown when the requested artifact is unavailable. | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.BuildAsync(Inno.Assets.AssetObject definition, System.Collections.Generic.IReadOnlyList inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L81) | Runs an aggregate asset build using the processor registered for a definition.
`definition`: The build definition asset.
`inputs`: The immutable input catalog snapshots.
`cancellationToken`: Cancellation for the candidate build. | The content-addressed output bundle key. | — | +| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetPipeline.ExportRuntimeArtifacts(string destinationContentRoot)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L108) | Exports the current source-free runtime catalog and its exact artifact closure.
`destinationContentRoot`: Empty destination for the deployed asset database. | Counts and source identities for the deployed runtime snapshot. | `System.InvalidOperationException`: Thrown when runtime artifacts are incomplete, a deployed reference is authoring-only, or a transitive authoring input has failed or changed since import. Last-good inputs do not certify an export.
`System.IO.IOException`: Thrown when the destination is not empty or cannot be written. | +| [`public System.Threading.Tasks.Task Inno.Assets.Pipeline.AssetPipeline.ExportRuntimeArtifactsAsync(string destinationContentRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Artifacts.cs#L144) | Exports a runtime-only artifact snapshot on a worker without loading artifact files into memory.
`destinationContentRoot`: The empty destination that receives the runtime asset database.
`cancellationToken`: The token checked while individual artifact files are copied. | A task that completes with counts and source identities for the captured runtime snapshot. | `System.InvalidOperationException`: Thrown when runtime artifacts are incomplete, transitive authoring inputs have failed or become stale, or the asset service is not available.
`System.OperationCanceledException`: Thrown when cancellation is requested before export completes. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.Rescan()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L27) | Reconciles source files, generated files and the persistent catalog. | — | — | +| [`public void Inno.Assets.Pipeline.AssetPipeline.Update()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L39) | Applies queued source and build changes on the initialization thread. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L72) | Gets indexed source entries.
`includeDirectories`: Whether directories should be included. | The stable source entry snapshot. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetFileSystemChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L84) | Gets immediate indexed children of a source directory.
`parent`: The isolated parent path. | The immediate child entry snapshot. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetFileSystemEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Changes.cs#L98) | Tries to resolve an indexed source entry.
`path`: The isolated source path.
`entry`: The resolved source entry. | when the entry exists and is not generated metadata. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.isInitialized { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L60) | Gets whether asset services are initialized. | — | — | +| [`public string Inno.Assets.Pipeline.AssetPipeline.assetRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L65) | Gets the absolute source asset root. | — | — | +| [`public string Inno.Assets.Pipeline.AssetPipeline.libraryRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L70) | Gets the absolute root containing rebuildable asset database data. | — | — | +| [`public string Inno.Assets.Pipeline.AssetPipeline.artifactRoot { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L75) | Gets the absolute generated artifact root. | — | — | +| [`public long Inno.Assets.Pipeline.AssetPipeline.revision { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L81) | Gets the monotonic identity of the current committed asset and source-mount state. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.sourceMounts { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L87) | Gets the active isolated source mount snapshot. | — | — | +| [`public Inno.Core.Identity.IdentityAllocator Inno.Assets.Pipeline.AssetPipeline.identities { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L93) | Gets the authoring identity domain shared by canonical assets and addressable source entries. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourceStore Inno.Assets.Pipeline.AssetPipeline.CreateSourceStore()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L102) | Creates a detached source editor sharing this owner's mounts, native converters and references. | A source store that must not outlive this asset pipeline. | — | +| [`public Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.AssetPipeline.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L116) | Captures native settings and nested asset dependencies through this explicit authoring owner.
`value`: Typed settings, never retained by the returned snapshot.
`TValue`: Current settings type. | Complete stable properties and dependencies without writing or importing an asset. | — | +| [`public void Inno.Assets.Pipeline.AssetPipeline.RestoreProperties(System.Guid stableTypeId, byte[] propertyData, TValue target) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L138) | Restores serialized properties to the existing asset object.
`stableTypeId`: The stable type id consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`propertyData`: The property data consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`target`: The existing target that receives the validated result.
`TValue`: Serialized asset object type receiving restored properties. | — | — | +| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.Changed`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L158) | Occurs after an asset database transaction has committed. | — | — | +| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.AssetReloaded`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L163) | Occurs after a canonical loaded asset has been updated in place. | — | — | +| [`public System.Action? Inno.Assets.Pipeline.AssetPipeline.SourceMountsChanged`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L169) | Occurs after a complete isolated source-mount generation is atomically replaced. | — | — | +| [`public Inno.Assets.Pipeline.AssetPipeline.AssetPipeline(Inno.Extensibility.Modules.ModuleHost modules, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, Inno.Assets.Pipeline.AssetPipelineOptions options)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L199) | Creates one isolated authoring or deployed-runtime asset pipeline.
`modules`: The module host whose candidate generations coordinate importer refreshes.
`types`: The type catalog used to discover and resolve asset extensions.
`serialization`: The serialization registry used for metadata and canonical asset state.
`identities`: The identity allocator that owns every canonical asset in this pipeline.
`logs`: The explicitly owned router receiving asset pipeline diagnostics.
`diagnostics`: The diagnostic hub that owns source database and asset processing reports.
`options`: The asset source, artifact and watcher configuration. | — | `System.ArgumentNullException`: Thrown when a required service is null. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.cs#L294) | Waits until queued source watcher changes have been processed. | — | — | +| [`public void Inno.Assets.Pipeline.AssetPipeline.CompleteExtensionDiscovery()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L31) | Ends initial authoring extension discovery and strictly retries dependent imports. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetAssetType(Inno.Assets.AssetPath path, out System.Type? assetType)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L51) | Tries to resolve an asset type without loading the asset.
`path`: The isolated source path.
`assetType`: The resolved concrete asset type. | when the type can be resolved. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetPersistentId(Inno.Assets.AssetPath path, out System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L68) | Tries to resolve a persistent identity without loading the asset.
`path`: The isolated source path.
`persistentId`: The resolved persistent identity. | when catalog metadata exists. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L85) | Tries to get a catalog snapshot by source-relative path.
`path`: The isolated source path.
`info`: The catalog snapshot when available. | when the path is cataloged. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryGetInfo(System.Guid persistentId, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Discovery.cs#L102) | Tries to get a catalog snapshot by persistent identity.
`persistentId`: The persistent asset identity.
`info`: The catalog snapshot when available. | when the identity is cataloged. | — | +| [`public TAsset Inno.Assets.Pipeline.AssetPipeline.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L36) | Loads a canonical asset by isolated source path.
`path`: The isolated source path.
`TAsset`: The required asset type. | The canonical asset instance. | — | +| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetPipeline.Load(Inno.Assets.AssetPath path, System.Type assetType)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L65) | Loads a canonical asset using a runtime asset type selected by an authoring workflow.
`path`: The isolated source path.
`assetType`: The required concrete or base asset type. | The canonical asset instance assignable to . | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when does not derive from .
`System.InvalidOperationException`: Thrown when no compatible canonical asset can be loaded. | +| [`public TAsset Inno.Assets.Pipeline.AssetPipeline.Load(System.Guid persistentId) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L95) | Loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`TAsset`: The required asset type. | The canonical asset instance. | `System.InvalidOperationException`: Thrown when no compatible asset can be loaded. | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryLoad(Inno.Assets.AssetPath path, out TAsset? asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L117) | Tries to load a canonical asset by isolated source path.
`path`: The isolated source path.
`asset`: The canonical asset when successful.
`TAsset`: The required asset type. | when a compatible asset was loaded. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.TryLoad(System.Guid persistentId, out TAsset? asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L142) | Tries to load a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`asset`: The canonical asset when successful.
`TAsset`: The required asset type. | when a compatible asset was loaded. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.LoadAsync(Inno.Assets.AssetPath path, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L167) | Asynchronously loads a canonical asset by isolated source path.
`path`: The isolated source path.
`cancellationToken`: Cancellation for the current caller's wait.
`TAsset`: The required asset type. | The canonical asset instance. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetPipeline.LoadAsync(System.Guid persistentId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L196) | Asynchronously loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`cancellationToken`: Cancellation for the current caller's wait.
`TAsset`: The required asset type. | The canonical asset instance. | — | +| [`public System.Threading.Tasks.ValueTask> Inno.Assets.Pipeline.AssetPipeline.AcquireAsync(Inno.Assets.AssetPath path, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L225) | Asynchronously acquires a canonical authoring asset by path for an explicit managed lifetime.
`path`: Mount-qualified logical asset path.
`cancellationToken`: Cancellation for this caller's asynchronous wait.
`TAsset`: Required asset contract. | A lease whose strong reference participates in the existing reachability sweep. | — | +| [`public System.Threading.Tasks.ValueTask> Inno.Assets.Pipeline.AssetPipeline.AcquireAsync(System.Guid persistentId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L250) | Asynchronously acquires a canonical authoring asset by persistent identity.
`persistentId`: Persistent asset identity.
`cancellationToken`: Cancellation for this caller's asynchronous wait.
`TAsset`: Required asset contract. | A lease whose strong reference participates in the existing reachability sweep. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetLoadedPaths()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L290) | Gets isolated source paths for all canonical loaded assets. | A stable isolated path snapshot. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L304) | Gets direct or transitive runtime dependencies of an asset.
`asset`: The asset to query.
`recursive`: Whether transitive dependencies should be included. | The dependency descriptors. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetPipeline.GetImportDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L322) | Gets source import dependencies that invalidate an asset artifact.
`asset`: The asset to query.
`recursive`: Whether transitive source dependencies should be included. | Canonical isolated source paths in stable order. | — | +| [`public Inno.Assets.AssetReferenceInfo Inno.Assets.Pipeline.AssetPipeline.GetReferenceInfo(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Loading.cs#L337) | Gets an engine-known reference diagnostic snapshot.
`asset`: The asset to inspect. | The reference diagnostic snapshot. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.Import(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L33) | Imports one source asset from an isolated source mount.
`path`: The isolated source path. | when an importer handled the source. | — | +| [`public Inno.Assets.Pipeline.AssetImportSettingsSnapshot Inno.Assets.Pipeline.AssetPipeline.GetImportSettings(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L57) | Reads a detached settings copy for the currently registered importer.
`path`: The isolated source path. | The settings copy and the fingerprint required for saving it. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.SaveImportSettings(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable? settings, string expectedFingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L82) | Saves import settings with conflict detection and immediately attempts to reimport the source.
`path`: The writable isolated source path.
`settings`: The edited settings, or null to reset to importer defaults.
`expectedFingerprint`: The fingerprint obtained when reading the settings. | True when settings were saved and reimport succeeded; false when the saved settings failed import. | `System.IO.IOException`: The settings changed externally or could not be written. | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.Save(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L107) | Saves an asset to its current source path.
`asset`: The asset to save. | when an importer exported the asset. | — | +| [`public bool Inno.Assets.Pipeline.AssetPipeline.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L125) | Saves an asset to a writable isolated source path, preserving the destination source identity when it exists.
`path`: Writable isolated source path.
`asset`: Asset to save. A detached value replaces the destination content without replacing its persistent identity. | when an importer exported the asset. | — | +| [`public void Inno.Assets.Pipeline.AssetPipeline.Move(Inno.Assets.AssetPath source, Inno.Assets.AssetPath target)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L163) | Moves a source asset while preserving its persistent identity and generated metadata.
`source`: Existing isolated source path.
`target`: New isolated source path. | — | `System.IO.FileNotFoundException`: Thrown when the source does not exist.
`System.IO.IOException`: Thrown when the target source or metadata already exists. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.Delete(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L230) | Deletes a source asset and its metadata while retaining a Library tombstone for existing references.
`path`: Existing isolated file or directory path. | — | `System.IO.FileNotFoundException`: Thrown when the source does not exist. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.CreateDirectory(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Mutations.cs#L298) | Creates a tracked source directory and its persistent metadata.
`path`: New isolated directory path. | — | `System.IO.DirectoryNotFoundException`: Thrown when the parent directory does not exist.
`System.IO.IOException`: Thrown when the target already exists. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Retirement.cs#L27) | Releases watchers, catalog participants, canonical objects, and rebuildable staging state. | — | — | +| [`public int Inno.Assets.Pipeline.AssetPipeline.UnloadUnusedAssets()`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipeline.Retirement.cs#L40) | Collects assets that have no external managed references. | The number of released canonical assets. | — | +| [`public Inno.Assets.Pipeline.AssetSampleImportTransaction Inno.Assets.Pipeline.AssetPipeline.PrepareSampleImport(Inno.Assets.AssetPath source)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.Samples.cs#L48) | Starts a private background sample clone without waiting for file copying or transformation.
`source`: An indexed installed Plugin sample directory whose final segment starts with ~. | The generation-pinned transaction to advance, validate and commit on this pipeline's owner thread. | `System.ArgumentException`: The source is not an installed sample directory.
`System.IO.IOException`: The destination name already exists.
`System.InvalidOperationException`: Another mutation or generation transaction is active, or this is not the initialization thread. | +| [`public void Inno.Assets.Pipeline.AssetPipeline.ReplaceSourceMounts(System.Collections.Generic.IReadOnlyList mounts)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.SourceMounts.cs#L155) | Validates and atomically replaces the complete source-mount generation while preserving the active generation after any candidate failure.
`mounts`: A writable project source followed by zero or more read-only sources. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourceMountTransaction Inno.Assets.Pipeline.AssetPipeline.PrepareSourceMounts(System.Collections.Generic.IReadOnlyList mounts)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetPipeline.SourceMounts.cs#L172) | Builds and validates an isolated source-mount candidate without changing active AssetPipeline state.
`mounts`: A writable project source followed by zero or more read-only sources. | A transaction that can be inspected, activated, completed, or rolled back. | — | +| [`public enum Inno.Assets.Pipeline.AssetPipelineMode : int`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L6) | Defines which side of the asset pipeline an instance serves. | — | — | +| [`Inno.Assets.Pipeline.AssetPipelineMode.Authoring = 0`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L11) | Reconciles writable source files and produces immutable artifacts. | — | — | +| [`Inno.Assets.Pipeline.AssetPipelineMode.RuntimeArtifacts = 1`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineMode.cs#L16) | Loads a read-only deployed catalog and its content-addressed artifacts without source files. | — | — | +| [`public readonly struct Inno.Assets.Pipeline.AssetPipelineOptions`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L11) | Initialization options for . | — | — | +| [`public bool Inno.Assets.Pipeline.AssetPipelineOptions.deferUnavailableExtensions { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L19) | Gets whether the composition host must still publish its initial authoring extensions. | — | — | +| [`public Inno.Assets.Pipeline.AssetPipelineMode Inno.Assets.Pipeline.AssetPipelineOptions.mode { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L24) | Gets whether the pipeline reconciles authoring sources or consumes a deployed artifact catalog. | — | — | +| [`public string Inno.Assets.Pipeline.AssetPipelineOptions.assetRoot { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L29) | Gets the root folder containing source assets. | — | — | +| [`public string Inno.Assets.Pipeline.AssetPipelineOptions.libraryRoot { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L34) | Gets the root folder containing rebuildable project data. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetPipelineOptions.enableFileSystemWatcher { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L39) | Gets whether file-system watching is enabled. | — | — | +| [`public int Inno.Assets.Pipeline.AssetPipelineOptions.fileWatcherFlushDelayMs { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L44) | Gets the watcher change coalescing delay in milliseconds. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourcePolicy? Inno.Assets.Pipeline.AssetPipelineOptions.sourcePolicy { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L49) | Gets the source filtering policy. | — | — | +| [`public System.Collections.Generic.IReadOnlyList? Inno.Assets.Pipeline.AssetPipelineOptions.sourceMounts { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L54) | Gets the complete source mount snapshot, or null to mount only . | — | — | +| [`public Inno.Assets.Pipeline.AssetCacheOptions Inno.Assets.Pipeline.AssetPipelineOptions.cacheOptions { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L59) | Gets the rebuildable cache policy. | — | — | +| [`public static Inno.Assets.Pipeline.AssetPipelineOptions Inno.Assets.Pipeline.AssetPipelineOptions.Create(string assetRoot, string libraryRoot)`](../../src/content/assets/Inno.Assets.Pipeline/AssetPipelineOptions.cs#L73) | Creates options with sensible defaults for most projects.
`assetRoot`: Source assets root folder.
`libraryRoot`: Rebuildable Library folder. | Initialized options value. | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSourceMountTransaction : System.IDisposable, Inno.Assets.Pipeline.IAssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L15) | Holds an isolated source-mount candidate that can be inspected before it atomically replaces the active Asset Database. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.sourceMounts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L65) | Gets the complete isolated mount snapshot represented by this candidate. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.recoveryChanges { get; }`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L71) | Gets neutral canonical reference outcomes after activation, or an empty set before activation or after rollback. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetSourceMountTransaction.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L83) | Gets candidate source entries without publishing them to active AssetPipeline consumers.
`includeDirectories`: Whether directory entries should be included. | A stable candidate entry snapshot. | — | +| [`public TAsset Inno.Assets.Pipeline.AssetSourceMountTransaction.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L105) | Loads one candidate asset by isolated source path.
`path`: Candidate source path.
`TAsset`: Required asset type. | The candidate asset instance. | `System.InvalidOperationException`: Thrown when no compatible candidate asset exists. | +| [`public bool Inno.Assets.Pipeline.AssetSourceMountTransaction.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L126) | Tries to get candidate catalog information by isolated source path.
`path`: Candidate source path.
`info`: Candidate catalog information when available. | True when the candidate path is cataloged. | — | +| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetSourceMountTransaction.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L136) | Retains one verified output so cache collection cannot remove an active generation.
`persistentId`: The persistent asset identity.
`outputName`: The required output protocol. | A caller-owned immutable artifact lease. | `System.InvalidOperationException`: The requested output is unavailable or invalid. | +| [`public bool Inno.Assets.Pipeline.AssetSourceMountTransaction.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L160) | Tries to resolve one named candidate artifact.
`persistentId`: Candidate asset identity.
`outputName`: Named artifact output.
`artifact`: Artifact information when available. | True when the candidate output exists. | — | +| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Activate()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L173) | Activates this candidate without releasing the previous generation or notifying observers. | — | — | +| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Complete()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L197) | Commits an activated candidate, notifies observers, and retires the previous generation. | — | `Inno.Core.Execution.RetirementPendingException`: Retirement exceeded its deadline. Both generations remain owned and the host must restart.
`System.AggregateException`: Publication or completed retirement failed; the host generation gate is faulted. | +| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Rollback()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L223) | Discards the candidate or restores the previous generation after provisional activation. | — | `Inno.Core.Execution.RetirementPendingException`: Candidate retirement exceeded its deadline; its owners remain retained until host restart.
`System.AggregateException`: Compensation or completed retirement failed. | +| [`public void Inno.Assets.Pipeline.AssetSourceMountTransaction.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/AssetSourceMountTransaction.cs#L249) | Discards an unfinished candidate and releases its rebuildable staging storage. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSourceStore`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L12) | Reads detached authoring bytes and saves them independently of successful import. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourceSnapshot Inno.Assets.Pipeline.AssetSourceStore.Read(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L34) | Reads a mounted source without modifying its canonical asset or compiled artifacts.
`path`: Exact mounted source identity. | A detached source and optimistic concurrency fingerprint. | — | +| [`public byte[] Inno.Assets.Pipeline.AssetSourceStore.Encode(TAsset asset) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L53) | Encodes native asset properties with the owner's reference context and dependency capture.
`asset`: Detached editable value.
`TAsset`: Native asset source type. | Native source bytes, with no canonical asset mutation. | — | +| [`public TAsset Inno.Assets.Pipeline.AssetSourceStore.Decode(System.ReadOnlySpan bytes) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L67) | Restores a detached native value using current-generation asset references.
`bytes`: Native source bytes.
`TAsset`: Native asset source type. | A detached value; referenced assets remain canonical read-only inputs. | — | +| [`public string Inno.Assets.Pipeline.AssetSourceStore.Save(Inno.Assets.AssetPath path, byte[] bytes, string? expectedHash)`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L85) | Atomically saves bytes after checking the source fingerprint; import is a separate operation.
`path`: Writable mounted destination.
`bytes`: Complete serializable source, including invalid authoring states.
`expectedHash`: Last read/save fingerprint; null requires a nonexistent destination. | The saved fingerprint. | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSourceSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L146) | Contains detached authoring bytes and conflict-detection state. | — | — | +| [`public byte[] Inno.Assets.Pipeline.AssetSourceSnapshot.bytes { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L161) | Gets a copy of the captured native source bytes. | — | — | +| [`public string Inno.Assets.Pipeline.AssetSourceSnapshot.contentHash { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L165) | Gets the source fingerprint required by a subsequent save. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSourceSnapshot.isReadOnly { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Editing/AssetSourceStore.cs#L169) | Gets whether the installation source cannot be edited in place. | — | — | +| [`public static class Inno.Assets.Pipeline.EditorAssets`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L11) | Provides Editor-script asset mutations through the authoring pipeline bound by the current host. | — | — | +| [`public static byte[] Inno.Assets.Pipeline.EditorAssets.EncodeNative(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L22) | Encodes a detached native asset with the current authoring pipeline's converters and reference context.
`asset`: Complete detached value to encode without saving or importing it. | Reload-safe native asset bytes suitable for draft state and History payloads. | — | +| [`public static TAsset Inno.Assets.Pipeline.EditorAssets.DecodeNative(byte[] bytes) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L43) | Decodes detached native asset bytes with the current authoring pipeline's converters and reference context.
`bytes`: Bytes previously produced by the native asset source codec.
`TAsset`: Expected native asset type. | A detached asset value that can be edited without publishing it. | — | +| [`public static Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.EditorAssets.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L64) | Captures reload-safe settings using the currently bound authoring owner's converters and references.
`value`: Settings to capture without saving or mutating their referenced assets.
`TValue`: Current serializable settings type. | Native properties, stable type identity and automatically collected dependencies. | — | +| [`public static bool Inno.Assets.Pipeline.EditorAssets.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/EditorAssets.cs#L88) | Creates or replaces a writable project asset source and imports the committed result.
`path`: The writable project asset path.
`asset`: The complete source value to persist. Saving over an existing source retains that source's persistent identity. | when the source was committed and imported successfully. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the current execution context is not bound to an authoring asset pipeline. | +| [`public sealed class Inno.Assets.Pipeline.AssetArtifactWriter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | Collects immutable named outputs for an aggregate asset build. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetArtifactWriter.WriteAsync(string outputName, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope = Inno.Assets.Pipeline.AssetDeploymentScope.Runtime)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L36) | Writes one named build output.
`outputName`: The stable output name.
`bytes`: The output content.
`cancellationToken`: Cancellation for the write operation.
`deploymentScope`: Whether runtime packages retain this output. | A completed operation after the output has been staged. | — | +| [`public void Inno.Assets.Pipeline.AssetArtifactWriter.ReportDiagnostic(string message)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L60) | Adds a build diagnostic.
`message`: The diagnostic message. | — | — | +| [`public Inno.Assets.Pipeline.AssetArtifactWriter.AssetArtifactWriter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetCatalogCandidate : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L14) | Owns the isolated catalog storage used to validate one candidate Asset Pipeline generation. | — | — | +| [`public Inno.Assets.Pipeline.AssetLoader Inno.Assets.Pipeline.AssetCatalogCandidate.loader { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L34) | Gets the isolated loader whose catalog and in-memory records represent the candidate generation. | — | — | +| [`public void Inno.Assets.Pipeline.AssetCatalogCandidate.Commit()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L48) | Publishes staged source metadata and atomically promotes the validated catalog, compensating metadata on failure. | — | `System.InvalidOperationException`: Thrown when the candidate has already been committed or disposed.
`System.IO.IOException`: Source metadata changed externally, a sidecar cannot be written, or the catalog cannot be promoted.
`System.UnauthorizedAccessException`: The operating system rejects a source sidecar or catalog write, including a directory at a file destination. | +| [`public void Inno.Assets.Pipeline.AssetCatalogCandidate.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetCatalogCandidate.cs#L60) | Removes the candidate catalog staging storage without disposing the candidate loader. | — | — | +| [`public enum Inno.Assets.Pipeline.AssetDeploymentScope : int`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L6) | Defines whether an imported asset participates in deployed runtime content. | — | — | +| [`Inno.Assets.Pipeline.AssetDeploymentScope.Runtime = 0`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L11) | The asset is deployed and must produce a named runtime artifact output. | — | — | +| [`Inno.Assets.Pipeline.AssetDeploymentScope.AuthoringOnly = 1`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetDeploymentScope.cs#L16) | The asset exists only for authoring workflows and is omitted from deployed catalogs. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetExportContext`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L12) | Provides generation-bound services for one editable asset source export. | — | — | +| [`public Inno.Extensibility.Types.TypeCatalog Inno.Assets.Pipeline.AssetExportContext.types { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L29) | Gets the type catalog bound to the active export generation. | — | — | +| [`public Inno.Core.Serialization.SerializationRegistry Inno.Assets.Pipeline.AssetExportContext.serialization { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L35) | Gets the serialization registry bound to the active export generation. | — | — | +| [`public Inno.Assets.Pipeline.AssetSerializationServices Inno.Assets.Pipeline.AssetExportContext.services { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L40) | Gets the narrow structured serialization API bound to this export generation. | — | — | +| [`public Inno.Assets.IAssetArtifactLookup Inno.Assets.Pipeline.AssetExportContext.artifacts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetExportContext.cs#L46) | Gets the owner-bound immutable outputs used to reconstruct editable sources. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImportContext`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L16) | Collects source data and dependency declarations for one import operation. | — | — | +| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetImportContext.AcquireArtifact(System.Guid assetId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L106) | Acquires an immutable dependency output and records its invalidation dependency automatically.
`assetId`: Persistent dependency identity.
`outputName`: Stable named output protocol. | A caller-owned lease that must be disposed after reading. | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetImportContext.assetPath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L117) | Gets the isolated source path. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportContext.absolutePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L122) | Gets the absolute source path. | — | — | +| [`public System.ReadOnlyMemory Inno.Assets.Pipeline.AssetImportContext.sourceBytes { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L127) | Gets the raw source bytes. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportContext.sourceHash { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L132) | Gets the deterministic source hash. | — | — | +| [`public System.Guid Inno.Assets.Pipeline.AssetImportContext.persistentId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L137) | Gets the persistent identity assigned to the source asset. | — | — | +| [`public Inno.Extensibility.Types.TypeCatalog Inno.Assets.Pipeline.AssetImportContext.types { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L143) | Gets the type catalog bound to the active import generation. | — | — | +| [`public Inno.Core.Serialization.SerializationRegistry Inno.Assets.Pipeline.AssetImportContext.serialization { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L149) | Gets the serialization registry bound to the active import generation. | — | — | +| [`public Inno.Assets.IAssetReferenceResolver Inno.Assets.Pipeline.AssetImportContext.references { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L155) | Gets the asset-reference resolver bound to the isolated candidate generation. | — | — | +| [`public Inno.Assets.Pipeline.AssetSerializationServices Inno.Assets.Pipeline.AssetImportContext.services { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L160) | Gets the narrow structured serialization API bound to this importer candidate. | — | — | +| [`public Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImportContext.importSettings { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L166) | Gets the settings restored from this source's sidecar against the isolated import generation. The value is detached and changes made during import are not saved to the sidecar. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportContext.extension { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L171) | Gets the normalized lower-case source extension. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportContext.ReadUtf8Text()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L179) | Reads the source bytes as UTF-8 text. | The decoded text without an optional byte-order mark. | — | +| [`public System.ReadOnlyMemory Inno.Assets.Pipeline.AssetImportContext.ReadSourceBytes(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L199) | Reads another source from the same candidate mount snapshot and records it as an import dependency.
`path`: An isolated path whose mount dependency was declared when it crosses Plugin boundaries. | An immutable source snapshot owned by the import operation. | `System.InvalidOperationException`: Thrown when the source crosses an undeclared mount boundary or references the writable Project from a Plugin.
`System.IO.FileNotFoundException`: Thrown when the resolved source does not exist. | +| [`public string Inno.Assets.Pipeline.AssetImportContext.ReadSourceUtf8Text(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L216) | Reads another source as UTF-8 from the current candidate mount snapshot and records the dependency.
`path`: An isolated source path. | Decoded UTF-8 text without an optional byte-order mark. | — | +| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnAsset(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L230) | Declares a direct runtime dependency by isolated source path.
`path`: The isolated dependency path. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnAsset(Inno.Assets.AssetDependency dependency)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L243) | Declares a direct runtime dependency by persistent descriptor.
`dependency`: The persistent dependency descriptor. | — | — | +| [`public TAsset Inno.Assets.Pipeline.AssetImportContext.ResolveDependency(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L263) | Resolves and declares a strongly typed runtime asset dependency during import.
`path`: Isolated dependency path.
`TAsset`: Expected asset type. | The currently committed dependency asset. | `System.InvalidOperationException`: Thrown when the dependency cannot be imported or has another type. | +| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnSource(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L285) | Declares a source file that invalidates this imported asset.
`path`: The isolated dependency path. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnArtifact(System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L301) | Declares an imported artifact that invalidates this imported asset.
`persistentId`: The persistent identity of the artifact owner. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportContext.DependsOnCustomInput(string key, string fingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportContext.cs#L320) | Declares a custom deterministic input that invalidates this asset.
`key`: The input identifier.
`fingerprint`: The current deterministic input fingerprint. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImporterAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L15) | Declares the immutable protocol identity of an automatically discovered asset importer. | — | — | +| [`public Inno.Assets.Pipeline.AssetImporterAttribute.AssetImporterAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L23) | Creates importer discovery metadata.
`id`: Globally stable importer protocol identifier. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImporterAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L32) | Gets the globally stable importer protocol identifier. | — | — | +| [`public abstract class Inno.Assets.Pipeline.AssetImporter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L38) | Defines metadata shared by automatically discovered asset importers. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImporter.importerId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L45) | Gets the stable importer implementation identifier. | — | — | +| [`public virtual Inno.Assets.Pipeline.AssetDeploymentScope Inno.Assets.Pipeline.AssetImporter.deploymentScope { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L52) | Gets whether imported assets are deployed or retained only for authoring workflows. | — | — | +| [`public abstract System.Type Inno.Assets.Pipeline.AssetImporter.targetAssetType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L57) | Gets the concrete asset type produced by this importer. | — | — | +| [`public abstract System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetImporter.supportedExtensions { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L62) | Gets the normalized source extensions accepted by this importer. | — | — | +| [`public virtual Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImporter.CreateImportSettings()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L71) | Creates a detached settings value for this importer in the current extension generation. | A fresh serializable value with a registered stable type identity, or null when this importer has no settings. Defaults must be deterministic; implementations must not retain the returned instance. | — | +| [`protected Inno.Assets.Pipeline.AssetImporter.AssetImporter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L38) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Assets.Pipeline.AssetImporter where TAsset : Inno.Assets.AssetObject : Inno.Assets.Pipeline.AssetImporter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L101) | Provides the strongly typed implementation base for an asset importer.
`TAsset`: The concrete imported asset type. | — | — | +| [`public override sealed System.Type Inno.Assets.Pipeline.AssetImporter.targetAssetType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L106) | Gets the runtime asset type accepted by this importer implementation. | — | — | +| [`protected abstract System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetImporter.ImportAsync(Inno.Assets.Pipeline.AssetImportContext context, Inno.Assets.Pipeline.AssetImportWriter output, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L123) | Imports one source into a managed asset and named artifact outputs.
`context`: The import transaction context.
`output`: The candidate output writer.
`cancellationToken`: Cancellation for the import. | An operation that completes when the candidate has been fully staged. | — | +| [`protected virtual System.Threading.Tasks.ValueTask?> Inno.Assets.Pipeline.AssetImporter.ExportAsync(Inno.Assets.Pipeline.AssetExportContext context, TAsset asset, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L144) | Exports an asset back into source bytes.
`context`: The generation-bound source export services.
`asset`: The asset to export.
`cancellationToken`: Cancellation for the export. | The source bytes, or when export is unsupported. | — | +| [`protected Inno.Assets.Pipeline.AssetImporter.AssetImporter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImporter.cs#L101) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImportExtensionUnavailableException : System.InvalidOperationException`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L11) | Reports a required authoring extension absent from the current generation. | — | — | +| [`public Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.AssetImportExtensionUnavailableException(string extensionKind, string extensionId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L22) | Describes a missing extension using generation-neutral identities.
`extensionKind`: The stable extension protocol identity.
`extensionId`: The required implementation's stable identity. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.extensionKind { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L37) | Gets the stable extension protocol identity. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportExtensionUnavailableException.extensionId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportExtensionUnavailableException.cs#L42) | Gets the required implementation's stable identity. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImportHealthSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L14) | Represents an immutable observation of writable-source import failures at one point in time. | — | — | +| [`public static Inno.Assets.Pipeline.AssetImportHealthSnapshot Inno.Assets.Pipeline.AssetImportHealthSnapshot.empty { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L32) | Gets the immutable snapshot used when no Asset Pipeline generation is active. | — | — | +| [`public readonly record struct Inno.Assets.Pipeline.AssetImportFailure : System.IEquatable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Describes a writable-source import failure introduced after an earlier health snapshot.
`assetPath`: The isolated path of the source that failed to import.
`diagnostics`: The deterministic importer diagnostics associated with the failure. | — | — | +| [`public Inno.Assets.Pipeline.AssetImportFailure.AssetImportFailure(string assetPath, string diagnostics)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Describes a writable-source import failure introduced after an earlier health snapshot.
`assetPath`: The isolated path of the source that failed to import.
`diagnostics`: The deterministic importer diagnostics associated with the failure. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportFailure.assetPath { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L45) | The isolated path of the source that failed to import. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportFailure.diagnostics { get; init; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L46) | The deterministic importer diagnostics associated with the failure. | — | — | +| [`public override string Inno.Assets.Pipeline.AssetImportFailure.ToString()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Assets.Pipeline.AssetImportFailure.operator !=(Inno.Assets.Pipeline.AssetImportFailure left, Inno.Assets.Pipeline.AssetImportFailure right)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Assets.Pipeline.AssetImportFailure.operator ==(Inno.Assets.Pipeline.AssetImportFailure left, Inno.Assets.Pipeline.AssetImportFailure right)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Assets.Pipeline.AssetImportFailure.GetHashCode()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Assets.Pipeline.AssetImportFailure.Equals(object obj)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Assets.Pipeline.AssetImportFailure.Equals(Inno.Assets.Pipeline.AssetImportFailure other)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Assets.Pipeline.AssetImportFailure.Deconstruct(out string assetPath, out string diagnostics)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportHealthSnapshot.cs#L44) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImportSettingsSnapshot`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L12) | Provides a detached settings value and an optimistic concurrency token for its source sidecar. | — | — | +| [`public Inno.Core.Serialization.ISerializable? Inno.Assets.Pipeline.AssetImportSettingsSnapshot.value { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L25) | Gets the editable settings copy, or null for an importer without settings. | — | — | +| [`public string Inno.Assets.Pipeline.AssetImportSettingsSnapshot.fingerprint { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetImportSettingsSnapshot.cs#L30) | Gets the source metadata fingerprint required when saving this copy. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetLoader : System.IDisposable, Inno.Assets.IAssetReferenceResolver, Inno.Assets.IAssetArtifactLookup, Inno.Assets.IAssetPropertyStateResolver`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L27) | Coordinates importing, persistent cataloging, canonical loading, reloading and collection for one source and artifact root pair. | — | — | +| [`public Inno.Assets.Pipeline.AssetCatalogCandidate Inno.Assets.Pipeline.AssetLoader.PrepareCatalogCandidate(System.Collections.Generic.IReadOnlyList mounts, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L47) | Creates an isolated source-mount and catalog candidate without changing the active catalog.
`mounts`: The complete source-mount snapshot to validate.
`sourcePolicy`: The source policy for the candidate, or to reuse this loader's policy. | A candidate that owns its catalog staging storage and exposes the isolated loader to its owner. | `System.ArgumentException`: Thrown when the mount snapshot is empty, duplicated, or has no writable project source.
`System.IO.IOException`: Thrown when the active catalog cannot be copied into candidate storage. | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L114) | Tries to get a catalog snapshot by isolated source path.
`path`: The isolated source path.
`info`: The immutable catalog snapshot when found. | when the source is cataloged. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetInfo(System.Guid persistentId, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L140) | Tries to get a catalog snapshot by persistent identity.
`persistentId`: The stable persistent identity used for lookup.
`info`: The resolved immutable metadata returned to the caller. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetPersistentId(Inno.Assets.AssetPath path, out System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L163) | Tries to resolve a persistent identity without loading the asset.
`path`: The isolated source path.
`persistentId`: The resolved identity. | when catalog metadata exists. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetAssetType(Inno.Assets.AssetPath path, out System.Type? assetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L189) | Tries to resolve the concrete asset type without loading it.
`path`: The isolated source path.
`assetType`: The resolved type. | when the type can be resolved. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetLoadedPaths()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Catalog.cs#L209) | Gets isolated source paths of all canonical loaded assets. | A stable isolated path snapshot. | — | +| [`public void Inno.Assets.Pipeline.AssetLoader.RestoreProperties(System.Guid stableTypeId, byte[] propertyData, TValue target) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L101) | Restores serialized properties to the existing asset object.
`stableTypeId`: The stable type id consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`propertyData`: The property data consumed by restore properties; ownership remains with the caller unless explicitly stated otherwise.
`target`: The existing target that receives the validated result.
`TValue`: Serialized asset object type receiving restored properties. | — | — | +| [`public Inno.Assets.Pipeline.AssetLoader.AssetLoader(Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, string assetRoot, string libraryRoot, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L145) | Creates an asset loader for one source and Library root pair.
`types`: The immutable type-generation owner used to discover importers and resolve asset types.
`serialization`: The serialization registry used for catalog, metadata, and asset state persistence.
`identities`: The identity allocator that owns every canonical asset loaded by this instance.
`logs`: The explicitly owned router receiving importer and catalog diagnostics.
`diagnostics`: The diagnostic hub that owns import, build, catalog, and reference reports.
`assetRoot`: The absolute source root.
`libraryRoot`: The absolute rebuildable Library root.
`sourcePolicy`: The source filtering policy, or for defaults. | — | — | +| [`public Inno.Assets.Pipeline.AssetLoader.AssetLoader(Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Core.Identity.IdentityAllocator identities, Inno.Core.Diagnostics.DiagnosticHub diagnostics, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IReadOnlyList mounts, string libraryRoot, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool runtimeArtifactsOnly = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L197) | Creates an asset loader over one project source and zero or more isolated sources.
`types`: The immutable type-generation owner used to discover importers and resolve asset types.
`serialization`: The serialization registry used for catalog, metadata, and asset state persistence.
`identities`: The identity allocator that owns every canonical asset loaded by this instance.
`logs`: The explicitly owned router receiving importer and catalog diagnostics.
`diagnostics`: The diagnostic hub that owns import, build, catalog, and reference reports.
`mounts`: Complete isolated source mount snapshot.
`libraryRoot`: Absolute rebuildable Library root.
`sourcePolicy`: Source filtering policy, or for defaults.
`runtimeArtifactsOnly`: Whether to trust a deployed read-only catalog and skip every source reconciliation operation. | — | — | +| [`public string Inno.Assets.Pipeline.AssetLoader.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L293) | Gets the absolute source root. | — | — | +| [`public string Inno.Assets.Pipeline.AssetLoader.libraryRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L298) | Gets the absolute rebuildable Library root. | — | — | +| [`public string Inno.Assets.Pipeline.AssetLoader.artifactRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L303) | Gets the derived content-addressed artifact root. | — | — | +| [`public System.Action? Inno.Assets.Pipeline.AssetLoader.AssetReloaded`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L308) | Occurs after a loaded canonical asset is updated in place. | — | — | +| [`public void Inno.Assets.Pipeline.AssetLoader.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L336) | Waits for pending import and build work. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.RefreshRegistries()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.cs#L344) | Refreshes extension registries and reimports affected sources when their snapshot changed. | when the importer registry changed. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L41) | Gets direct or transitive runtime dependencies of an asset.
`asset`: The asset to query.
`recursive`: Whether transitive dependencies should be included. | The persistent dependency descriptors. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.GetImportDependencies(Inno.Assets.AssetObject asset, bool recursive = false)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L61) | Gets source import dependencies that invalidate an asset artifact.
`asset`: The asset to query.
`recursive`: Whether transitive source dependencies should be included. | Canonical isolated source paths in stable order. | — | +| [`public Inno.Assets.AssetReferenceInfo Inno.Assets.Pipeline.AssetLoader.GetReferenceInfo(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs#L86) | Gets an engine-known reference diagnostic snapshot.
`asset`: The asset to inspect. | The reference diagnostic snapshot. | — | +| [`public Inno.Assets.Pipeline.AssetImportHealthSnapshot Inno.Assets.Pipeline.AssetLoader.CaptureWritableImportHealth()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Diagnostics.cs#L35) | Captures the current failure identity of every writable source without exposing catalog internals. | An immutable health snapshot suitable for validating a later candidate generation. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetLoader.FindIntroducedImportFailures(Inno.Assets.Pipeline.AssetImportHealthSnapshot baseline)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Diagnostics.cs#L59) | Finds writable-source failures that are new or changed relative to an earlier health snapshot.
`baseline`: The health snapshot captured before the candidate generation was activated. | A deterministic path-ordered collection containing only introduced or changed failures. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.Import(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Importing.cs#L38) | Imports one isolated source file into metadata and a runtime artifact.
`path`: The isolated source path. | when an importer handled the source. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.BuildAsync(Inno.Assets.AssetObject definition, System.Collections.Generic.IReadOnlyList inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Importing.cs#L55) | Builds and validates the requested artifact asynchronously before publishing it.
`definition`: The build definition asset.
`inputs`: The immutable input catalog snapshots.
`cancellationToken`: Cancellation for the candidate build. | The content-addressed output bundle key. | — | +| [`public Inno.Assets.Pipeline.AssetImportSettingsSnapshot Inno.Assets.Pipeline.AssetLoader.GetImportSettings(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L31) | Reads a detached importer settings value without changing source metadata.
`path`: The isolated source path, not its sidecar path. | The current settings and the metadata fingerprint required for a subsequent save. | `System.NotSupportedException`: The source has no registered importer.
`System.IO.InvalidDataException`: The persisted settings cannot be restored by the current importer. | +| [`public bool Inno.Assets.Pipeline.AssetLoader.SaveImportSettings(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable? settings, string expectedFingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L68) | Atomically saves importer settings and reimports the source. Failed imports retain both the saved settings and the previous successful artifact; callers must inspect the returned import status separately from saving.
`path`: The writable isolated source path.
`settings`: A value of the current importer's settings type, or null to reset to defaults.
`expectedFingerprint`: The fingerprint returned when the settings were read. | True when reimport succeeds; false when settings were saved but reimport failed. | `System.IO.IOException`: The sidecar changed externally or could not be written.
`System.InvalidOperationException`: The source is read-only or belongs to an isolated candidate.
`System.ArgumentException`: The settings have a different type from the current importer. | +| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(Inno.Assets.AssetPath path, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L41) | Loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type. | The canonical asset, or when unavailable or incompatible. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(Inno.Assets.AssetPath path, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L64) | Tries to load a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | +| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(System.Guid persistentId, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L85) | Loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type. | The canonical asset, or when unavailable or incompatible. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(System.Guid persistentId, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L108) | Tries to load a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(Inno.Assets.AssetPath path, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L132) | Asynchronously loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(System.Guid persistentId, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L172) | Asynchronously loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | +| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetLoader.ResolveReference(System.Guid persistentId, System.Guid stableTypeId, string lastKnownPath, System.Type expectedType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L214) | Resolves a serialized reference or creates a persistent missing placeholder.
`persistentId`: The referenced persistent identity.
`stableTypeId`: The referenced stable asset type identity.
`lastKnownPath`: The last known source-relative path.
`expectedType`: The declared destination type. | A compatible canonical asset or missing placeholder. | — | +| [`public int Inno.Assets.Pipeline.AssetLoader.CollectArtifacts(System.TimeSpan gracePeriod, long maximumSizeBytes)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L41) | Collects unreachable content-addressed artifacts.
`gracePeriod`: The minimum age of an unreachable bundle.
`maximumSizeBytes`: The cache size limit, or zero for no limit. | The number of removed artifact bundles. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L74) | Tries to resolve a named output from the current artifact bundle.
`persistentId`: The stable persistent identity used for lookup.
`outputName`: The stable artifact output name used for lookup.
`artifact`: The resolved immutable artifact payload returned to the caller. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetLoader.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L109) | Acquires an immutable output while excluding concurrent artifact collection.
`persistentId`: The source asset's persistent identity.
`outputName`: The required output protocol. | A lease retaining this exact artifact key, even after reimport or source deletion. | `System.InvalidOperationException`: No current output can be resolved. | +| [`public int Inno.Assets.Pipeline.AssetLoader.UnloadUnusedAssets()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L126) | Collects canonical assets that have no external managed references. | The number of released canonical assets. | — | +| [`public void Inno.Assets.Pipeline.AssetLoader.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Retirement.cs#L38) | Stops new loads and releases canonical assets before their registries and diagnostics. | — | `Inno.Core.Execution.RetirementPendingException`: A load, serialized operation or unload hook is still active. Retain the loader and retry after quiescence.
`System.AggregateException`: Completed releases failed; all quiescent resources have still been attempted. | +| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetLoader.ExportRuntimeArtifacts(string destinationLibraryRoot)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.RuntimeExport.cs#L45) | Writes a source-free runtime catalog and its exact immutable artifact closure.
`destinationLibraryRoot`: Empty destination that becomes the deployed content root. | Counts and source identities for the exported runtime snapshot. | `System.InvalidOperationException`: Thrown when runtime artifacts are incomplete, a deployed reference is authoring-only, or a transitive authoring input has failed or become stale.
`System.IO.IOException`: Thrown when the destination is not empty or content cannot be copied. | +| [`public Inno.Assets.AssetRuntimeContentInfo Inno.Assets.Pipeline.AssetLoader.ExportRuntimeArtifacts(string destinationLibraryRoot, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.RuntimeExport.cs#L66) | Exports the validated runtime artifact closure while observing cooperative cancellation between files.
`destinationLibraryRoot`: The empty directory that receives the runtime-only asset database.
`cancellationToken`: The token checked before every artifact file is copied. | Counts and source identities represented by the exported snapshot. | `System.InvalidOperationException`: Thrown when artifacts are incomplete or transitive source inputs no longer certify the imported snapshot.
`System.OperationCanceledException`: Thrown when cancellation is requested before the snapshot finishes. | +| [`public bool Inno.Assets.Pipeline.AssetLoader.Save(Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Saving.cs#L38) | Saves an asset back to its current source path.
`asset`: The asset to export. | when an importer exported the asset. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.Save(Inno.Assets.AssetPath path, Inno.Assets.AssetObject asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Saving.cs#L58) | Saves an asset to its initial or existing isolated source path while preserving an existing destination identity.
`path`: The isolated source path.
`asset`: The asset to export. Detached replacement values do not replace an existing source's persistent identity. | when an importer exported the asset. | — | +| [`public void Inno.Assets.Pipeline.AssetLoader.Rescan(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceChanges.cs#L38) | Reconciles source files, metadata, artifacts and the in-memory catalog.
`cancellationToken`: Cancellation for reconciliation and importer work; unpublished candidates can be discarded after it drains. | — | `System.OperationCanceledException`: Reconciliation or an importer observes cancellation. | +| [`public void Inno.Assets.Pipeline.AssetLoader.ApplySourceChanges(System.Collections.Generic.IReadOnlyList changes)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceChanges.cs#L60) | Applies normalized source file changes to the persistent catalog.
`changes`: The normalized source changes. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSerializationServices`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L12) | Provides generation-bound structured serialization without exposing host registries to importer extensions. | — | — | +| [`public System.Guid Inno.Assets.Pipeline.AssetSerializationServices.GetStableTypeId()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L55) | Resolves the stable persistent type identity of a registered serializable type.
`TValue`: The registered type whose stable identity is required. | The stable type identifier owned by the active extension generation. | `System.InvalidOperationException`: Thrown when has no stable type registration. | +| [`public Inno.Assets.AssetPropertySnapshot Inno.Assets.Pipeline.AssetSerializationServices.CaptureProperties(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L69) | Captures typed properties and their references as one neutral, owner-independent value.
`value`: Current generation settings object, never retained.
`TValue`: Current serializable settings contract. | Frozen native properties with exact stable type identity and collected direct dependencies. | — | +| [`public byte[] Inno.Assets.Pipeline.AssetSerializationServices.Serialize(TValue value) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L96) | Serializes one structured value and declares every encountered asset reference as a runtime dependency.
`value`: The value to serialize through the active converter generation.
`TValue`: The serializable value contract. | Deterministic structured bytes owned by the caller. | `System.ArgumentNullException`: Thrown when is . | +| [`public TValue Inno.Assets.Pipeline.AssetSerializationServices.Deserialize(System.ReadOnlySpan bytes) where TValue : class, Inno.Core.Serialization.ISerializable`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetSerializationServices.cs#L126) | Deserializes one structured value against the active importer candidate generation.
`bytes`: Complete structured bytes produced by the common serialization system.
`TValue`: The serializable value contract. | A restored value owned by the caller. | `System.InvalidOperationException`: Thrown when the value contains an asset reference but no candidate resolver is available. | +| [`public sealed class Inno.Assets.Pipeline.AssetBuildContext where TDefinition : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L14) | Provides a stable input snapshot to an aggregate asset build.
`TDefinition`: The build definition asset type. | — | — | +| [`public Inno.Assets.Pipeline.AssetBuildContext.AssetBuildContext(TDefinition definition, System.Collections.Generic.IReadOnlyList inputs)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L25) | Creates a build context.
`definition`: The build definition.
`inputs`: The immutable input catalog snapshots. | — | — | +| [`public TDefinition Inno.Assets.Pipeline.AssetBuildContext.definition { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L36) | Gets the build definition. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetBuildContext.inputs { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildContext.cs#L41) | Gets the input catalog snapshots. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetBuildProcessorAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L14) | Declares the immutable cache protocol identity of an automatically discovered asset build processor. | — | — | +| [`public Inno.Assets.Pipeline.AssetBuildProcessorAttribute.AssetBuildProcessorAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L22) | Creates build-processor discovery metadata.
`id`: Globally stable build processor identifier. | — | — | +| [`public string Inno.Assets.Pipeline.AssetBuildProcessorAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L31) | Gets the globally stable build processor identifier. | — | — | +| [`public abstract class Inno.Assets.Pipeline.AssetBuildProcessor`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L37) | Defines an automatically discovered aggregate asset build processor. | — | — | +| [`public string Inno.Assets.Pipeline.AssetBuildProcessor.processorId { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L44) | Gets the stable processor identifier used by build cache keys. | — | — | +| [`public abstract System.Type Inno.Assets.Pipeline.AssetBuildProcessor.definitionType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L51) | Gets the definition type accepted by this processor. | — | — | +| [`protected Inno.Assets.Pipeline.AssetBuildProcessor.AssetBuildProcessor()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L37) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Assets.Pipeline.AssetBuildProcessor where TDefinition : Inno.Assets.AssetObject : Inno.Assets.Pipeline.AssetBuildProcessor`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L78) | Provides a strongly typed aggregate asset build processor.
`TDefinition`: The definition asset type. | — | — | +| [`public override sealed System.Type Inno.Assets.Pipeline.AssetBuildProcessor.definitionType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L84) | Gets the concrete type handled by this extension implementation. | — | — | +| [`protected abstract System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetBuildProcessor.BuildAsync(Inno.Assets.Pipeline.AssetBuildContext context, Inno.Assets.Pipeline.AssetArtifactWriter output, System.Threading.CancellationToken cancellationToken)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L101) | Builds immutable outputs from a consistent asset snapshot.
`context`: The build context.
`output`: The candidate output writer.
`cancellationToken`: Cancellation for the build. | An operation that completes when all outputs are staged. | — | +| [`protected Inno.Assets.Pipeline.AssetBuildProcessor.AssetBuildProcessor()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Building/AssetBuildProcessor.cs#L78) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetImportWriter where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L16) | Collects the complete candidate output of one source import.
`TAsset`: The imported asset type. | — | — | +| [`public TAsset? Inno.Assets.Pipeline.AssetImportWriter.asset { get; private set; }`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L33) | Gets the candidate asset assigned by the importer. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.SetDeploymentScope(Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L43) | Selects the deployment scope for this imported asset. This permits one importer to produce runtime assets and editor-only assets from different source documents without changing their asset type.
`deploymentScope`: The scope of the complete imported asset, including its generated state. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.SetAsset(TAsset asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L58) | Assigns the managed asset produced by the importer.
`asset`: The imported asset. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetImportWriter.WriteArtifactAsync(string outputName, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope = Inno.Assets.Pipeline.AssetDeploymentScope.Runtime)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L84) | Writes one immutable named artifact output.
`outputName`: The stable output name.
`bytes`: The output content.
`cancellationToken`: Cancellation for the write operation.
`deploymentScope`: Whether this output is retained in runtime packages. Runtime payload and asset state cannot be authoring-only. | A completed operation after the output has been staged. | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnAsset(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L108) | Declares a runtime dependency by isolated source path.
`path`: The normalized file-system path used by this operation. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnAsset(Inno.Assets.AssetDependency dependency)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L116) | Declares a runtime dependency by persistent descriptor.
`dependency`: The dependency consumed by depends on asset; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnSource(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L124) | Declares a source input that invalidates this import.
`path`: The normalized file-system path used by this operation. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnArtifact(System.Guid persistentId)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L132) | Declares an asset artifact input that invalidates this import.
`persistentId`: The stable persistent identity used for lookup. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.DependsOnCustomInput(string key, string fingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L143) | Declares a custom deterministic import input.
`key`: The input identifier.
`fingerprint`: The deterministic input fingerprint. | — | — | +| [`public void Inno.Assets.Pipeline.AssetImportWriter.ReportDiagnostic(string message)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Importing/AssetImportWriter.cs#L154) | Adds a non-fatal import diagnostic.
`message`: The diagnostic message. | — | — | | [`public static class Inno.Assets.Pipeline.NativeAssetSourceSerialization`](../../src/content/assets/Inno.Assets.Pipeline/Serialization/NativeAssetSourceSerialization.cs#L13) | Imports and exports editable asset source state through the common native serializer. | — | — | +| [`public static byte[] Inno.Assets.Pipeline.NativeAssetSourceSerialization.Export(TAsset asset, Inno.Assets.Pipeline.AssetSerializationServices services) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Serialization/NativeAssetSourceSerialization.cs#L30) | Serializes one asset's editable properties and direct asset dependencies.
`asset`: Unsaved or loaded asset whose editable state should be captured.
`services`: The generation-bound serialization services supplied by the export context.
`TAsset`: Concrete asset source type. | Deterministic native source bytes. | — | +| [`public static TAsset Inno.Assets.Pipeline.NativeAssetSourceSerialization.Import(System.ReadOnlySpan bytes, Inno.Assets.Pipeline.AssetSerializationServices services, out System.Collections.Generic.IReadOnlyList dependencies) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Serialization/NativeAssetSourceSerialization.cs#L73) | Restores one concrete asset and its declared direct dependencies.
`bytes`: Native source bytes.
`dependencies`: Receives persistent dependencies discovered during export.
`services`: The generation-bound serialization and reference services supplied by the import context.
`TAsset`: Concrete asset source type. | A detached asset ready for an import transaction. | `System.InvalidOperationException`: Thrown when the source type identity is incompatible. | +| [`public readonly struct Inno.Assets.Pipeline.AssetChangedEvent`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L17) | Batched file-system change event for asset source files.
`relativePath`: Changed path relative to watched root.
`changeType`: File-system change type.
`oldRelativePath`: Old path for rename operations. | — | — | +| [`public Inno.Assets.Pipeline.AssetChangedEvent.AssetChangedEvent(string relativePath, System.IO.WatcherChangeTypes changeType, string oldRelativePath = "")`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L17) | Batched file-system change event for asset source files.
`relativePath`: Changed path relative to watched root.
`changeType`: File-system change type.
`oldRelativePath`: Old path for rename operations. | — | — | +| [`public string Inno.Assets.Pipeline.AssetChangedEvent.relativePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L25) | Changed path relative to watched root. | — | — | +| [`public System.IO.WatcherChangeTypes Inno.Assets.Pipeline.AssetChangedEvent.changeType { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L30) | Underlying file-system change type. | — | — | +| [`public string Inno.Assets.Pipeline.AssetChangedEvent.oldRelativePath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetChangedEvent.cs#L36) | Old path relative to watched root for rename operations. Empty for non-rename changes. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetFileEntry : Inno.Core.Identity.IdentityObject`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L11) | One node in the asset source filesystem index. | — | — | +| [`public string Inno.Assets.Pipeline.AssetFileEntry.name { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L16) | Gets the final source path segment, or the semantic mount-root label when this entry is a source root. | — | — | +| [`public string Inno.Assets.Pipeline.AssetFileEntry.nameWithoutExtension { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L25) | Gets the final source path segment without its last extension. | — | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetFileEntry.assetPath { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L30) | Gets the isolated source path. | — | — | +| [`public Inno.Assets.AssetSourceId Inno.Assets.Pipeline.AssetFileEntry.source { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L35) | Gets the owning source mount identity. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isReadOnly { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L40) | Gets whether source mutations are forbidden. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isSample { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L45) | Gets whether this entry is the root of an installed Plugin sample awaiting Project import. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isSampleContent { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L50) | Gets whether this entry belongs to an installed Plugin sample subtree awaiting Project import. | — | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetFileEntry.parentAssetPath { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L55) | Gets the isolated parent directory path. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileEntry.isDirectory { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L60) | Gets whether the entry represents a directory. | — | — | +| [`public string Inno.Assets.Pipeline.AssetFileEntry.extension { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L64) | Gets the normalized lower-case file extension. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileEntry.AssetFileEntry()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetFileSystem : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L18) | Indexed asset source filesystem model backed by . | — | — | +| [`public string Inno.Assets.Pipeline.AssetFileSystem.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L37) | Gets the absolute source asset root. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.isWatching { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L42) | Gets whether source file watching is active. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(string assetRoot, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L59) | Creates an indexed source file system.
`assetRoot`: The absolute source root.
`autoStart`: Whether file watching should start immediately.
`flushDelayMs`: The watcher batch delay in milliseconds.
`sourcePolicy`: The source filtering policy, or for defaults. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(System.Collections.Generic.IReadOnlyList mounts, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool requireWritableProject = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L96) | Creates an indexed file system over isolated source mounts.
`mounts`: Complete source mount snapshot.
`autoStart`: Whether writable project source watching starts immediately.
`flushDelayMs`: Watcher batch delay in milliseconds.
`sourcePolicy`: Source filtering policy, or for defaults.
`requireWritableProject`: Whether the project mount must support authoring mutations. Runtime catalogs may set this to when every mounted identity directory is read-only. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Start()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L168) | Starts source file watching. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Stop()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L177) | Stops source file watching. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Refresh()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L188) | Rebuilds the indexed source file snapshot. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.Exists(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L210) | Determines whether an indexed source entry exists.
`path`: The isolated source path. | when the entry exists. | — | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.TryGetEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L232) | Tries to resolve an indexed source entry.
`path`: The isolated source path.
`entry`: The resolved entry when available. | when the entry exists. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L261) | Gets a stable snapshot of indexed entries.
`includeDirectories`: Whether directory entries should be included. | The indexed entries. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L285) | Gets immediate children of an indexed directory.
`parent`: The isolated parent directory path. | The immediate child entries. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L308) | Polls normalized changes and refreshes the indexed source snapshot. | The changes observed since the previous poll. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L319) | Polls changes and reports whether watcher recovery requires a full rescan.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The changes observed since the previous poll. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L335) | Waits for a quiet watcher window, refreshes the index, and returns queued changes. | An immutable snapshot of the values selected by the operation. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L346) | Waits for queued changes and reports whether a full rescan is required.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The normalized queued changes. | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L366) | Releases the resources owned by this implementation. | — | — | +| [`public static class Inno.Assets.Pipeline.AssetSample`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L11) | Defines authoring sample directories and their installed Plugin behavior. | — | — | +| [`public const string Inno.Assets.Pipeline.AssetSample.fileType = ".isample"`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L16) | Gets the logical File Browser type used for a sample directory. | — | — | +| [`public static bool Inno.Assets.Pipeline.AssetSample.HasSampleDirectoryName(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L28) | Determines whether the final path segment uses the sample-directory naming convention.
`path`: The isolated source path to inspect. | when the final segment starts with ~, regardless of whether the path belongs to the writable Project source or an installed Plugin source. | — | +| [`public static bool Inno.Assets.Pipeline.AssetSample.IsRoot(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L46) | Determines whether the final segment identifies a sample directory.
`path`: The isolated source path to inspect. | when the path belongs to an installed Plugin source and its final segment starts with ~. A Project directory with the same name remains normal writable authoring content. | — | +| [`public static bool Inno.Assets.Pipeline.AssetSample.Contains(Inno.Assets.AssetPath path, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L64) | Determines whether a source path is a sample directory or is contained by one.
`path`: The isolated source path to inspect.
`isDirectory`: Whether the path itself represents a directory. | when the path belongs to an installed Plugin source and any directory segment starts with ~. Project content is never hidden from authoring import by this method. | — | +| [`public static string Inno.Assets.Pipeline.AssetSample.GetImportName(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L84) | Gets the original sample directory name for a writable Project copy.
`path`: A sample directory path whose final segment starts with ~. | The unchanged final directory segment, including every leading ~. | `System.ArgumentException`: Thrown when does not identify a sample directory or has no usable name after its prefix. | +| [`public static bool Inno.Assets.Pipeline.AssetSample.IsRuntimeExcluded(Inno.Assets.AssetPath path, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L108) | Determines whether a path belongs to an authoring-only sample subtree that must not enter a Player runtime closure.
`path`: The isolated source path to inspect.
`isDirectory`: Whether the path itself represents a directory. | when any directory segment starts with ~, including in the writable Project source. | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSampleImportTransaction : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L22) | Owns a cancellable sample clone, its pinned generation and its owner-thread publication. | — | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleImportTransaction.target { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L89) | Gets the destination path with the original sample directory name preserved. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.isFaulted { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L94) | Gets whether failed publication or retirement has faulted the host and requires a restart. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.isValidationComplete { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L99) | Gets whether background validation has drained and Commit can inspect its outcome. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSampleImportTransaction.Advance()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L113) | Starts background indexing after cloning, then adopts its completed catalog without waiting. | True when the candidate is indexed and ready for BeginValidation; false while preparation is pending. | `System.InvalidOperationException`: The transaction is finished or the caller is not its initialization thread.
`System.OperationCanceledException`: Preparation or indexing observed cancellation before a candidate could be adopted. | +| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.BeginValidation(System.Func validate)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L167) | Starts one authoring preflight after indexing, retaining its dependencies until it drains.
`validate`: Owner-thread capture followed by asynchronous validation. Continuations must not mutate live Assets. | — | `System.ArgumentNullException`: The validation callback is null.
`System.InvalidOperationException`: The candidate is not indexed, validation already started, or the transaction is finished. | +| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Commit(System.Action? beforePublish = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L203) | Publishes a successfully validated import exactly once, without waiting for unfinished work.
`beforePublish`: Optional owner-thread finalization, such as recording neutral History data. Failure leaves the transaction open for Rollback and no Changed notification is published. | — | `System.InvalidOperationException`: Validation is unfinished, the transaction is finished, or the caller is not its owner thread.
`System.OperationCanceledException`: The import or its validation was canceled before publication. | +| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Cancel()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L222) | Requests cancellation of copying and validation while retaining all dependencies. | — | — | +| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Rollback()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L230) | Cancels work and removes an uncommitted copy after all background consumers have drained. | — | `Inno.Core.Execution.RetirementPendingException`: Background work is still draining; retain the transaction and retry on its owner thread. | +| [`public void Inno.Assets.Pipeline.AssetSampleImportTransaction.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleImportTransaction.cs#L271) | Retires an uncommitted import on its owner thread after cancellation has drained. | — | `Inno.Core.Execution.RetirementPendingException`: Canceled work has not drained; disposal must be retried without releasing its owner. | +| [`public sealed class Inno.Assets.Pipeline.AssetSampleTransformContext`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L12) | Gives a source-language extension access to a staged sample clone before serialized references are remapped. | — | — | +| [`public string Inno.Assets.Pipeline.AssetSampleTransformContext.stagedRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L36) | Gets the absolute private staging directory; files here are committed only after all transforms succeed. | — | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleTransformContext.source { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L41) | Gets the installed sample source path. | — | — | +| [`public Inno.Assets.AssetPath Inno.Assets.Pipeline.AssetSampleTransformContext.target { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L46) | Gets the writable project destination path. | — | — | +| [`public System.Threading.CancellationToken Inno.Assets.Pipeline.AssetSampleTransformContext.cancellationToken { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L51) | Gets cancellation for this private, generation-pinned transformation. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Assets.Pipeline.AssetSampleTransformContext.identityMap { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L56) | Gets cloned source identities, including directory and file metadata. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSampleTransformContext.TryGetSourceIdentity(string relativePath, out System.Guid oldId, out System.Guid newId)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L73) | Resolves the source and clone identity of one file relative to the sample directory.
`relativePath`: Slash-separated source-local file path below the sample directory.
`oldId`: Receives the installed source identity.
`newId`: Receives the writable clone identity. | Whether both identities were recorded from sample metadata. | — | +| [`public void Inno.Assets.Pipeline.AssetSampleTransformContext.MapType(System.Guid oldId, System.Guid newId)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L102) | Registers one source-language type replacement for subsequent structured asset rewriting.
`oldId`: The type identity stored in installed sample assets.
`newId`: The type identity declared by the cloned source. | — | `System.IO.InvalidDataException`: A source identity is reused inconsistently. | +| [`public interface Inno.Assets.Pipeline.IAssetSampleSourceRewriter`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L117) | Rewrites a source language in a private sample clone before source assets are published. | — | — | +| [`void Inno.Assets.Pipeline.IAssetSampleSourceRewriter.Transform(Inno.Assets.Pipeline.AssetSampleTransformContext context)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L129) | Rewrites staged source files and registers any changed serialized type identities.
`context`: The transaction-owned staged clone. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute : System.Attribute`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L136) | Registers a source-language sample transformer through the reloadable type catalog. | — | — | +| [`public Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute.AssetSampleSourceRewriterAttribute(string id)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L144) | Creates one ordered source-language transformer declaration.
`id`: Stable extension identity used for diagnostics and ordering. | — | — | +| [`public string Inno.Assets.Pipeline.AssetSampleSourceRewriterAttribute.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSampleSourceRewriter.cs#L153) | Gets the stable extension identity. | — | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSourceMount`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L14) | Maps one isolated asset source to a controlled physical root. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourceMount.AssetSourceMount(Inno.Assets.AssetSourceId id, string rootPath, bool isReadOnly, System.Collections.Generic.IEnumerable? dependencies = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L31) | Creates an asset source mount.
`id`: Stable source identity.
`rootPath`: Absolute or resolvable physical source root.
`isReadOnly`: Whether all source mutations must be rejected.
`dependencies`: Other read-only sources this source may reference. | — | — | +| [`public Inno.Assets.AssetSourceId Inno.Assets.Pipeline.AssetSourceMount.id { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L51) | Gets the stable source identity. | — | — | +| [`public string Inno.Assets.Pipeline.AssetSourceMount.rootPath { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L56) | Gets the controlled physical source root. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSourceMount.isReadOnly { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L61) | Gets whether source mutations are forbidden. | — | — | +| [`public System.Collections.Generic.IReadOnlySet Inno.Assets.Pipeline.AssetSourceMount.dependencySourceIds { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L66) | Gets explicitly declared cross-source dependencies. | — | — | +| [`public string Inno.Assets.Pipeline.AssetSourceMount.Resolve(string localPath)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSourceMount.cs#L77) | Resolves a source-local path and rejects physical root escape.
`localPath`: Source-local path. | An absolute path contained by this mount. | — | +| [`public sealed class Inno.Assets.Pipeline.AssetSourcePolicy`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L10) | Defines which physical entries are excluded from an asset source tree. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourcePolicy.AssetSourcePolicy()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L47) | Creates a source policy using the engine's default noise filters. | — | — | +| [`public Inno.Assets.Pipeline.AssetSourcePolicy.AssetSourcePolicy(System.Collections.Generic.IEnumerable? ignoredFileNames, System.Collections.Generic.IEnumerable? ignoredDirectoryNames, System.Collections.Generic.IEnumerable? ignoredPrefixes, System.Collections.Generic.IEnumerable? ignoredSuffixes)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L67) | Creates a source policy with additional ignored names and affixes.
`ignoredFileNames`: Additional case-insensitive file names excluded from every source mount.
`ignoredDirectoryNames`: Additional case-insensitive directory names excluded with their descendants.
`ignoredPrefixes`: Additional file-name prefixes treated as transient authoring noise.
`ignoredSuffixes`: Additional file-name suffixes treated as transient authoring noise. | — | — | +| [`public static Inno.Assets.Pipeline.AssetSourcePolicy Inno.Assets.Pipeline.AssetSourcePolicy.defaultPolicy { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L84) | Gets the default source policy. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetSourcePolicy.IsIgnored(string relativePath, bool isDirectory)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L98) | Determines whether an entry is excluded from the source database.
`relativePath`: The normalized path relative to its owning source mount.
`isDirectory`: Whether the source entry represents a directory. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`public static bool Inno.Assets.Pipeline.AssetSourcePolicy.IsGeneratedPath(string relativePath)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/Filtering/AssetSourcePolicy.cs#L131) | Determines whether a path is generated asset metadata.
`relativePath`: The normalized path relative to its owning source mount. | when the operation succeeds or its condition is satisfied; otherwise, . | — | +| [`public interface Inno.Assets.Pipeline.IAssetSourceSnapshot : Inno.Assets.IAssetArtifactLookup`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L15) | Provides read-only authoring source access for active or isolated candidate compilation. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.IAssetSourceSnapshot.sourceMounts { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L20) | Gets the source mounts represented by this view. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.IAssetSourceSnapshot.GetFileSystemEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L31) | Captures the indexed entries represented by this view.
`includeDirectories`: Whether directory entries are included. | A stable entry collection for owner-thread input discovery. | — | +| [`TAsset Inno.Assets.Pipeline.IAssetSourceSnapshot.Load(Inno.Assets.AssetPath path) where TAsset : Inno.Assets.AssetObject`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L48) | Loads an authoring asset without publishing a candidate catalog.
`path`: The isolated source path.
`TAsset`: The required authoring asset type. | The resolved authoring asset. | `System.InvalidOperationException`: No compatible asset can be loaded from this view. | +| [`bool Inno.Assets.Pipeline.IAssetSourceSnapshot.TryGetInfo(Inno.Assets.AssetPath path, out Inno.Assets.AssetInfo? info)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/IAssetSourceSnapshot.cs#L62) | Resolves immutable catalog information by source path.
`path`: The isolated source path.
`info`: The catalog information, or null when the source is absent. | True when catalog information exists. | — | ## 项目依赖 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Logging](../core/Inno.Core.Logging.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Collections](../core/Inno.Core.Collections.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):公开引用边界由实际签名核对。 -- [Inno.References](../references/Inno.References.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 - -## Artifact 与部署摘要补充契约 - - - - - - -| 声明 | 行为与结果 | 所有权 / 线程 | 失败或限制 | -| --- | --- | --- | --- | -| [`public sealed class Inno.Assets.Pipeline.AssetArtifactWriter`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | 建立本次 aggregate build 的输出/诊断收集器。 | 内部结果由 Pipeline 使用;公开 Writer 不提供物理路径。 | 输出不能重复写入。 | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetArtifactWriter.WriteAsync(string outputName, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.AssetDeploymentScope deploymentScope = Inno.Assets.Pipeline.AssetDeploymentScope.Runtime)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L36) | 复制 bytes,暂存唯一的命名输出;返回已完成的 ValueTask。AuthoringOnly 输出不进入运行闭包。 | Writer 属于当前 aggregate build;调用后可释放/修改传入缓冲区。 | 空名称、非法 scope、将 runtime/asset-state 标为 AuthoringOnly、重复名称或预取消均失败。 | -| [`public void Inno.Assets.Pipeline.AssetArtifactWriter.ReportDiagnostic(string message)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L60) | 向本次构建加入诊断文本。 | 不开始导入、发布或文件 IO。 | 空白诊断会抛 ArgumentException。 | -| [`public Inno.Assets.Pipeline.AssetArtifactWriter.AssetArtifactWriter()`](../../src/content/assets/Inno.Assets.Pipeline/Importing/Artifacts/AssetArtifactWriter.cs#L11) | 建立本次 aggregate build 的输出/诊断收集器。 | 内部结果由 Pipeline 使用;公开 Writer 不提供物理路径。 | 输出不能重复写入。 | +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.Core.Diagnostics` | 实现依赖;不传递 compile surface | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Core.Collections` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Core.Identity` | 公开签名依赖 | +| `Inno.References` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs b/src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs index f8bdc6c06..60a9ceca2 100644 --- a/src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs +++ b/src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs @@ -31,6 +31,13 @@ public sealed class AssetFileSystem : IDisposable private bool m_identitiesActive; private bool m_disposed; + private readonly record struct SourceEntry( + AssetPath path, + Guid persistentId, + bool isReadOnly, + bool isDirectory + ); + /// /// Gets the absolute source asset root. /// @@ -183,18 +190,68 @@ public void Stop() } /// - /// Rebuilds the indexed source file snapshot. + /// Reconciles a complete source snapshot while preserving unchanged entry identities. /// + /// + /// The source filesystem has been disposed. + /// + /// + /// Source enumeration fails; the previous complete index and its identities remain active. + /// + /// + /// A source directory cannot be read; the previous complete index remains active. + /// + /// + /// Multiple source paths claim the same persistent entry identity. + /// + /// + /// A new entry identity cannot be registered; the previous index remains active. + /// public void Refresh() { ObjectDisposedException.ThrowIf(m_disposed, this); lock (m_sync) { - UnregisterEntries(); + List candidate = CollectEntries(); + AssetFileEntry[] previous = m_entries.All().ToArray(); + Dictionary existing = previous.ToDictionary(static entry => entry.identity.persistentId); + var prepared = new List<(SourceEntry source, AssetFileEntry entry)>(candidate.Count); + var registered = new List(); + try + { + foreach (SourceEntry source in candidate) + { + if (!existing.TryGetValue(source.persistentId, out AssetFileEntry? entry)) + { + entry = new AssetFileEntry(); + if (m_identitiesActive) + { + if (!m_identities.Register(entry, source.persistentId)) + throw new InvalidOperationException($"Asset source entry '{source.path}' could not acquire its identity."); + registered.Add(entry); + } + else + m_identities.InitializePersistentIdentity(entry, source.persistentId); + } + prepared.Add((source, entry)); + } + } + catch + { + foreach (AssetFileEntry entry in registered) + _ = m_identities.Unregister(entry); + throw; + } m_entries.RemoveAll(); - foreach (AssetSourceMount mount in m_mounts.Values.OrderBy(static value => value.id.value, StringComparer.Ordinal)) - IndexDirectoryRecursive(mount, mount.rootPath, string.Empty); + foreach ((SourceEntry source, AssetFileEntry entry) in prepared) + { + IndexEntry(entry, source); + existing.Remove(source.persistentId); + } + if (m_identitiesActive) + foreach (AssetFileEntry removed in existing.Values) + _ = m_identities.Unregister(removed); } } @@ -435,54 +492,56 @@ private void RefreshSafely() } } - private void IndexDirectoryRecursive( - AssetSourceMount mount, - string absoluteDirectoryPath, - string localDirectoryPath - ) { - string normalizedDirectory = NormalizeLocalPath(localDirectoryPath); - AddOrUpdateEntry(new AssetPath(mount.id, normalizedDirectory), mount.isReadOnly, isDirectory: true); - - foreach (string absoluteChildDirectory in Directory.EnumerateDirectories(absoluteDirectoryPath)) + private List CollectEntries() + { + var entries = new List(); + var identities = new HashSet(); + foreach (AssetSourceMount mount in m_mounts.Values.OrderBy(static value => value.id.value, StringComparer.Ordinal)) { - string name = Path.GetFileName(absoluteChildDirectory); - string childRelativePath = CombineLocalPath(normalizedDirectory, name); - if (m_sourcePolicy.IsIgnored(childRelativePath, isDirectory: true)) - continue; - IndexDirectoryRecursive(mount, absoluteChildDirectory, childRelativePath); + var pending = new Stack(); + pending.Push(string.Empty); + while (pending.TryPop(out string? directory)) + { + Add(new AssetPath(mount.id, directory), mount.isReadOnly, isDirectory: true); + string absoluteDirectory = mount.Resolve(directory); + foreach (string child in Directory.EnumerateDirectories(absoluteDirectory)) + { + string path = CombineLocalPath(directory, Path.GetFileName(child)); + if (!m_sourcePolicy.IsIgnored(path, isDirectory: true)) + pending.Push(path); + } + foreach (string child in Directory.EnumerateFiles(absoluteDirectory)) + { + string path = CombineLocalPath(directory, Path.GetFileName(child)); + if (!m_sourcePolicy.IsIgnored(path, isDirectory: false)) + Add(new AssetPath(mount.id, path), mount.isReadOnly, isDirectory: false); + } + } } - - foreach (string absoluteFile in Directory.EnumerateFiles(absoluteDirectoryPath)) - { - string name = Path.GetFileName(absoluteFile); - string fileRelativePath = CombineLocalPath(normalizedDirectory, name); - if (m_sourcePolicy.IsIgnored(fileRelativePath, isDirectory: false)) - continue; - AddOrUpdateEntry(new AssetPath(mount.id, fileRelativePath), mount.isReadOnly, isDirectory: false); + return entries; + + void Add( + AssetPath path, + bool isReadOnly, + bool isDirectory + ) { + Guid sourceId = m_persistentIdentityResolver?.Invoke(path) ?? CreateFallbackPersistentId(path.ToString()); + Guid entryId = CreateEntryPersistentId(sourceId); + if (!identities.Add(entryId)) + throw new InvalidDataException($"Asset source path '{path}' duplicates a persistent entry identity."); + entries.Add(new(path, entryId, isReadOnly, isDirectory)); } } - private void AddOrUpdateEntry( - AssetPath assetPath, - bool isReadOnly, - bool isDirectory + private void IndexEntry( + AssetFileEntry existing, + SourceEntry source ) { + AssetPath assetPath = source.path; + bool isDirectory = source.isDirectory; string path = assetPath.ToString(); - AssetFileEntry? existing = m_entries.First(m_pathKey, path); - if (existing is null) - { - existing = new AssetFileEntry(); - Guid sourcePersistentId = m_persistentIdentityResolver?.Invoke(assetPath) - ?? CreateFallbackPersistentId(path); - Guid entryPersistentId = CreateEntryPersistentId(sourcePersistentId); - if (m_identitiesActive) - _ = m_identities.Register(existing, entryPersistentId); - else - m_identities.InitializePersistentIdentity(existing, entryPersistentId); - } - existing.assetPath = assetPath; - existing.isReadOnly = isReadOnly; + existing.isReadOnly = source.isReadOnly; existing.isSample = isDirectory && AssetSample.IsRoot(assetPath); existing.isSampleContent = AssetSample.Contains(assetPath, isDirectory); existing.parentAssetPath = AssetPath.Parse(GetParentPath(path)); diff --git a/tests/assets/Inno.Assets.Pipeline.Tests/AssetFileSystemTests.cs b/tests/assets/Inno.Assets.Pipeline.Tests/AssetFileSystemTests.cs index 3f6b41df7..a33cf931f 100644 --- a/tests/assets/Inno.Assets.Pipeline.Tests/AssetFileSystemTests.cs +++ b/tests/assets/Inno.Assets.Pipeline.Tests/AssetFileSystemTests.cs @@ -13,6 +13,91 @@ namespace Inno.Assets.Pipeline.Tests; public sealed class AssetFileSystemTests { + [Fact] + public void RefreshPreservesUnchangedEntryInstancesAndRuntimeIdentities() + { + string root = CreateRoot(); + try + { + Directory.CreateDirectory(Path.Combine(root, "Folder")); + File.WriteAllText(Path.Combine(root, "Folder", "value.txt"), "value"); + using var fileSystem = new AssetFileSystem(root, autoStart: false); + AssetFileEntry[] previous = fileSystem.GetEntries().ToArray(); + var identities = previous.Select(static entry => entry.identity.runtimeIdentity).ToArray(); + fileSystem.Refresh(); + for (int index = 0; index < previous.Length; index++) + { + Assert.True(fileSystem.TryGetEntry(previous[index].assetPath, out AssetFileEntry current)); + Assert.Same(previous[index], current); + Assert.Equal(identities[index], current.identity.runtimeIdentity); + } + } + finally + { + DeleteRoot(root); + } + } + + [Fact] + public void FailedEnumerationKeepsThePreviousCompleteIndexAndItsIdentities() + { + string root = CreateRoot(); + string moved = root + ".temporarily-moved"; + try + { + File.WriteAllText(Path.Combine(root, "value.txt"), "value"); + using var fileSystem = new AssetFileSystem(root, autoStart: false); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("value.txt"), out AssetFileEntry previous)); + var identity = previous.identity.runtimeIdentity; + Directory.Move(root, moved); + try + { + Assert.Throws(() => fileSystem.Refresh()); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("value.txt"), out AssetFileEntry retained)); + Assert.Same(previous, retained); + Assert.Equal(identity, retained.identity.runtimeIdentity); + } + finally + { + Directory.Move(moved, root); + } + fileSystem.Refresh(); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("value.txt"), out AssetFileEntry restored)); + Assert.Same(previous, restored); + } + finally + { + DeleteRoot(root); + } + } + + [Fact] + public void RefreshRetiresRemovedEntriesAndPreservesUnaffectedEntries() + { + string root = CreateRoot(); + try + { + File.WriteAllText(Path.Combine(root, "keep.txt"), "keep"); + File.WriteAllText(Path.Combine(root, "remove.txt"), "remove"); + using var fileSystem = new AssetFileSystem(root, autoStart: false); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("keep.txt"), out AssetFileEntry keep)); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("remove.txt"), out AssetFileEntry removed)); + File.Delete(Path.Combine(root, "remove.txt")); + File.WriteAllText(Path.Combine(root, "added.txt"), "added"); + fileSystem.Refresh(); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("keep.txt"), out AssetFileEntry retained)); + Assert.Same(keep, retained); + Assert.False(fileSystem.TryGetEntry(AssetPath.Project("remove.txt"), out _)); + Assert.Null(removed.identity.runtimeIdentity); + Assert.True(fileSystem.TryGetEntry(AssetPath.Project("added.txt"), out AssetFileEntry added)); + Assert.NotNull(added.identity.runtimeIdentity); + } + finally + { + DeleteRoot(root); + } + } + [Fact] public void Queries_RejectRootedAndTraversalPaths() { From 63d4f7dce8b21daa22a6f7e5a00825be3f46dd2d Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 06:21:37 +0800 Subject: [PATCH 11/54] feat(editor): complete reusable presentation and SDK-owned compilation contracts --- AGENTS.md | 2 + .../BgfxImGuiRenderer.cs | 3 + .../ImGuiPresentationContext.cs | 8 +- .../Api/IPlatformImGuiRenderer.Api.cs | 5 + .../Api/PlatformImGuiContext.Api.cs | 22 +- .../Context/PlatformImGuiContext.cs | 68 +- .../Rendering/PlatformImGuiSdlRenderer.cs | 3 + build/cli/Inno.Build.Cli/BuildWorkspace.cs | 3 +- .../StandardBuildEnvironment.cs | 24 +- build/pipeline/Inno.Build/Inno.Build.csproj | 5 +- .../SdkScriptCompilationEnvironment.cs | 112 + .../Managed/DotNetSdkResolver.cs | 46 +- docs/README.md | 2 +- docs/architecture/CURRENT_ACCEPTANCE.md | 2 +- docs/architecture/CURRENT_ISSUES.md | 6 +- docs/architecture/EDITOR_UI_ARCHITECTURE.md | 5 +- .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 100 + docs/architecture/README.md | 2 + docs/assets/Inno.Assets.Pipeline.md | 32 +- .../Inno.Adapter.Presentation.ImGui.Bgfx.md | 82 +- .../Inno.Adapter.Presentation.ImGui.Sdl3.md | 3083 ++++++++--------- docs/build/Inno.Build.Cli.md | 58 +- .../build/Inno.Build.Distribution.Standard.md | 80 +- docs/build/Inno.Build.Toolchains.md | 1660 ++++----- docs/build/Inno.Build.md | 486 +-- docs/editor/Inno.Editor.Assets.ImGui.md | 60 +- docs/editor/Inno.Editor.Core.md | 6 +- docs/editor/Inno.Editor.Graph.ImGui.md | 136 +- docs/editor/Inno.Editor.ImGui.md | 722 ++-- docs/editor/Inno.Editor.Interactions.md | 100 +- docs/editor/Inno.Editor.Panel.Hierarchy.md | 2 + docs/editor/Inno.Editor.Panel.Inspector.md | 16 +- docs/editor/Inno.Editor.Panel.Logging.md | 4 + docs/editor/Inno.Editor.Panel.Settings.md | 2 + docs/editor/Inno.Editor.Panel.Stats.md | 4 +- docs/platform/Inno.Adapter.Presentation.md | 176 +- docs/scene/Inno.Scene.md | 960 +++-- docs/scripting/Inno.Scripting.Compiler.md | 361 +- docs/tooling/Inno.Tooling.Architecture.md | 43 +- .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 111 +- .../IPresentationContext.cs | 15 +- .../Runtime/EditorInteraction.cs | 10 +- .../Runtime/EditorInteractions.cs | 22 +- .../Selection/EditorSelectionState.cs | 15 +- .../Hosting/EditorAuthoringServices.cs | 7 +- .../Inno.Editor.Hosting/Hosting/EditorHost.cs | 2 +- .../Hosting/EditorLayer.cs | 6 +- .../Browser/FileBrowserData.cs | 78 +- .../Browser/FileBrowserPanel.Grid.cs | 82 +- .../Browser/FileBrowserPanel.Layout.cs | 49 +- .../Browser/FileBrowserPanel.List.cs | 60 +- .../Browser/FileBrowserPanel.Navigation.cs | 111 +- .../Browser/FileBrowserPanel.cs | 26 +- .../Browser/FileBrowserRename.cs | 1 + .../Browser/FileBrowserTree.Navigation.cs | 137 + .../Browser/FileBrowserTree.Rows.cs | 116 + .../Browser/FileBrowserTree.cs | 148 +- .../Browser/FileBrowserUtility.cs | 24 +- .../Presentation/HierarchyPanel.Rows.cs | 118 + .../Presentation/HierarchyPanel.cs | 253 +- .../Styling/HierarchyStyle.cs | 36 + .../DragDrop/AssetReferenceDropTarget.cs | 9 +- .../EngineObjectReferenceDropTarget.cs | 9 +- .../Layers/GameObjectLayerSelector.cs | 2 +- .../Presentation/InspectorCardControls.cs | 10 +- .../Presentation/InspectorTargetHeader.cs | 4 +- .../Presentation/InspectorTypeOrigin.cs | 2 +- .../Styling/InspectorTargetStyle.cs | 17 + .../Targets/GameObjectInspectionDrawer.cs | 10 +- .../Targets/GameObjectTagSelector.cs | 2 +- .../Targets/GameSceneInspectionDrawer.cs | 4 +- .../Targets/SelectionInspectionDrawer.cs | 2 +- .../Console/ConsoleEntryCopyTarget.cs | 10 +- .../Console/ConsolePanel.Layout.cs | 204 ++ .../Console/ConsolePanel.cs | 244 +- .../Console/ConsolePanelContent.cs | 16 +- .../Console/ConsoleView.cs | 125 + .../Console/CopyConsoleDetailsCommand.cs | 2 + .../Console/CopyConsoleMessageCommand.cs | 2 + .../Styling/ConsoleStyle.cs | 114 + .../SceneViewPanel.cs | 2 +- .../Presentation/SettingsModal.cs | 8 +- .../Presentation/SettingsPageView.cs | 6 +- .../Styling/SettingsStyle.cs | 15 + .../Canvas/ShaderEditorCanvas.MiniMap.cs | 4 +- .../Canvas/ShaderEditorCanvas.cs | 426 +-- .../ShaderEditorDocuments.CanvasEdits.cs | 54 + .../Documents/ShaderEditorDocuments.cs | 22 +- .../Inspection/ShaderInspectionDrawers.cs | 8 +- .../FrameStatisticsSampler.cs | 81 +- .../Inno.Editor.Panel.Stats/StatsPanel.cs | 59 +- .../Inno.Editor.Assets.ImGui.csproj | 2 +- .../AssetSelectionInspectionDrawer.cs | 2 +- .../Properties/ScriptingApi.cs | 1 + .../Styling/AssetBrowserStyle.cs | 158 + .../Canvas/GraphCanvasLayout.cs | 84 + .../Canvas/GraphCanvasNode.cs | 102 + .../Canvas/GraphCanvasPointerController.cs | 199 ++ .../Canvas/GraphCanvasPointerInput.cs | 49 + .../Canvas/GraphCanvasPointerResult.cs | 49 + .../Canvas/GraphCanvasPort.cs | 108 + .../Canvas/GraphCanvasView.Nodes.cs | 264 ++ .../Canvas/GraphCanvasView.cs | 58 +- .../Properties/ScriptingApi.cs | 6 + .../Drawing/EditorDrawList.cs | 10 +- .../Drawing/ImGui.Drawing.cs | 26 +- .../presentation/Inno.Editor.ImGui/ImGui.cs | 27 + .../Properties/ScriptingApi.cs | 1 + .../Runtime/EditorUiSession.cs | 47 +- .../Runtime/ImGuiEditorRuntime.cs | 17 + .../Styling/EditorPalette.cs | 225 +- .../Styling/EditorStyleMetrics.cs | 324 +- .../Widgets/ImGuiWidget.Card.cs | 28 +- .../Widgets/ImGuiWidget.Clipping.cs | 79 + .../Widgets/ImGuiWidget.ContextMenu.cs | 2 +- .../Widgets/ImGuiWidget.Controls.cs | 2 +- .../Widgets/ImGuiWidget.HelpBox.cs | 30 +- .../Widgets/ImGuiWidget.Lifetime.cs | 1 + .../Widgets/ImGuiWidget.Panel.cs | 6 +- .../Widgets/ImGuiWidget.Search.cs | 5 +- .../Widgets/ImGuiWidget.Selection.cs | 182 +- .../Widgets/ImGuiWidget.Style.cs | 4 +- .../Widgets/ImGuiWidget.Tree.cs | 181 +- .../Widgets/ImGuiWidget.Trend.cs | 72 + .../Zoom/EditorZoomModule.cs | 6 +- .../scene/Inno.Scene/Core/GameScene.cs | 9 + .../Inno.Scene/Service/HierarchyService.cs | 48 +- .../Compilation/FrameworkReferenceResolver.cs | 70 +- .../ScriptCompilationEnvironment.cs | 5 +- .../Compilation/ScriptCompilerEngine.cs | 13 +- .../Compilation/ScriptProjectGenerator.cs | 33 +- .../IScriptCompilationEnvironmentProvider.cs | 20 + .../ScriptCompilationInputs.cs | 61 + .../Inno.Scripting.Compiler/ScriptCompiler.cs | 4 +- .../ScriptCompilerOptions.cs | 5 + tests/Invoke-EditorUiAcceptance.ps1 | 139 + tests/Invoke-IsolatedTests.ps1 | 6 + tests/Invoke-WikiChecks.ps1 | 14 +- .../AudioScriptingApiTests.cs | 9 +- .../Inno.Audio.Tests/Inno.Audio.Tests.csproj | 1 + .../Inno.Build.Tests/BuildPipelineTests.cs | 3 +- .../CompilerToolBuildScopeTests.cs | 1 + .../DevelopmentAppHostSdkTests.cs | 65 + .../NuGetLoadedIdentityTests.cs | 132 + .../Inno.Build.Tests/RepositoryWikiTests.cs | 10 +- .../GraphCanvasPointerTests.cs | 121 + .../GraphCanvasPresentationTests.cs | 78 + .../GraphCanvasViewTests.cs | 13 + .../GraphDocumentControllerTests.cs | 57 +- .../Inno.Editor.ImGui.Tests.csproj | 1 + .../NativeWindowTestHost.cs | 5 + .../ViewportMetricsTests.cs | 106 + .../ViewportRetirementTests.cs | 1 + .../WidgetLayoutTests.cs | 356 ++ .../EditorRuntimeTests.Selection.cs | 42 +- .../ConsolePanelNativeLayoutTests.cs | 21 +- .../ConsoleSearchNativeTests.cs | 154 + .../ConsoleVirtualizationNativeTests.cs | 139 + .../GraphCanvasNativeTests.cs | 139 + .../Inno.Editor.PlayMode.Tests.csproj | 10 + .../LargePanelPerformanceTests.cs | 128 + .../ListPanelKeyboardNativeTests.cs | 121 + .../NativeClipboard.cs | 64 + .../NativePanelHost.cs | 218 ++ .../NativePanelInput.cs | 54 + .../NativeProjectPanelHost.cs | 114 + .../SceneDocumentLifecycleNativeTests.cs | 49 + .../StatisticsPanelNativeTests.cs | 86 + .../TreeKeyboardOwnershipNativeTests.cs | 94 + .../VisualScaleMatrixTests.cs | 75 + .../editor-ui/PanelLifecycle.editor.cs | 71 + .../Inno.Scene.Tests/SceneFacadeTests.cs | 84 + .../EditorStyleMetricsTests.cs | 34 +- .../Inno.Editor.Scripting.Tests.csproj | 1 + .../InspectorPresentationLayoutTests.cs | 1 + .../ScriptingPipelineTests.cs | 10 +- .../ShaderEditorWorkflowTests.Performance.cs | 144 + .../ShaderEditorWorkflowTests.cs | 70 +- .../CSharp14ScriptingTests.cs | 109 +- .../CompilerFixture.cs | 15 +- .../Inno.Scripting.Compiler.Tests.csproj | 1 + .../SdkCompilationInputsTests.cs | 84 + .../ArchitectureOutputOwnerTests.cs | 71 + .../Inno.Tooling.Architecture.Tests.csproj | 1 + .../ArchitectureValidator.cs | 19 +- .../DocumentationSourceModels.cs | 11 +- .../MSBuildProjectGraphValidator.cs | 17 +- .../PublicApiBoundaryValidator.cs | 15 +- .../WikiProjectReferences.cs | 36 +- 189 files changed, 11534 insertions(+), 5902 deletions(-) create mode 100644 build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs create mode 100644 docs/architecture/EDITOR_UI_DESIGN_STANDARD.md create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Navigation.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Rows.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.Rows.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Styling/HierarchyStyle.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Inspector/Styling/InspectorTargetStyle.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.Layout.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleView.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Logging/Styling/ConsoleStyle.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.Settings/Styling/SettingsStyle.cs create mode 100644 src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.CanvasEdits.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Clipping.cs create mode 100644 src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Trend.cs create mode 100644 src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs create mode 100644 src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs create mode 100644 tests/Invoke-EditorUiAcceptance.ps1 create mode 100644 tests/build/Inno.Build.Tests/DevelopmentAppHostSdkTests.cs create mode 100644 tests/build/Inno.Build.Tests/NuGetLoadedIdentityTests.cs create mode 100644 tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPointerTests.cs create mode 100644 tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPresentationTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/ConsoleSearchNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/ConsoleVirtualizationNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/NativeClipboard.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/NativePanelInput.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/SceneDocumentLifecycleNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/StatisticsPanelNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/VisualScaleMatrixTests.cs create mode 100644 tests/fixtures/editor-ui/PanelLifecycle.editor.cs create mode 100644 tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs create mode 100644 tests/scripting/Inno.Scripting.Compiler.Tests/SdkCompilationInputsTests.cs create mode 100644 tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureOutputOwnerTests.cs diff --git a/AGENTS.md b/AGENTS.md index 5ea25f9bf..158ddc36d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -329,4 +329,6 @@ public void AAA( - Temporary presentation worlds register their borrowed Core allocator through the existing selection coordinator's routing lease. Restore Edit selection before releasing that lease; failed retirement must preserve its owner. The routing table never becomes a second live-object authority. - Boxed value types are not automatically neutral: a host struct may contain extension objects. Cross-callback targets retain only known neutral scalars strongly; all other non-identity objects use weak references, including boxed structs. Selection, focus and snapshots must not prolong their referenced objects' lifetime. - The formal script SDK host references the actual exported API owners directly, including reusable presentation libraries when required. It must not use concrete Panels as transitive dependency carriers for asset, shader or graph authoring APIs. +- Session-owned zoom and density requests commit through the presentation preparation phase after selecting the owned context and display metrics, before native frame creation. Repeated preparation during the same drawing frame cannot publish a new metric snapshot. Standalone metrics remain usable without native UI. +- Presentation preparation, drawing and publication form one owned operation. Reentry and retirement during that operation must fail before releasing resources. A callback failure ends only its owned native frame, preserves the original failure and never publishes partial draw data; it must not clear another context's stacks or lower native assertions. - Native presentation fixtures must create and enter an `EditorUiSession`, explicitly compose their feature/presentation owners, and resolve the exact service under test. Asynchronous shutdown fixtures must retry the public Pending retirement protocol until owned cleanup completes; they cannot rely on continuation timing or remove resource-release assertions. diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs index 01a27eba5..9bee115a5 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs @@ -104,6 +104,9 @@ GraphicsPipelineDescriptor pipelineDescriptor ///
public bool supportsViewports => m_bgfxDevice.supportsAdditionalSurfaces; + /// + public bool supportsVertexOffsets => true; + /// /// Queues a compiled shader replacement that commits atomically at a frame safety point. /// diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationContext.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationContext.cs index f08bd66f0..25dfe8b2e 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationContext.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationContext.cs @@ -60,10 +60,12 @@ bool force ) => m_context.TryCaptureIniSettings(out settings, force); - void IPresentationContext.RenderFrame(Action drawFrame) - { + void IPresentationContext.RenderFrame( + Action drawFrame, + Action? prepareFrame + ) { ArgumentNullException.ThrowIfNull(drawFrame); - _ = m_context.RenderFrame(drawFrame); + _ = m_context.RenderFrame(drawFrame, prepareFrame); } PresentationTextureHandle IPresentationContext.RegisterTexture(PersistentTextureHandle texture) diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs index 3fcae50fd..77fba39da 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs @@ -87,6 +87,11 @@ int height /// public interface IPlatformImGuiRenderer : IDisposable { + /// + /// Gets whether every draw-data consumer applies per-command vertex offsets to 16-bit indices. + /// + bool supportsVertexOffsets { get; } + /// /// Gets whether detached viewport windows are rendered by this backend. /// diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs index 9b4d976f5..7e3a0ab59 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs @@ -94,10 +94,25 @@ public partial bool TryCaptureIniSettings( /// /// Draw callback that issues ImGui commands for this frame. /// + /// + /// Optional preparation after context and display metrics selection, before native frame creation. + /// /// - /// The native pointer to ImDrawData for the rendered frame. + /// Borrowed ImDrawData valid until the next frame or context retirement; the caller must not free it. /// - public partial IntPtr RenderFrame(Action drawFrame); + /// + /// This context has retired. + /// + /// + /// A frame operation is already active. Callbacks must balance their own drawing scopes. + /// + /// + /// A callback failed and its owned native frame also failed to retire. + /// + public partial IntPtr RenderFrame( + Action drawFrame, + Action? prepareFrame = null + ); /// /// Draws a renderer-registered texture without exposing a native ImGui texture identifier. @@ -127,5 +142,8 @@ public unsafe partial void DrawImage( /// /// Releases all unmanaged/native resources associated with this ImGui context. /// + /// + /// A frame operation is still using this context. No resources are released by this rejection. + /// public partial void Dispose(); } diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Context/PlatformImGuiContext.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Context/PlatformImGuiContext.cs index 2517ddfc2..aaa19d314 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Context/PlatformImGuiContext.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Context/PlatformImGuiContext.cs @@ -50,6 +50,7 @@ public sealed unsafe partial class PlatformImGuiContext private readonly bool m_enableSmoothResize; private bool m_hasStartedFrame; private bool m_isFrameActive; + private bool m_isFrameOperationActive; private bool m_textInputActive; private bool m_disposed; @@ -102,6 +103,8 @@ internal PlatformImGuiContext( UpdateDisplayMetrics(io); m_renderer = renderer ?? new PlatformImGuiSdlRenderer(window); + if (m_renderer.supportsVertexOffsets) + io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasVtxOffset; m_viewports = enableViewports ? new PlatformImGuiViewportBackend(application, window, m_renderer) : null; RegisterClipboardCallbacks(); } @@ -658,35 +661,73 @@ internal void PrepareLiveResizeWindow(uint windowId) /// /// The callback invoked by render frame within the operation's owned lifetime. /// + /// + /// Optional callback invoked with the owned context before native frame creation. + /// /// - /// The validated int ptr that represents the completed operation. + /// Borrowed native draw data for the completed frame, valid until the next frame or retirement. /// - public partial IntPtr RenderFrame(Action drawFrame) - { + public partial IntPtr RenderFrame( + Action drawFrame, + Action? prepareFrame + ) { ArgumentNullException.ThrowIfNull(drawFrame); - if (m_isFrameActive) + ObjectDisposedException.ThrowIf(m_disposed, this); + if (m_isFrameOperationActive || m_isFrameActive) throw new InvalidOperationException("An ImGui frame cannot be reentered."); var now = m_frameTimer.Elapsed; var deltaSeconds = (float)(now - m_lastFrameTime).TotalSeconds; m_lastFrameTime = now; - SynchronizePendingLiveResizeWindows(); - BeginFrame(deltaSeconds); + m_isFrameOperationActive = true; try { + SynchronizePendingLiveResizeWindows(); + BeginFrame(deltaSeconds, prepareFrame); drawFrame(); return EndFrame(); } - catch + catch (Exception failure) { - m_isFrameActive = false; + try + { + AbortNativeFrame(); + } + catch (Exception cleanupFailure) + { + throw new AggregateException("Presentation failed and its native frame could not retire.", failure, cleanupFailure); + } throw; } + finally + { + m_isFrameActive = false; + m_isFrameOperationActive = false; + } } - private void BeginFrame(float deltaTimeSeconds) + private void AbortNativeFrame() { + if (m_disposed || m_context->WithinFrameScope == 0) + return; + ImGuiNativeContext* previous = ImGuiNative.Raw.GetCurrentContext(); + ImGuiNative.Raw.SetCurrentContext(m_context); + try + { + ImGuiNative.Raw.EndFrame(); + } + finally + { + if (previous != m_context) + ImGuiNative.Raw.SetCurrentContext(previous); + } + } + + private void BeginFrame( + float deltaTimeSeconds, + Action? prepareFrame + ) { if (m_disposed) { throw new ObjectDisposedException(nameof(PlatformImGuiContext)); @@ -707,6 +748,7 @@ private void BeginFrame(float deltaTimeSeconds) UpdateTextInputState(io); m_viewports?.SynchronizeMetrics(); + prepareFrame?.Invoke(); m_isFrameActive = true; ImGuiNative.NewFrame(); @@ -800,8 +842,11 @@ ref SDLEvent sdlEvent } /// - /// Releases the resources owned by this instance. + /// Retires the context and its owned resources after the current frame operation has completed. /// + /// + /// A frame operation still owns the context; the caller must retry after that operation completes. + /// public partial void Dispose() { if (m_disposed) @@ -809,6 +854,9 @@ public partial void Dispose() return; } + if (m_isFrameOperationActive) + throw new InvalidOperationException("A presentation context cannot retire during its frame operation."); + ImGuiNative.Raw.SetCurrentContext(m_context); var io = ImGuiNative.Raw.GetIO(); UnregisterClipboardCallbacks(); diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Rendering/PlatformImGuiSdlRenderer.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Rendering/PlatformImGuiSdlRenderer.cs index e47a4a312..a6457d993 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Rendering/PlatformImGuiSdlRenderer.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Rendering/PlatformImGuiSdlRenderer.cs @@ -34,6 +34,9 @@ internal PlatformImGuiSdlRenderer(Sdl3PlatformWindow window) /// public bool supportsViewports => false; + /// + public bool supportsVertexOffsets => true; + /// /// Records ImGui draw data for the main application viewport. /// diff --git a/build/cli/Inno.Build.Cli/BuildWorkspace.cs b/build/cli/Inno.Build.Cli/BuildWorkspace.cs index e9aeab4ba..121a0e315 100644 --- a/build/cli/Inno.Build.Cli/BuildWorkspace.cs +++ b/build/cli/Inno.Build.Cli/BuildWorkspace.cs @@ -214,7 +214,8 @@ .. PluginSourceService.GetActivatableMounts(scan) var compiler = new ScriptCompiler( new ScriptCompilerOptions { - projectRootDirectory = projectRoot + projectRootDirectory = projectRoot, + compilationEnvironment = new SdkScriptCompilationEnvironment(context.dotnetHost, projectRoot) }, assets, plugins); diff --git a/build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs b/build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs index edfb58209..e818b09d1 100644 --- a/build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs +++ b/build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using Inno.Build.Composition; using Inno.Build.Toolchains; @@ -20,7 +21,8 @@ public static class StandardBuildEnvironment /// The native target explicitly selected for executable build tools, independently of exported games. /// /// - /// An explicit managed SDK executable, or null to use the current tool process's selected host. + /// An explicit SDK executable, or null to use DOTNET_HOST_PATH, the loaded shared runtime's + /// muxer, then normal executable discovery when this process has no shared runtime installation. /// /// /// A frozen execution context; publication targets must be selected independently. @@ -36,8 +38,24 @@ public static BuildCompositionContext Capture( BuildTargetId toolsTarget, string? dotnetHost = null ) { - string host = dotnetHost ?? Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? "dotnet"; + BuildHostDescriptor buildHost = StandardBuildHosts.Capture(); + string host = dotnetHost ?? Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") + ?? CurrentRuntimeHost("dotnet" + buildHost.services.executableExtension); host = ToolchainEnvironment.ResolveExecutable(host); - return new BuildCompositionContext(host, applicationDirectory, StandardBuildHosts.Capture(), toolsTarget); + return new BuildCompositionContext(host, applicationDirectory, buildHost, toolsTarget); + } + + private static string CurrentRuntimeHost(string executable) + { + // An IDE can launch an apphost directly without setting DOTNET_HOST_PATH. + // Resolve the muxer beside the runtime that actually loaded this process before consulting PATH. + var runtime = new DirectoryInfo(RuntimeEnvironment.GetRuntimeDirectory()); + if (runtime.Parent?.Name == "Microsoft.NETCore.App" && runtime.Parent.Parent is { Name: "shared", Parent: { } installation }) + { + string runtimeHost = Path.Combine(installation.FullName, executable); + if (File.Exists(runtimeHost)) + return runtimeHost; + } + return executable; } } diff --git a/build/pipeline/Inno.Build/Inno.Build.csproj b/build/pipeline/Inno.Build/Inno.Build.csproj index 3bf32a643..d3fd52fa3 100644 --- a/build/pipeline/Inno.Build/Inno.Build.csproj +++ b/build/pipeline/Inno.Build/Inno.Build.csproj @@ -9,6 +9,7 @@
+ - + diff --git a/build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs b/build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs new file mode 100644 index 000000000..f4dafb011 --- /dev/null +++ b/build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs @@ -0,0 +1,112 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Inno.Build.Toolchains; +using Inno.Core.IO; +using Inno.Scripting.Compiler; + +namespace Inno.Build; + +/// +/// Evaluates the authoring project's selected SDK and maps its framework contract into scripting inputs. +/// +public sealed class SdkScriptCompilationEnvironment : IScriptCompilationEnvironmentProvider +{ + private readonly string m_dotnetHost; + private readonly string m_projectRoot; + + /// + /// Creates a host-owned input provider without selecting installed reference packs independently. + /// + /// + /// The .NET host executable selected by the composition root. + /// + /// + /// The authoring project whose ancestor SDK configuration governs compilation and IDE use. + /// + /// + /// A host or project location is empty. + /// + public SdkScriptCompilationEnvironment( + string dotnetHost, + string projectRoot + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(dotnetHost); + ArgumentException.ThrowIfNullOrWhiteSpace(projectRoot); + m_dotnetHost = dotnetHost; + m_projectRoot = Path.GetFullPath(projectRoot); + } + + /// + public ScriptCompilationInputs Capture(CancellationToken cancellationToken = default) + => CaptureAsync(cancellationToken).GetAwaiter().GetResult(); + + private async Task CaptureAsync(CancellationToken cancellationToken) + { + DotNetSdkDescriptor sdk = await DotNetSdkResolver.ResolveDirectoryAsync(m_dotnetHost, m_projectRoot, cancellationToken).ConfigureAwait(false); + string requests = Path.Combine(m_projectRoot, "Library", "Scripting", "SdkInputs"); + using ProcessOwnedDirectory owner = await ProcessOwnedDirectory.CreateAsync(requests, cancellationToken).ConfigureAwait(false); + string project = Path.Combine(owner.directory, "Framework.csproj"); + await File.WriteAllTextAsync(project, """ + + + false + false + + + + net10.0 + 14.0 + disable + false + + + + + + + + """, cancellationToken).ConfigureAwait(false); + _ = await RunSdkAsync(sdk, + [sdk.cliPath, "restore", project, "--disable-build-servers", "-m:1", "-nr:false", "-v:quiet"], + owner.directory, cancellationToken).ConfigureAwait(false); + string output = await RunSdkAsync(sdk, + [sdk.cliPath, "msbuild", project, "-nologo", "-m:1", "-nr:false", "-t:CaptureScriptFramework", + "-getItem:ReferencePath", "-getProperty:TargetFramework,LangVersion,DefineConstants,NETCoreSdkVersion"], + owner.directory, cancellationToken).ConfigureAwait(false); + using JsonDocument document = JsonDocument.Parse(output); + JsonElement properties = document.RootElement.GetProperty("Properties"); + if (properties.GetProperty("TargetFramework").GetString() != "net10.0" + || properties.GetProperty("LangVersion").GetString() != "14.0" + || properties.GetProperty("NETCoreSdkVersion").GetString() != sdk.sdkIdentity) + throw new InvalidDataException("SDK scripting evaluation returned an inconsistent framework, language or SDK identity."); + return new ScriptCompilationInputs(sdk.sdkIdentity, + document.RootElement.GetProperty("Items").GetProperty("ReferencePath").EnumerateArray() + .Select(static item => item.GetProperty("FullPath").GetString()!), + properties.GetProperty("DefineConstants").GetString()!.Split(';', StringSplitOptions.RemoveEmptyEntries)); + } + + private static async Task RunSdkAsync( + DotNetSdkDescriptor sdk, + IReadOnlyList arguments, + string directory, + CancellationToken cancellationToken + ) { + using var output = new StringWriter(); + using var errors = new StringWriter(); + try + { + await ToolchainEnvironment.RunAsync(sdk.hostPath, arguments, directory, cancellationToken, + DotNetSdkEnvironment.Create(sdk.hostPath), output, errors).ConfigureAwait(false); + return output.ToString(); + } + catch (InvalidOperationException failure) + { + throw new InvalidOperationException($"Script framework evaluation with SDK '{sdk.sdkIdentity}' failed.\n{output}{errors}", failure); + } + } +} diff --git a/build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs b/build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs index 8b74687f6..0b77850be 100644 --- a/build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs +++ b/build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs @@ -32,19 +32,59 @@ public static class DotNetSdkResolver /// /// The selected host cannot resolve a valid SDK identity and absolute base path. /// - public static async ValueTask ResolveAsync( + public static ValueTask ResolveAsync( string hostPath, string projectPath, CancellationToken cancellationToken = default ) { ArgumentException.ThrowIfNullOrWhiteSpace(hostPath); ArgumentException.ThrowIfNullOrWhiteSpace(projectPath); - hostPath = ToolchainEnvironment.ResolveExecutable(hostPath); string project = Path.GetFullPath(projectPath); if (!File.Exists(project)) throw new FileNotFoundException("The managed entry project is absent.", project); + return ResolveDirectoryAsync(hostPath, Path.GetDirectoryName(project)!, cancellationToken); + } + + /// + /// Selects the SDK from an authoring owner's directory before any temporary evaluation project is created. + /// + /// + /// The managed host explicitly selected by the composition root. + /// + /// + /// The existing source owner whose ancestor global.json rules govern the operation. + /// + /// + /// Cancels discovery and drains the SDK process. + /// + /// + /// The selected SDK and fixed managed CLI path, without probing reference pack directories. + /// + /// + /// The source owner directory does not exist. + /// + /// + /// The host or selected SDK CLI is missing. + /// + /// + /// The host cannot resolve the source owner's requested SDK. + /// + /// + /// The caller cancels SDK discovery. + /// + public static async ValueTask ResolveDirectoryAsync( + string hostPath, + string ownerDirectory, + CancellationToken cancellationToken = default + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(hostPath); + ArgumentException.ThrowIfNullOrWhiteSpace(ownerDirectory); + string directory = Path.GetFullPath(ownerDirectory); + if (!Directory.Exists(directory)) + throw new DirectoryNotFoundException($"The managed SDK source owner is absent: '{directory}'."); + hostPath = ToolchainEnvironment.ResolveExecutable(hostPath); string information = await ToolchainEnvironment.CaptureOutputAsync(hostPath, ["--info"], - Path.GetDirectoryName(project)!, cancellationToken, + directory, cancellationToken, DotNetSdkEnvironment.Create(hostPath, new Dictionary { ["DOTNET_CLI_UI_LANGUAGE"] = "en-US" diff --git a/docs/README.md b/docs/README.md index 537f7d9ba..ef8e21069 100644 --- a/docs/README.md +++ b/docs/README.md @@ -59,4 +59,4 @@ 平台构建接入新增独立标准产品库、Windows 宿主及三个平台 Build integration;各项目的当前公开契约和签名映射见对应分类索引。 -Editor UI 的程序集边界、当前文件树、手势、选择与基础能力复用见 [Editor UI 架构](architecture/EDITOR_UI_ARCHITECTURE.md)。[Editor 索引](editor/README.md) 包含 Assets.ImGui、Shaders.ImGui、Graph.ImGui 和 Workbench 的独立项目页。 +Editor UI 的程序集边界、当前文件树、手势、选择与基础能力复用见 [Editor UI 架构](architecture/EDITOR_UI_ARCHITECTURE.md),具体交互与实现约束见 [Editor UI 设计规范](architecture/EDITOR_UI_DESIGN_STANDARD.md)。[Editor 索引](editor/README.md) 包含 Assets.ImGui、Shaders.ImGui、Graph.ImGui 和 Workbench 的独立项目页。 diff --git a/docs/architecture/CURRENT_ACCEPTANCE.md b/docs/architecture/CURRENT_ACCEPTANCE.md index 074d87e5a..c3500b000 100644 --- a/docs/architecture/CURRENT_ACCEPTANCE.md +++ b/docs/architecture/CURRENT_ACCEPTANCE.md @@ -4,7 +4,7 @@ 当前 Editor UI 的分层、Session、Selection、History、插件调用方及逐项质量评价见[Editor UI 验收](../../records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md)和[当前 UI 架构](EDITOR_UI_ARCHITECTURE.md)。该报告分别列出自动验证、产品运行与未执行的人工/设备检查,不将此前数量累加为本次结果。 -**原计划尚未全部完成。** [逐项复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)确认菜单索引、Graph 抽取、Console/Stats 功能及规模/生命周期验收仍有缺口,PR #26 保持 Draft。源码 `2dc37114` 的 6/6 CI checks 已通过,包含真实 macOS ARM64 自动测试;不能继续把所有 Mac 自动验收说成未执行,也不能把 CI 通过等同于全部 UI 计划完成。本次另有 121 项 Interactions 复跑通过。 +**原计划尚未全部完成。** 后续实现和实时证据以[issue 闭合台账](../../records/architecture/EDITOR_UI_CLOSURE_PLAN.md)为准;菜单索引、Graph、Console、Stats 和大视图已有后续实现,完整矩阵仍在验收,原基线的当前 FlappyBird 同输入比较已复现为阻塞。PR #26 保持 Draft。`2dc37114` 等历史源码的 6/6 CI checks 包含 macOS ARM64 自动测试,只保留其对应 revision 的证明范围;不能把历史 CI 或局部用例算作最终候选已全部通过。 当前依赖通过标准 `.csproj` / `PackageReference` 和 SDK Restore 解析,源码不保存独立依赖锁清单;本轮范围与证据见[标准 NuGet 还原验收](../../records/architecture/acceptance/STANDARD_NUGET_RESTORE_ACCEPTANCE_2026_10_26.md)。基线保持 .NET SDK 10.0.402、C# 14、BGCS 4.1.2。 diff --git a/docs/architecture/CURRENT_ISSUES.md b/docs/architecture/CURRENT_ISSUES.md index f81dcdd36..1a763f701 100644 --- a/docs/architecture/CURRENT_ISSUES.md +++ b/docs/architecture/CURRENT_ISSUES.md @@ -8,11 +8,13 @@ ## 当前 Editor UI 计划缺口 -[2026-10-27 原计划复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)是本分支 UI 完成状态的最新依据。菜单/快捷键 generation 索引、通用 Graph 节点与框选呈现、Console 搜索、Stats 趋势与快照复用尚未完整实现;键盘旅程、10k/2k 性能、100 次开关和 10 次 reload 验收不完整。全部 CI 通过不能关闭这些条目;PR #26 保持 Draft。 +[原计划复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)发现的缺口正在按[真实 issue 闭合台账](../../records/architecture/EDITOR_UI_CLOSURE_PLAN.md)处理。菜单/快捷键 generation 索引、通用 Graph 呈现、Console 搜索、Stats 趋势与大视图裁剪已有实现;最终操作、视觉、生命周期和成对性能矩阵仍未全部闭合。深查发现的资产刷新身份问题按 [#43](https://github.com/Wolfiora/InnoEngine/issues/43)在所属索引修复,未在 UI 添加 fallback。PR #26 保持 Draft,不能用历史 CI 或局部测试代替最终验收。 + +E11 的原重构前 FlappyBird 同输入比较存在已复现阻塞:原基线 `b303d9f1` 及其正式工具能够构建,但当前插件源码依赖其不存在的 `EditorUiSession`,正式脚本编译失败。原始日志保留在闭合验收 owner 的 `e11/original-flappy-projection.log`。不增加兼容层或替换输入来冒充同输入比较;中立 10k/2k 负载继续按相同冻结输入验收。 F03 / [#13](https://github.com/Wolfiora/InnoEngine/issues/13) 已在本分支修复:History 先验证栈顶 ID 再 Pop,三种非 LIFO 拒绝回归在最终 Windows/macOS CI 通过,本次 121 项 Interactions 复跑也通过。等待 PR 合并后关闭 issue,不再把当前分支描述为仍在校验前弹栈。 -F09 / [#16](https://github.com/Wolfiora/InnoEngine/issues/16) 的固定默认 assets 读取已在当前源码及 master 中移除;测试读取 SDK Restore 实际返回的 ProjectAssetsFile。原 issue 的 package/load mismatch 等完整负向矩阵本次未重新证明,暂不将根因修正扩大为全部验收完成。 +F09 / [#16](https://github.com/Wolfiora/InnoEngine/issues/16) 的固定默认 assets 读取已在当前源码及 master 中移除;测试读取 SDK Restore 实际返回的 ProjectAssetsFile。本次四个真实 SDK/实际加载用例已通过:默认还原、独立 ArtifactsPath、损坏的默认 obj 干扰,以及测试自有副本中相同程序集名称与 MVID 但内容不同的负例;后者被内容校验拒绝,并验证 collectible context 严格回收。最终候选 CI 仍单独报告。 ## 本轮已修复并验证 diff --git a/docs/architecture/EDITOR_UI_ARCHITECTURE.md b/docs/architecture/EDITOR_UI_ARCHITECTURE.md index ab7f5005f..bad3b2d04 100644 --- a/docs/architecture/EDITOR_UI_ARCHITECTURE.md +++ b/docs/architecture/EDITOR_UI_ARCHITECTURE.md @@ -34,10 +34,11 @@ src/composition/editor/ ├── presentation/ │ ├── Inno.Editor.ImGui/ │ │ ├── Runtime/ # Session, frame and owner lifetime +│ │ │ ├── EditorUiSession.cs +│ │ │ └── EditorMenuRenderer.cs # Unified models to ImGui │ │ ├── Controls/ # Pointer-free facade and borrows │ │ ├── Widgets/ # Session-owned controls -│ │ ├── Menus/ # Unified models to ImGui -│ │ └── Styling/ # Palette and density metrics +│ │ └── Styling/ # Palette and density metrics │ ├── Inno.Editor.Assets.ImGui/ # Browser state, icons, basic Inspector │ ├── Inno.Editor.Shaders.ImGui/ # Material and node presentation │ ├── Inno.Editor.Graph.ImGui/ # Generic canvas geometry and hit tests diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md new file mode 100644 index 000000000..f35776bde --- /dev/null +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -0,0 +1,100 @@ +# Editor UI 设计与实现规范 + +[Wiki 首页](../README.md) · [Editor 架构](EDITOR_UI_ARCHITECTURE.md) · [Identity / Reference / Reload](IDENTITY_REFERENCE_RELOAD_STANDARD.md) + +本页定义当前 Editor 的交互和实现契约。测试结果及未完成项属于验收报告;本页不以规范代替验收证据。 + +## 职责与依赖 + +| Owner | 职责 | 禁止承担的职责 | +| --- | --- | --- | +| Core | Identity、Events、IO、序列化等基础机制 | 具体 Panel、ImGui 或领域编辑 | +| Editor Interactions | Action、菜单模型、选择、手势、History、文档协议 | Native 绘制、具体领域数据模型 | +| Feature | 原子领域修改、验证、恢复与 History payload | 停靠布局、Native 控件、另一个 Panel 的状态 | +| ImGui presentation | Session、作用域、控件、输入路由和通用样式 | 领域持久化、第二套事件总线或 Undo 栈 | +| Domain presentation | Browser、Shader 等领域视觉与组合 | 修改底层机制以维护领域类型白名单 | +| Panel | 组合已有 Feature 和 presentation | 独立资产数据库、编译流水线或通用状态管理器 | + +新增扩展先确定程序集、所有权和失败边界,再实现控件。每个项目采用项目级命名空间;Widgets 只在对应 `ImGuiWidget..cs` 中组织紧密相关的类型。公开 API 必须有实际外部消费者,内部缓存不得因测试方便而公开。 + +## 样式、尺寸与布局 + +样式分为基础语义值、通用组件值和领域呈现值。通用层表达文本、边框、焦点、禁用和内容强调,不认识 Shader 节点类别或 Asset 类型。Browser 布局属于 Assets presentation;Shader 端口和节点类别属于 Shader presentation。 + +Console、Hierarchy 和 Settings 的专属颜色与尺寸由各自 Panel 的内部 `Styling` 类型维护,Tag/Layer 颜色属于 Inspector 的内部呈现。公共 `EditorPalette` 与 `EditorStyleMetrics` 只保留基础值和可复用的卡片、标题、分区、表单等组件值;这些内部领域样式不进入脚本 API,也不保存 Session、Type 或扩展实例。 + +- DPI 表达显示设备密度,Zoom 表达用户的逻辑尺寸偏好;不能把同一尺寸重复缩放。 +- 后端先选择所属 Context 并更新显示 metrics,再执行 preparation callback,最后开始 Native frame。Session-owned Zoom/Density 在准备阶段统一生效;绘制中请求修改只影响后续帧。单独创建的 metrics 仍可作为无 Native 的即时值模型使用。 +- 表单先扣除操作列,再按标签与输入约 2:3 分配空间。长标签和窄 Inspector 使用裁剪、换行或 Tooltip,不能遮挡输入。 +- 同类按钮、搜索、树行、菜单、表格、卡片和 Modal 使用同一套通用 metrics。 +- 每个内容区只有一个主要滚动 owner。下拉框和菜单在当前 viewport 工作区内有界滚动。 +- 纯图标操作必须提供明确 Tooltip;错误、Missing、只读与禁用状态不能只依赖颜色。 +- 空内容说明下一步操作;忙碌状态说明当前工作;失败保留具体诊断和可执行的恢复路径。 + +尺度契约测试与物理显示器测试分别记录。模拟 framebuffer scale 不能代表已在所有真实 DPI 设备验收。 + +## Action、菜单、工具栏与快捷键 + +动作的唯一业务入口是现有 Action 协议。主菜单、上下文菜单、命令搜索、工具栏和快捷键查询相同的可用性、checked 状态和禁用原因。 + +带目标的 `EditorInteraction` 只用于当前同步请求,并在请求内强持有目标;不能把它缓存到后续帧或 generation。长期焦点与延迟路由另由既有 Interactions 使用 Core Identity 或弱引用管理。Hierarchy/Inspector 的公开 drop target 也是请求级借用,不属于 History payload 或持久状态。 + +静态声明的 Action ID、area、路径、顺序和快捷键按 extension generation 建索引。Selection、Play、Missing、只读状态及动态菜单参数必须来自当前请求,不能进入 generation 长寿命缓存。候选完整成功后原子替换;失败保留 last-good。 + +菜单路径表达用户任务,显示名称不承担持久身份。排序稳定、分组清晰,没有空组、重复动作或多余分隔线。上下文菜单的目标必须与当前选中或右击的记录一致;目标消失时关闭菜单,不能继续操作旧对象。 + +文本框、Popup 和 Modal 占有输入时,不向底层列表派发编辑键。保存等明确工作流仍遵守现有输入策略,不能以每个 Panel 自行判断快捷键代替统一路由。 + +## 选择、焦点与键盘导航 + +焦点、Selection、激活和数据修改是四种不同操作。跨帧定位对象必须重新经过 Core Identity;列表缓存只保留中立显示数据和 ID。 + +| 输入 | 行为 | +| --- | --- | +| Up / Down | 在当前可见序列移动 | +| Home / End | 移到首尾 | +| PageUp / PageDown | 按当前可见页移动 | +| Shift | 从 anchor 扩展范围 | +| 平台主修饰键 | 遵守产品注入的键盘策略 | +| Enter | 激活当前条目 | +| Escape | 退出搜索或取消当前交互 | + +过滤、排序、展开、鼠标范围选择与键盘范围选择必须使用同一序列。隐藏项不能被误选,裁剪不能丢失重命名、拖动、anchor 或焦点。安装 Plugin 内容保持只读,不能因为换了一种输入方式而绕过领域权限。 + +Console 搜索对 message、source、file 及已有诊断文本执行不区分大小写的普通子串匹配,并与级别过滤组合。Collapse、详情、复制和选择必须对应相同的匹配 occurrences。Clear 清除派生选择及展开状态,不建立第二份日志存储。 + +## 编辑、History 与文档 + +可逆数据修改统一进入 `EditorInteractions.history`。Feature 先完成原子领域修改,再记录中立 payload。单次连续拖动产生一条逻辑 Undo;两次独立拖动产生两条。布尔、创建、删除和排序不能使用连续值 mergeKey 合并。 + +Escape 回滚当前手势;命令、保存、Play、reload 和关闭文档先完成或取消相应手势,再进入自己的操作。批量中途失败必须恢复所有已修改内容。回滚失败保留 payload、owner 和诊断,不能提前释放后宣称成功。 + +Missing 形成可恢复 barrier,不移动 Undo/Redo 栈,不丢原始 bytes;恢复后原操作重新可用。保存、打开、纯选择不属于数据 Undo。文档 dirty、保存、放弃、关闭与恢复通过统一文档协议完成;不能用 Panel 局部标志隐藏尚未发布的领域修改。 + +## Graph 与大视图 + +通用 Graph 文档不依赖 Rendering 或 ImGui。Graph ImGui 只负责节点几何、命中、框选、移动预览和连接意图;Shader owner 解释端口、节点、参数、编译与诊断。呈现层不能直接代替领域 controller 修改文档。 + +几何缓存只保存 ID 和中立值,其有效性由文档、revision、generation 和尺度决定。平移通过坐标变换处理;局部节点变化只失效相关几何。自定义内容回调只属于当前绘制作用域,不进入长期缓存。 + +固定行高列表按可见行提交;树按 revision、展开及过滤建立扁平可见序列。Console 对可变高度内容缓存测量结果,宽度、尺度、内容和展开变化时失效。原生子窗口及上下文菜单复用有限的呈现槽位,不能用无限增长的记录 ID 创建历史窗口。 + +优化先定义负载和采样方法。提交项数量、分配、CPU、GC、frame median/P95 与功能结果一起验收;不能只展示 FPS,不能用降低正确性换取指标。 + +## 生命周期与异常 + +每个交互 Host 拥有一个 `EditorUiSession`。`current` 是同步作用域访问器;状态仍属于具体 Session 和 presentation owner。Native 借用不能越过 frame、window、context 或 owner thread。 + +Begin/End、Push/Pop、Child、Table、Popup、Clip 和样式遵守各自真实配对规则,并用局部 `try/finally` 释放本次所有权。扩展异常保留原始诊断,后续合法帧必须能够继续绘制。不能清空其他 owner 的状态来恢复当前 Panel。 + +Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒绝重入和退休。准备失败不创建 Native frame;绘制失败只结束所属 Context 的失败帧,不发布部分 draw data。调用方仍须平衡自己打开的窗口和栈,后端不伪造修复其他 owner 的作用域。 + +关闭 Panel 退休其暂态控件;关闭文档退休文档状态。generation 切换不能保留旧 Type、delegate、扩展实例或 collectible 引用。旧 ALC 退休严格执行既有 GC/finalizer/weak-monitor barrier;timeout 进入 Faulted,不能清空 monitor 继续。 + +`editor.ini` 保存可读布局及中立项目导航,不保存 Selection、Undo、runtime 引用或编译中间态。正常退出先捕获 Module/Panel,再捕获最新布局,最后原子写入。 + +## 交付与验收 + +变更必须同步源码、调用方、项目引用、公开 XML、Wiki 和适用测试。使用真实公开契约与可替换边界,不使用 IVT、私有反射或测试后门。 + +最终报告分别评价架构、组织、状态与生命周期、抽象、复用、样式、扩展、交互、Undo/Redo、菜单和颗粒度;每项列出证据与限制。目录整齐、测试数量或正常启动不能代替视觉、输入、重复 reload、资源收敛和成对性能验收。 diff --git a/docs/architecture/README.md b/docs/architecture/README.md index d7aa573eb..de78eed1b 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -15,6 +15,8 @@ | [Web Player](WEB_PLAYER_ARCHITECTURE.md) | 浏览器启动、回调、链接与运行生命周期如何组合? | | [Identity 与热重载](IDENTITY_REFERENCE_RELOAD_STANDARD.md) | 如何跨代定位对象、保留 Missing、退休旧模块? | | [C# 开发规范](CSHARP_DEVELOPMENT_STANDARD.md) | 如何组织源码、公开契约、错误和注释? | +| [Editor UI 架构](EDITOR_UI_ARCHITECTURE.md) | Feature、Panel、presentation 和 Session 如何协作? | +| [Editor UI 设计规范](EDITOR_UI_DESIGN_STANDARD.md) | 样式、输入、选择、History、菜单和生命周期遵守什么契约? | | [当前问题](CURRENT_ISSUES.md) | 哪些能力或证据仍未收口? | | [当前验收](CURRENT_ACCEPTANCE.md) | 当前代码实际验证了什么,有哪些环境限制? | diff --git a/docs/assets/Inno.Assets.Pipeline.md b/docs/assets/Inno.Assets.Pipeline.md index f313e08b5..4e4465da4 100644 --- a/docs/assets/Inno.Assets.Pipeline.md +++ b/docs/assets/Inno.Assets.Pipeline.md @@ -862,22 +862,22 @@ static bool AdvanceSampleImport( | [`public string Inno.Assets.Pipeline.AssetFileEntry.extension { get; internal set; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L64) | Gets the normalized lower-case file extension. | — | — | | [`public Inno.Assets.Pipeline.AssetFileEntry.AssetFileEntry()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileEntry.cs#L11) | Inherited or compiler-provided contract. | — | — | | [`public sealed class Inno.Assets.Pipeline.AssetFileSystem : System.IDisposable`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L18) | Indexed asset source filesystem model backed by . | — | — | -| [`public string Inno.Assets.Pipeline.AssetFileSystem.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L37) | Gets the absolute source asset root. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.isWatching { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L42) | Gets whether source file watching is active. | — | — | -| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(string assetRoot, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L59) | Creates an indexed source file system.
`assetRoot`: The absolute source root.
`autoStart`: Whether file watching should start immediately.
`flushDelayMs`: The watcher batch delay in milliseconds.
`sourcePolicy`: The source filtering policy, or for defaults. | — | — | -| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(System.Collections.Generic.IReadOnlyList mounts, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool requireWritableProject = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L96) | Creates an indexed file system over isolated source mounts.
`mounts`: Complete source mount snapshot.
`autoStart`: Whether writable project source watching starts immediately.
`flushDelayMs`: Watcher batch delay in milliseconds.
`sourcePolicy`: Source filtering policy, or for defaults.
`requireWritableProject`: Whether the project mount must support authoring mutations. Runtime catalogs may set this to when every mounted identity directory is read-only. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Start()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L168) | Starts source file watching. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Stop()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L177) | Stops source file watching. | — | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Refresh()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L188) | Rebuilds the indexed source file snapshot. | — | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.Exists(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L210) | Determines whether an indexed source entry exists.
`path`: The isolated source path. | when the entry exists. | — | -| [`public bool Inno.Assets.Pipeline.AssetFileSystem.TryGetEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L232) | Tries to resolve an indexed source entry.
`path`: The isolated source path.
`entry`: The resolved entry when available. | when the entry exists. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L261) | Gets a stable snapshot of indexed entries.
`includeDirectories`: Whether directory entries should be included. | The indexed entries. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L285) | Gets immediate children of an indexed directory.
`parent`: The isolated parent directory path. | The immediate child entries. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L308) | Polls normalized changes and refreshes the indexed source snapshot. | The changes observed since the previous poll. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L319) | Polls changes and reports whether watcher recovery requires a full rescan.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The changes observed since the previous poll. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L335) | Waits for a quiet watcher window, refreshes the index, and returns queued changes. | An immutable snapshot of the values selected by the operation. | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L346) | Waits for queued changes and reports whether a full rescan is required.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The normalized queued changes. | — | -| [`public void Inno.Assets.Pipeline.AssetFileSystem.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L366) | Releases the resources owned by this implementation. | — | — | +| [`public string Inno.Assets.Pipeline.AssetFileSystem.assetRoot { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L44) | Gets the absolute source asset root. | — | — | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.isWatching { get; }`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L49) | Gets whether source file watching is active. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(string assetRoot, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L66) | Creates an indexed source file system.
`assetRoot`: The absolute source root.
`autoStart`: Whether file watching should start immediately.
`flushDelayMs`: The watcher batch delay in milliseconds.
`sourcePolicy`: The source filtering policy, or for defaults. | — | — | +| [`public Inno.Assets.Pipeline.AssetFileSystem.AssetFileSystem(System.Collections.Generic.IReadOnlyList mounts, bool autoStart = true, int flushDelayMs = 80, Inno.Assets.Pipeline.AssetSourcePolicy? sourcePolicy = null, bool requireWritableProject = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L103) | Creates an indexed file system over isolated source mounts.
`mounts`: Complete source mount snapshot.
`autoStart`: Whether writable project source watching starts immediately.
`flushDelayMs`: Watcher batch delay in milliseconds.
`sourcePolicy`: Source filtering policy, or for defaults.
`requireWritableProject`: Whether the project mount must support authoring mutations. Runtime catalogs may set this to when every mounted identity directory is read-only. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Start()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L175) | Starts source file watching. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Stop()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L184) | Stops source file watching. | — | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Refresh()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L210) | Reconciles a complete source snapshot while preserving unchanged entry identities. | — | `System.ObjectDisposedException`: The source filesystem has been disposed.
`System.IO.IOException`: Source enumeration fails; the previous complete index and its identities remain active.
`System.UnauthorizedAccessException`: A source directory cannot be read; the previous complete index remains active.
`System.IO.InvalidDataException`: Multiple source paths claim the same persistent entry identity.
`System.InvalidOperationException`: A new entry identity cannot be registered; the previous index remains active. | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.Exists(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L267) | Determines whether an indexed source entry exists.
`path`: The isolated source path. | when the entry exists. | — | +| [`public bool Inno.Assets.Pipeline.AssetFileSystem.TryGetEntry(Inno.Assets.AssetPath path, out Inno.Assets.Pipeline.AssetFileEntry entry)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L289) | Tries to resolve an indexed source entry.
`path`: The isolated source path.
`entry`: The resolved entry when available. | when the entry exists. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetEntries(bool includeDirectories = true)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L318) | Gets a stable snapshot of indexed entries.
`includeDirectories`: Whether directory entries should be included. | The indexed entries. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.GetChildren(Inno.Assets.AssetPath parent)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L342) | Gets immediate children of an indexed directory.
`parent`: The isolated parent directory path. | The immediate child entries. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L365) | Polls normalized changes and refreshes the indexed source snapshot. | The changes observed since the previous poll. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.PollChanges(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L376) | Polls changes and reports whether watcher recovery requires a full rescan.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The changes observed since the previous poll. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L392) | Waits for a quiet watcher window, refreshes the index, and returns queued changes. | An immutable snapshot of the values selected by the operation. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Assets.Pipeline.AssetFileSystem.WaitForIdle(out bool requiresFullRescan)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L403) | Waits for queued changes and reports whether a full rescan is required.
`requiresFullRescan`: Whether the watcher reported an unreliable event stream. | The normalized queued changes. | — | +| [`public void Inno.Assets.Pipeline.AssetFileSystem.Dispose()`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetFileSystem.cs#L423) | Releases the resources owned by this implementation. | — | — | | [`public static class Inno.Assets.Pipeline.AssetSample`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L11) | Defines authoring sample directories and their installed Plugin behavior. | — | — | | [`public const string Inno.Assets.Pipeline.AssetSample.fileType = ".isample"`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L16) | Gets the logical File Browser type used for a sample directory. | — | — | | [`public static bool Inno.Assets.Pipeline.AssetSample.HasSampleDirectoryName(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Sources/AssetSample.cs#L28) | Determines whether the final path segment uses the sample-directory naming convention.
`path`: The isolated source path to inspect. | when the final segment starts with ~, regardless of whether the path belongs to the writable Project source or an installed Plugin source. | — | diff --git a/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Bgfx.md b/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Bgfx.md index 93e8becd3..228689b58 100644 --- a/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Bgfx.md +++ b/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Bgfx.md @@ -28,15 +28,14 @@ RetireViewport 清除该视口后续绘制并委托 Device.RetireWindowSurface ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + @@ -51,57 +50,48 @@ RetireViewport 清除该视口后续绘制并委托 Device.RetireWindowSurface - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.BgfxImGuiRenderer(Inno.Rendering.IRenderDevice device, Inno.Rendering.GraphicsPipelineDescriptor pipelineDescriptor)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L78) | Creates a frame-contributing ImGui renderer around a backend-neutral shader artifact.
`device`:Active BGFX-backed neutral device.
`pipelineDescriptor`:Compiled ImGui shader stages and reflected interface. | — | `System.ArgumentException`:Thrown when the shader artifact does not use the required ImGui layout.
`System.NotSupportedException`:Thrown when device is not BGFX-backed. | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.AddRenderPasses(Inno.Rendering.RenderGraphBuilder graph, ulong frameIndex)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L367) | Adds the renderer's frame passes and resource declarations to the render graph.
`graph`:The graph consumed by add render passes; ownership remains with the caller unless explicitly stated otherwise.
`frameIndex`:The monotonic frame identity associated with this operation. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.CreateViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L219) | Creates a viewport using this implementation's validated inputs.
`target`:The existing target that receives the validated result. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L423) | Marks renderer-owned GPU resources for release at the next frame safety point or device teardown. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.DrainViewportRetirements()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L315) | Drains requested retirements at a closed-frame owner-thread point outside native UI callbacks. | Failure or timeout retains all unfinished viewport and renderer owners. | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.PrepareFrame(ulong frameIndex)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L323) | Prepares frame-owned resources before render graph recording begins.
`frameIndex`:The monotonic frame identity associated with this operation. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.PresentViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L283) | Presents the completed frame for the supplied auxiliary viewport.
`target`:The existing target that receives the validated result. | — | — | -| [`public Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RegisterTexture(Inno.Rendering.PersistentTextureHandle texture)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L132) | Registers a persistent render texture as an opaque ImGui texture token.
返回:An opaque token accepted by Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.DrawImage(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle,System.Numerics.Vector2,System.Numerics.Vector2,System.Numerics.Vector2).
`texture`:Texture owned by the active device generation. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RenderMain(nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L176) | Records ImGui draw data for the main application viewport.
`drawData`:The draw data consumed by render main; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RenderViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target, nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L260) | Records ImGui draw data for the supplied auxiliary viewport.
`target`:The existing target that receives the validated result.
`drawData`:The draw data consumed by render viewport; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.ReplaceShaderArtifact(Inno.Rendering.GraphicsPipelineDescriptor pipelineDescriptor)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L112) | Queues a compiled shader replacement that commits atomically at a frame safety point.
`pipelineDescriptor`:Complete candidate artifact. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.ResizeViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L238) | Resizes an auxiliary viewport surface to its current platform dimensions.
`target`:The existing target that receives the validated result. | — | — | -| [`public System.Threading.Tasks.Task Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RetireViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L297) | Destroys the auxiliary viewport and releases its rendering resources.
返回:Completion of render-thread window release, or an already completed task for an unallocated viewport.
`target`:The existing target that receives the validated result. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.SynchronizeMainOutput(int pixelWidth, int pixelHeight)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L199) | Invalidates a captured main packet when its drawable extent changes. The Shell owns device presentation availability and publishes it at frame boundaries.
`pixelWidth`:The pixel width consumed by synchronize main output; ownership remains with the caller unless explicitly stated otherwise.
`pixelHeight`:The pixel height consumed by synchronize main output; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.UnregisterTexture(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle texture)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L157) | Unregisters an opaque texture token without taking ownership of the source texture.
返回:true when the token was an external registration.
`texture`:Previously allocated token. | — | — | -| [`public string? Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.lastShaderError { get; private set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L99) | Gets the last recoverable shader replacement error while the last-good pipeline remains active. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.supportsViewports { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L104) | Gets whether supports viewports is enabled for this implementation. | — | — | -| [`public static Inno.Rendering.RenderVertexLayout Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.vertexLayout { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L94) | Gets the exact interleaved vertex layout required by the built-in ImGui shaders. | — | — | -| [`public sealed class Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer : Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer, Inno.Rendering.IRenderFrameGraphContributor`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L17) | Copies Dear ImGui command lists into frame-owned data and composites them through BGFX RenderGraph passes. | — | — | - -### `Inno.Adapter.Presentation.ImGui.BgfxImGuiShaderArtifacts` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Rendering.GraphicsPipelineDescriptor Inno.Adapter.Presentation.ImGui.BgfxImGuiShaderArtifacts.Load(Inno.Rendering.GraphicsApi api)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiShaderArtifacts.cs#L25) | Reads the precompiled program for the declared product and requested graphics API without invoking a compiler.
返回:The validated texture binding, stages and render state required by ImGui.
`api`:The active device API. | — | `System.IO.InvalidDataException`:The installed adapter lacks a required artifact or its binding contract is invalid. | -| [`public static class Inno.Adapter.Presentation.ImGui.BgfxImGuiShaderArtifacts`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiShaderArtifacts.cs#L11) | Loads the graph-built, source-free ImGui program distributed with this presentation adapter. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider` - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider.ImGuiPresentationProvider(Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions interaction)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationProvider.cs#L22) | Registers the bundled presentation implementation without creating a native context.
`interaction`:The borrowed immutable conventions explicitly selected by the Editor product. | — | `System.ArgumentNullException`:Interaction configuration is absent. | -| [`public override Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider.CreateContext(Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationProvider.cs#L29) | Creates a new caller-owned presentation context over the supplied host resources.
返回:A new context whose caller must dispose it before releasing the supplied resources. Providers must not return null.
`options`:Externally owned platform and rendering resources, together with the requested presentation policy. | — | `System.NotSupportedException`:The host resources or requested features are incompatible with this implementation. | +| [`public sealed class Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer : Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer, Inno.Rendering.IRenderFrameGraphContributor`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L18) | Copies Dear ImGui command lists into frame-owned data and composites them through BGFX RenderGraph passes. | — | — | +| [`public Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.BgfxImGuiRenderer(Inno.Rendering.IRenderDevice device, Inno.Rendering.GraphicsPipelineDescriptor pipelineDescriptor)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L79) | Creates a frame-contributing ImGui renderer around a backend-neutral shader artifact.
`device`: Active BGFX-backed neutral device.
`pipelineDescriptor`: Compiled ImGui shader stages and reflected interface. | — | `System.ArgumentException`: Thrown when the shader artifact does not use the required ImGui layout.
`System.NotSupportedException`: Thrown when is not BGFX-backed. | +| [`public static Inno.Rendering.RenderVertexLayout Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.vertexLayout { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L95) | Gets the exact interleaved vertex layout required by the built-in ImGui shaders. | — | — | +| [`public string? Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.lastShaderError { get; private set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L100) | Gets the last recoverable shader replacement error while the last-good pipeline remains active. | — | — | +| [`public bool Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.supportsViewports { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L105) | Gets whether supports viewports is enabled for this implementation. | — | — | +| [`public bool Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.supportsVertexOffsets { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L108) | Gets whether every draw-data consumer applies per-command vertex offsets to 16-bit indices. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.ReplaceShaderArtifact(Inno.Rendering.GraphicsPipelineDescriptor pipelineDescriptor)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L116) | Queues a compiled shader replacement that commits atomically at a frame safety point.
`pipelineDescriptor`: Complete candidate artifact. | — | — | +| [`public Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RegisterTexture(Inno.Rendering.PersistentTextureHandle texture)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L136) | Registers a persistent render texture as an opaque ImGui texture token.
`texture`: Texture owned by the active device generation. | An opaque token accepted by . | — | +| [`public bool Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.UnregisterTexture(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle texture)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L161) | Unregisters an opaque texture token without taking ownership of the source texture.
`texture`: Previously allocated token. | when the token was an external registration. | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RenderMain(nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L180) | Records ImGui draw data for the main application viewport.
`drawData`: The draw data consumed by render main; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.SynchronizeMainOutput(int pixelWidth, int pixelHeight)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L203) | Invalidates a captured main packet when its drawable extent changes. The Shell owns device presentation availability and publishes it at frame boundaries.
`pixelWidth`: The pixel width consumed by synchronize main output; ownership remains with the caller unless explicitly stated otherwise.
`pixelHeight`: The pixel height consumed by synchronize main output; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.CreateViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L223) | Creates a viewport using this implementation's validated inputs.
`target`: The existing target that receives the validated result. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.ResizeViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L242) | Resizes an auxiliary viewport surface to its current platform dimensions.
`target`: The existing target that receives the validated result. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RenderViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target, nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L264) | Records ImGui draw data for the supplied auxiliary viewport.
`target`: The existing target that receives the validated result.
`drawData`: The draw data consumed by render viewport; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.PresentViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L287) | Presents the completed frame for the supplied auxiliary viewport.
`target`: The existing target that receives the validated result. | — | — | +| [`public System.Threading.Tasks.Task Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.RetireViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L301) | Destroys the auxiliary viewport and releases its rendering resources.
`target`: The existing target that receives the validated result. | Completion of render-thread window release, or an already completed task for an unallocated viewport. | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.DrainViewportRetirements()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L319) | Drains requested retirements at a closed-frame owner-thread point outside native UI callbacks. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.PrepareFrame(ulong frameIndex)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L327) | Prepares frame-owned resources before render graph recording begins.
`frameIndex`: The monotonic frame identity associated with this operation. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.AddRenderPasses(Inno.Rendering.RenderGraphBuilder graph, ulong frameIndex)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L371) | Adds the renderer's frame passes and resource declarations to the render graph.
`graph`: The graph consumed by add render passes; ownership remains with the caller unless explicitly stated otherwise.
`frameIndex`: The monotonic frame identity associated with this operation. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.BgfxImGuiRenderer.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiRenderer.cs#L427) | Marks renderer-owned GPU resources for release at the next frame safety point or device teardown. | — | — | +| [`public static class Inno.Adapter.Presentation.ImGui.BgfxImGuiShaderArtifacts`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiShaderArtifacts.cs#L11) | Loads the graph-built, source-free ImGui program distributed with this presentation adapter. | — | — | +| [`public static Inno.Rendering.GraphicsPipelineDescriptor Inno.Adapter.Presentation.ImGui.BgfxImGuiShaderArtifacts.Load(Inno.Rendering.GraphicsApi api)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/BgfxImGuiShaderArtifacts.cs#L25) | Reads the precompiled program for the declared product and requested graphics API without invoking a compiler.
`api`: The active device API. | The validated texture binding, stages and render state required by ImGui. | `System.IO.InvalidDataException`: The installed adapter lacks a required artifact or its binding contract is invalid. | | [`public sealed class Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider : Inno.Adapter.Presentation.PresentationBackendProvider`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationProvider.cs#L9) | Registers SDL3 window integration with BGFX ImGui presentation. | — | — | +| [`public Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider.ImGuiPresentationProvider(Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions interaction)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationProvider.cs#L22) | Registers the bundled presentation implementation without creating a native context.
`interaction`: The borrowed immutable conventions explicitly selected by the Editor product. | — | `System.ArgumentNullException`: Interaction configuration is absent. | +| [`public override Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.ImGui.ImGuiPresentationProvider.CreateContext(Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Bgfx/ImGuiPresentationProvider.cs#L29) | Creates a new caller-owned presentation context over the supplied host resources.
`options`: Externally owned platform and rendering resources, together with the requested presentation policy. | A new context whose caller must dispose it before releasing the supplied resources. Providers must not return null. | `System.NotSupportedException`: The host resources or requested features are incompatible with this implementation. | ## 项目依赖 -- [Inno.Native.ImGui](Inno.Native.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Rendering.Bgfx](../Bgfx/Inno.Adapter.Rendering.Bgfx.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Adapter.Presentation](../../platform/Inno.Adapter.Presentation.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Presentation.ImGui.Sdl3](Inno.Adapter.Presentation.ImGui.Sdl3.md):公开引用边界由实际签名核对。 -- [Inno.Rendering](../../rendering/Inno.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Platform.Sdl3](../Sdl3/Inno.Adapter.Platform.Sdl3.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Native.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Rendering.Bgfx` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Presentation` | 公开签名依赖 | +| `Inno.Adapter.Presentation.ImGui.Sdl3` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Adapter.Platform.Sdl3` | 公开签名依赖 | diff --git a/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md b/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md index a323875f7..ed42b1120 100644 --- a/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md +++ b/docs/backends/ImGui/Inno.Adapter.Presentation.ImGui.Sdl3.md @@ -4,6 +4,8 @@ Viewport 位置与尺寸 setter 经共享 Native ImGui 的标量 callback adapte [分类索引](README.md) · [Editor ImGui](../../editor/Inno.Editor.ImGui.md) · [BGFX ImGui](Inno.Adapter.Presentation.ImGui.Bgfx.md) · [Wiki 首页](../../README.md) +`RenderFrame` 的 preparation callback 在选择所属 Context、更新显示 metrics 后执行,并早于 Native NewFrame。准备、绘制和发布期间拒绝重入或 Dispose。准备失败不创建 Native frame;绘制 callback 失败只结束所属帧,不向 renderer 发布部分 draw data,原异常继续传播。调用方仍负责平衡自己的窗口和 Push/Pop;后端不会清空其他 Context 的栈。返回的 draw-data 指针仅借用至下一帧或 Context 退休。 + ## 公开 API Viewport 的位置与逻辑尺寸由 ImGui 的平台 Move/Resize 请求及 getter/setter 协议协调。 @@ -42,62 +44,65 @@ CreateImGuiContext 必须显式传入 ImGuiInteractionOptions,窗口必须属 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Adapter.Presentation.ImGui.ImGuiContextFlags` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableDocking = 2`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L22) | The enable docking key. | — | — | -| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableSmoothResize = 4`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L26) | The enable smooth resize key. | — | — | -| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableViewports = 1`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L18) | The enable viewports key. | — | — | -| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.None = 0`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L14) | The none key. | — | — | -| [`public enum Inno.Adapter.Presentation.ImGui.ImGuiContextFlags : int`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L9) | Identifies the supported im gui context flags values for this contract. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiFont` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiFont.IsAvailable(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L26) | Gets whether an exact font style is available in the current ImGui context.
返回:true when the exact face is registered.
`style`:The composable style to query. | — | — | -| [`public static Inno.Adapter.Presentation.ImGui.ImGuiFontScope Inno.Adapter.Presentation.ImGui.ImGuiFont.PushStyle(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L49) | Pushes a font style for the current ImGui context and returns a scope that restores it.
返回:A disposable scope. Missing faces gracefully fall back to the nearest registered face.
`style`:The composable style to use. | — | — | -| [`public static class Inno.Adapter.Presentation.ImGui.ImGuiFont`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L12) | Provides scoped access to font faces registered for the current ImGui context. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiFontScope` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Adapter.Presentation.ImGui.ImGuiFontScope.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L173) | Restores the font that was active before this scope. | — | — | -| [`public ref struct Inno.Adapter.Presentation.ImGui.ImGuiFontScope`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L157) | Restores the previous ImGui font when a scoped style ends. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiFontStyle` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Bold = 1`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L19) | Uses a bold font face. | — | — | -| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Italic = 2`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L24) | Uses an italic font face. | — | — | -| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Regular = 0`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L14) | Uses the regular font face. | — | — | -| [`public enum Inno.Adapter.Presentation.ImGui.ImGuiFontStyle : int`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L9) | Describes composable visual variants of an ImGui font family. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiIcon` - @@ -1503,1541 +1508,1479 @@ CreateImGuiContext 必须显式传入 ImGuiInteractionOptions,窗口必须属 -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.A = "A"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L26) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AddressBook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L27) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AddressCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L28) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L29) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignJustify = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L30) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L31) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L32) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Anchor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L33) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L34) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L35) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L36) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L37) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L38) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L39) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L40) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L42) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L43) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L44) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L45) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L41) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ankh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L46) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AppleWhole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L47) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Archway = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L48) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L49) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown19 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L50) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown91 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L51) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownAZ = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L52) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L53) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownShortWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L54) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownUpAcrossLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L55) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownUpLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L56) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownWideShort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L57) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownZA = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L58) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L59) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowLeftLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L60) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowPointer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L61) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L62) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L63) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L64) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L65) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightToBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L66) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightToCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L67) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L68) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRotateRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L69) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L87) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsDownToPeople = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L88) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L89) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsLeftRightToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L90) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L91) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsSpin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L92) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsSplitUpAndLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L93) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToCircle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L94) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L95) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToEye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L96) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsTurnRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L97) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsTurnToDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L98) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L99) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpDownLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L100) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L101) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTrendDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L70) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTrendUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L71) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTurnDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L72) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTurnUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L73) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L74) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp19 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L75) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp91 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L76) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpAZ = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L77) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L78) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromGroundWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L79) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromWaterPump = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L80) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L81) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpRightDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L82) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpRightFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L83) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpShortWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L84) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpWideShort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L85) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpZA = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L86) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Asterisk = "*"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L102) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.At = "@"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L103) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Atom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L104) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AudioDescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L105) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AustralSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L106) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Award = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L107) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.B = "B"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L108) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Baby = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L109) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BabyCarriage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L110) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Backward = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L111) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BackwardFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L112) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BackwardStep = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L113) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L114) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacteria = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L115) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacterium = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L116) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BagShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L117) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bahai = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L118) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BahtSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L119) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ban = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L120) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bandage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L122) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BangladeshiTakaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L123) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BanSmoking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L121) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Barcode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L124) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L125) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BarsProgress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L126) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BarsStaggered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L127) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Baseball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L128) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BaseballBatBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L129) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Basketball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L131) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BasketShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L130) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bath = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L132) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L133) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L134) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L135) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryQuarter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L136) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryThreeQuarters = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L137) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L138) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BedPulse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L139) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BeerMugEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L140) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L141) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BellConcierge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L142) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BellSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L143) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BezierCurve = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L144) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bicycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L145) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Binoculars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L146) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Biohazard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L147) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BitcoinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L148) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Blender = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L149) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BlenderPhone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L150) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Blog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L151) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bold = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L152) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L153) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoltLightning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L154) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bomb = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L155) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L156) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L157) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Book = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L158) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookAtlas = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L159) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookBible = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L160) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookBookmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L161) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookJournalWhills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L162) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bookmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L169) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L163) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L164) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookOpenReader = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L165) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookQuran = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L166) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookSkull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L167) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookTanakh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L168) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderAll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L170) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderNone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L171) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderTopLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L172) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoreHole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L173) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BottleDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L174) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BottleWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L175) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlFood = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L176) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlingBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L178) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlRice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L177) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Box = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L179) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxArchive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L180) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxesPacking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L183) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxesStacked = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L184) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L181) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxTissue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L182) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Braille = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L185) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Brain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L186) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BrazilianRealSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L187) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BreadSlice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L188) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bridge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L189) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L190) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L191) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L192) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L193) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L194) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Briefcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L195) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BriefcaseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L196) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Broom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L197) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BroomBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L198) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Brush = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L199) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bucket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L200) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L201) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bugs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L203) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BugSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L202) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Building = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L204) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L205) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L206) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L207) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L208) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingColumns = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L209) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L210) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L211) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingNgo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L212) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L213) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L214) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L215) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L216) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bullhorn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L217) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bullseye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L218) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Burger = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L219) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Burst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L220) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L221) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BusinessTime = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L223) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BusSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L222) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.C = "C"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L224) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CableCar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L225) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CakeCandles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L226) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Calculator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L227) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Calendar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L228) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L229) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarDay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L230) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarDays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L231) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L232) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L233) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarWeek = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L234) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L235) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Camera = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L236) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CameraRetro = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L237) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CameraRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L238) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Campground = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L239) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CandyCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L240) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cannabis = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L241) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Capsules = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L242) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Car = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L243) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Caravan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L250) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarBattery = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L244) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L245) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L251) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L252) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L253) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L254) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L246) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarRear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L247) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Carrot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L255) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarSide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L248) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L256) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartFlatbed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L257) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartFlatbedSuitcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L258) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L259) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L260) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarTunnel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L249) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CashRegister = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L261) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L262) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CediSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L263) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CentSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L264) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Certificate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L265) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chair = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L266) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chalkboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L267) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChalkboardUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L268) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChampagneGlasses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L269) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChargingStation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L270) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartArea = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L271) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartBar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L272) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartColumn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L273) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartDiagram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L274) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartGantt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L275) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L276) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartPie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L277) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L278) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Check = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L279) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CheckDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L280) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CheckToSlot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L281) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cheese = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L282) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chess = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L283) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessBishop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L284) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessBoard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L285) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessKing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L286) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessKnight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L287) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessPawn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L288) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessQueen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L289) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessRook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L290) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L291) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L292) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L293) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L294) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Child = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L295) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildCombatant = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L296) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L297) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildReaching = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L298) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Children = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L299) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Church = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L300) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Circle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L301) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L302) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L303) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L304) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L305) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L306) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L307) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L308) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L309) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L310) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDollarToSlot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L311) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L312) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L313) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L314) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleH = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L315) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleHalfStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L316) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleInfo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L317) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L318) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L319) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L320) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleNotch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L321) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePause = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L322) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePlay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L323) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L324) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L325) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleRadiation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L326) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L327) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleStop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L328) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L329) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L330) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L331) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.City = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L332) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clapperboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L333) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clipboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L334) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L335) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L336) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L337) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L338) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L339) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClockRotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L340) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L341) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClosedCaptioning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L342) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cloud = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L343) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L344) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L345) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L346) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMeatball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L347) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMoon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L348) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMoonRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L349) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L350) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudShowersHeavy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L351) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudShowersWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L352) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudSun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L353) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudSunRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L354) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clover = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L355) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Code = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L356) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeBranch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L357) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeCommit = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L358) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeCompare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L359) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeFork = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L360) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeMerge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L361) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodePullRequest = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L362) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Coins = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L363) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ColonSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L364) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Comment = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L365) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L366) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L367) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L368) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L369) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Comments = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L372) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentsDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L373) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L370) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentSms = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L371) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CompactDisc = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L374) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Compass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L375) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CompassDrafting = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L376) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Compress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L377) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Computer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L378) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ComputerMouse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L379) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cookie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L380) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CookieBite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L381) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Copy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L382) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Copyright = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L383) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Couch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L384) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L385) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CreditCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L386) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L387) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CropSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L388) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cross = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L389) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crosshairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L390) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L391) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L392) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crutch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L393) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CruzeiroSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L394) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cube = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L395) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cubes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L396) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CubesStacked = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L397) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.D = "D"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L398) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Database = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L399) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DeleteLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L400) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Democrat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L401) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Desktop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L402) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dharmachakra = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L403) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramNext = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L404) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramPredecessor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L405) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramProject = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L406) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramSuccessor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L407) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Diamond = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L408) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiamondTurnRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L409) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L410) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceD20 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L411) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceD6 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L412) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceFive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L413) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceFour = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L414) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceOne = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L415) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceSix = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L416) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceThree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L417) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceTwo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L418) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Disease = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L419) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Display = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L420) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Divide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L421) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dna = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L422) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L423) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DollarSign = "$"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L424) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dolly = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L425) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DongSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L426) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DoorClosed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L427) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DoorOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L428) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dove = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L429) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DownLeftAndUpRightToCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L430) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Download = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L432) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DownLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L431) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dragon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L433) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrawPolygon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L434) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Droplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L435) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DropletSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L436) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Drum = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L437) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrumSteelpan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L438) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrumstickBite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L439) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dumbbell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L440) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dumpster = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L441) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DumpsterFire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L442) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dungeon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L443) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.E = "E"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L444) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarDeaf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L445) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarListen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L446) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAfrica = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L447) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAmericas = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L448) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAsia = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L449) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthEurope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L450) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthOceania = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L451) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Egg = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L452) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eject = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L453) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Elevator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L454) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ellipsis = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L455) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EllipsisVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L456) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Envelope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L457) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L458) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L459) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeOpenText = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L460) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopesBulk = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L461) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Equals = "="`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L462) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eraser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L463) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ethernet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L464) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EuroSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L465) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Exclamation = "!"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L466) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Expand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L467) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Explosion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L468) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L469) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeDropper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L470) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeLowVision = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L471) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L472) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.F = "F"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L473) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceAngry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L474) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceDizzy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L475) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFlushed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L476) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFrown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L477) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFrownOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L478) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrimace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L479) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L480) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L481) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinBeamSweat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L482) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinHearts = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L483) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L484) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinSquintTears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L485) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinStars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L486) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L487) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L488) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongueSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L489) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongueWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L490) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L491) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L492) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKiss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L493) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKissBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L494) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKissWinkHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L495) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaugh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L496) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L497) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L498) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L499) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceMeh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L500) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceMehBlank = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L501) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceRollingEyes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L502) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSadCry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L503) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSadTear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L504) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L505) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmileBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L506) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmileWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L507) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSurprise = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L508) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceTired = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L509) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L510) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Faucet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L511) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaucetDrip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L512) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fax = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L513) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Feather = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L514) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FeatherPointed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L515) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ferry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L516) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.File = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L517) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L518) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L519) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileAudio = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L520) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L521) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L522) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L523) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L524) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L525) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L526) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L527) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileContract = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L528) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCsv = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L529) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileExcel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L530) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileExport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L531) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileFragment = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L532) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileHalfDashed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L533) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileImage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L534) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileImport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L535) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileInvoice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L536) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileInvoiceDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L537) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L538) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L539) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePdf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L540) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L541) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePowerpoint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L542) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePrescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L543) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L544) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileSignature = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L545) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileVideo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L546) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileWaveform = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L547) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileWord = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L548) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileZipper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L549) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fill = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L550) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FillDrip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L551) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Film = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L552) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Filter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L553) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilterCircleDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L554) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilterCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L555) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fingerprint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L556) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L557) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireBurner = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L558) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireExtinguisher = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L559) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireFlameCurved = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L560) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireFlameSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L561) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fish = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L562) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FishFins = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L563) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Flag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L564) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlagCheckered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L565) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlagUsa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L566) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Flask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L567) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlaskVial = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L568) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FloppyDisk = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L569) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlorinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L570) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Folder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L571) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderClosed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L572) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L573) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L574) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L575) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderTree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L576) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Font = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L577) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FontAwesome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L578) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Football = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L579) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Forward = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L580) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ForwardFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L581) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ForwardStep = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L582) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FrancSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L583) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Frog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L584) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Futbol = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L585) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.G = "G"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L586) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gamepad = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L587) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GasPump = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L588) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gauge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L589) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L590) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L591) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeSimpleHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L592) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gavel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L593) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L594) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L595) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gem = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L596) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Genderless = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L597) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ghost = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L598) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gift = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L599) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gifts = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L600) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Glasses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L603) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GlassWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L601) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GlassWaterDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L602) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Globe = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L604) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GolfBallTee = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L605) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gopuram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L606) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GraduationCap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L607) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GreaterThan = ">"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L608) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GreaterThanEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L609) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Grip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L610) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L611) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripLinesVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L612) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L613) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GroupArrowsRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L614) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GuaraniSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L615) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Guitar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L616) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L617) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.H = "H"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L618) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hammer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L619) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hamsa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L620) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L621) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandBackFist = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L622) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Handcuffs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L642) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L623) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandFist = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L624) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHolding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L625) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L626) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L627) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingHand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L628) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L629) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L630) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandLizard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L631) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandMiddleFinger = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L632) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPeace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L633) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L634) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L638) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L635) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L636) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L637) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hands = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L643) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsAslInterpreting = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L644) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsBound = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L645) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsBubbles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L646) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandScissors = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L639) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsClapping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L647) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Handshake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L652) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeAngle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L653) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L654) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSimpleSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L655) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L656) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHolding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L648) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHoldingChild = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L649) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHoldingCircle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L650) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L640) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandSpock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L641) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsPraying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L651) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hanukiah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L657) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HardDrive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L658) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hashtag = "#"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L659) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatCowboy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L660) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatCowboySide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L661) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatWizard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L662) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Heading = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L667) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Headphones = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L668) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadphonesSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L669) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Headset = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L670) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideCough = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L663) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideCoughSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L664) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideMask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L665) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L666) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Heart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L671) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L672) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L673) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L674) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L675) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L676) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L677) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L678) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartPulse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L679) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Helicopter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L680) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelicopterSymbol = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L681) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelmetSafety = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L682) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelmetUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L683) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HexagonNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L684) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HexagonNodesBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L685) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Highlighter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L686) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HillAvalanche = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L687) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HillRockslide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L688) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hippo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L689) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HockeyPuck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L690) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HollyBerry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L691) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Horse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L692) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HorseHead = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L693) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hospital = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L694) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HospitalUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L695) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hotdog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L697) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hotel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L698) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HotTubPerson = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L696) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hourglass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L699) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassEnd = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L700) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L701) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassStart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L702) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.House = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L703) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimney = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L704) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L705) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L706) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L707) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyWindow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L708) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L709) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L710) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L711) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L712) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L713) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L714) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFloodWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L715) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFloodWaterCircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L716) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseLaptop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L717) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L718) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L719) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L720) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L721) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L722) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L723) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseSignal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L724) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseTsunami = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L725) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L726) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HryvniaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L727) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hurricane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L728) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.I = "I"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L729) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IceCream = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L731) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Icicles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L732) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Icons = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L733) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ICursor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L730) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdBadge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L734) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L735) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdCardClip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L736) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Igloo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L737) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Image = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L738) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ImagePortrait = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L739) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Images = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L740) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Inbox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L741) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Indent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L742) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IndianRupeeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L743) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Industry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L744) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Infinity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L745) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Info = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L746) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Italic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L747) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.J = "J"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L748) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Jar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L749) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JarWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L750) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Jedi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L751) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JetFighter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L752) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JetFighterUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L753) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Joint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L754) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JugDetergent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L755) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.K = "K"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L756) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Kaaba = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L757) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Key = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L758) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Keyboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L759) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Khanda = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L760) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KipSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L761) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KitchenSet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L763) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KitMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L762) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KiwiBird = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L764) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.L = "L"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L765) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Landmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L767) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandmarkDome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L768) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandmarkFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L769) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandMineOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L766) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Language = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L770) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Laptop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L771) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopCode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L772) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopFile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L773) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L774) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LariSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L775) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LayerGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L776) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Leaf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L777) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LeftLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L778) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L779) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lemon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L780) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LessThan = "<"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L781) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LessThanEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L782) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LifeRing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L783) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lightbulb = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L784) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LinesLeaning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L785) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Link = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L786) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LinkSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L787) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LiraSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L788) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.List = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L789) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L790) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListOl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L791) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListUl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L792) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LitecoinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L793) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationArrow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L794) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationCrosshairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L795) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L796) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationPin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L797) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationPinLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L798) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L799) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LockOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L800) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Locust = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L801) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lungs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L802) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LungsVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L803) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.M = "M"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L804) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Magnet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L805) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L806) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L807) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassChart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L808) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L809) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassLocation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L810) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L811) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L812) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ManatSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L813) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Map = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L814) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapLocation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L815) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapLocationDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L816) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapPin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L817) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Marker = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L818) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L819) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsAndVenus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L820) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsAndVenusBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L821) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L822) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L823) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStrokeRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L824) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStrokeUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L825) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L826) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlassCitrus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L827) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlassEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L828) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L829) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MaskFace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L830) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MasksTheater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L832) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MaskVentilator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L831) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MattressPillow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L833) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Maximize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L834) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Medal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L835) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Memory = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L836) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Menorah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L837) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mercury = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L838) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Message = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L839) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Meteor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L840) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microchip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L841) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microphone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L842) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L843) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneLinesSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L844) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L845) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microscope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L846) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MillSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L847) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Minimize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L848) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Minus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L849) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mitten = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L850) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mobile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L851) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L852) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileRetro = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L853) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileScreen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L854) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileScreenButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L855) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L856) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill1 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L857) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill1Wave = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L858) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L863) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillTransfer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L859) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillTrendUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L860) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillWave = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L861) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L862) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L864) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyCheckDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L865) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Monument = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L866) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Moon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L867) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MortarPestle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L868) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mosque = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L869) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mosquito = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L870) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MosquitoNet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L871) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Motorcycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L872) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mound = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L873) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mountain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L874) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MountainCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L875) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MountainSun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L876) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MugHot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L877) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MugSaucer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L878) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Music = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L879) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.N = "N"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L880) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NairaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L881) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NetworkWired = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L882) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Neuter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L883) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Newspaper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L884) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Notdef = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L886) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NotEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L885) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NotesMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L888) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NoteSticky = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L887) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num0 = "0"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L16) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num1 = "1"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L17) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num2 = "2"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L18) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num3 = "3"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L19) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num4 = "4"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L20) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num5 = "5"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L21) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num6 = "6"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L22) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num7 = "7"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L23) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num8 = "8"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L24) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num9 = "9"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L25) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.O = "O"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L889) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ObjectGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L890) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ObjectUngroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L891) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.OilCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L892) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.OilWell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L893) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Om = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L894) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Otter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L895) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Outdent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L896) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.P = "P"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L897) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pager = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L898) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paintbrush = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L900) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PaintRoller = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L899) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Palette = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L901) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pallet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L902) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Panorama = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L903) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paperclip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L905) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PaperPlane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L904) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ParachuteBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L906) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paragraph = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L907) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Passport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L908) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paste = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L909) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pause = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L910) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paw = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L911) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Peace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L912) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L913) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pencil = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L919) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenClip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L914) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenFancy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L915) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenNib = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L916) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenRuler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L917) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenToSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L918) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleArrows = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L920) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleCarryBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L921) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L922) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L923) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeoplePulling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L924) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleRobbery = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L925) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleRoof = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L926) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PepperHot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L927) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Percent = "%"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L928) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Person = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L929) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonArrowDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L930) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonArrowUpFromLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L931) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBiking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L932) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBooth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L933) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBreastfeeding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L934) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L935) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L936) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonChalkboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L937) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L938) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L939) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L940) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L941) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L942) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L943) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDigging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L944) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDotsFromLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L945) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L946) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDressBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L947) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDrowning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L948) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonFalling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L949) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonFallingBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L950) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHalfDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L951) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHarassing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L952) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHiking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L953) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryPointing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L954) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryRifle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L955) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryToPerson = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L956) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonPraying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L957) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonPregnant = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L958) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L959) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRifle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L960) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRunning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L961) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonShelter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L962) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkating = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L963) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkiing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L964) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkiingNordic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L965) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSnowboarding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L966) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSwimming = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L967) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonThroughWindow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L968) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L969) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingArrowLoopLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L970) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L971) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingDashedLineArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L972) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingLuggage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L973) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingWithCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L974) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PesetaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L975) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PesoSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L976) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Phone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L977) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L978) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L979) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneVolume = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L980) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhotoFilm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L981) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PiggyBank = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L982) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L983) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PizzaSlice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L984) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaceOfWorship = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L985) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L986) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneArrival = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L987) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L988) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L989) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L990) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneDeparture = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L991) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L992) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L993) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L994) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlantWilt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L995) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlateWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L996) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Play = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L997) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L998) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L999) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1000) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1001) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1002) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1003) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1004) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plus = "+"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1005) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlusMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1006) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Podcast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1007) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Poo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1008) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Poop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1010) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PooStorm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1009) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PowerOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1011) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Prescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1012) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PrescriptionBottle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1013) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PrescriptionBottleMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1014) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Print = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1015) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PumpMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1016) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PumpSoap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1017) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PuzzlePiece = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1018) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Q = "Q"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1019) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Qrcode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1020) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Question = "?"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1021) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.QuoteLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1022) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.QuoteRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1023) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.R = "R"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1024) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Radiation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1025) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Radio = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1026) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rainbow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1027) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RankingStar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1028) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Receipt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1029) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RecordVinyl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1030) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleAd = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1031) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1032) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1033) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Recycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1034) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Registered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1035) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Repeat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1036) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Reply = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1037) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ReplyAll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1038) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Republican = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1039) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Restroom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1040) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Retweet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1041) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ribbon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1042) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1043) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1044) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1045) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightToBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1046) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ring = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1047) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Road = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1048) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadBarrier = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1049) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadBridge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1050) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1051) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1052) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1053) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1054) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadSpikes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1055) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Robot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1056) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rocket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1057) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1058) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1059) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RotateRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1060) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Route = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1061) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1062) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RubleSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1063) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1064) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ruler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1065) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerCombined = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1066) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerHorizontal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1067) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1068) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupeeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1069) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupiahSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1070) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.S = "S"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1071) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SackDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1072) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SackXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1073) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sailboat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1074) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Satellite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1075) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SatelliteDish = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1076) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleBalanced = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1077) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleUnbalanced = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1078) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleUnbalancedFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1079) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.School = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1080) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1081) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1082) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1083) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1084) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1085) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Scissors = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1086) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Screwdriver = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1087) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScrewdriverWrench = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1088) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Scroll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1089) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScrollTorah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1090) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SdCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1091) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Section = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1092) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Seedling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1093) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Server = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1094) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shader = "S"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L15) | Letter mark used for authored Shader graph assets. | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shapes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1095) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Share = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1096) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShareFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1097) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShareNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1098) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SheetPlastic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1099) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShekelSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1100) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1101) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldCat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1102) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldDog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1103) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldHalved = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1104) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1105) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1106) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ship = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1107) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shirt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1108) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShoePrints = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1109) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1110) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShopLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1111) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShopSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1112) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shower = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1113) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shrimp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1114) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shuffle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1115) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShuttleSpace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1116) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Signal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1118) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Signature = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1119) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SignHanging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1117) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SignsPost = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1120) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SimCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1121) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1122) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sitemap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1123) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Skull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1124) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SkullCrossbones = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1125) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Slash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1126) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sleigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1127) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sliders = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1128) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Smog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1129) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Smoking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1130) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowflake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1131) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowman = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1132) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowplow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1133) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Soap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1134) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Socks = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1135) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SolarPanel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1136) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1137) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SortDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1138) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SortUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1139) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1140) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SpaghettiMonsterFlying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1141) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SpellCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1142) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spider = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1143) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spinner = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1144) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Splotch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1145) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spoon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1146) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SprayCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1147) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SprayCanSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1148) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Square = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1149) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareArrowUpRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1150) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareBinary = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1151) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1152) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1153) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1154) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1155) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1156) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareEnvelope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1157) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1158) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareH = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1159) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1160) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareNfi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1161) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareParking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1162) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1163) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePersonConfined = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1164) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePhone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1165) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePhoneFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1166) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1167) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePollHorizontal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1168) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePollVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1169) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareRootVariable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1170) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareRss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1171) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareShareNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1172) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareUpRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1173) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1174) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1175) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StaffSnake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1176) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1177) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stamp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1178) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stapler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1179) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Star = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1180) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarAndCrescent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1181) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1182) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarHalfStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1183) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarOfDavid = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1184) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarOfLife = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1185) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SterlingSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1186) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stethoscope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1187) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1188) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stopwatch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1189) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stopwatch20 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1190) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Store = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1191) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StoreSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1192) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StreetView = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1193) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Strikethrough = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1194) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stroopwafel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1195) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Subscript = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1196) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Suitcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1197) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SuitcaseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1198) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SuitcaseRolling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1199) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1200) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SunPlantWilt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1201) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Superscript = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1202) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Swatchbook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1203) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Synagogue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1204) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Syringe = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1205) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.T = "T"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1206) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Table = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1207) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCells = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1208) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsColumnLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1209) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsLarge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1210) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsRowLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1211) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsRowUnlock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1212) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableColumns = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1213) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1214) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tablet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1216) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TabletButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1217) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableTennisPaddleBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1215) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tablets = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1219) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TabletScreenButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1218) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TachographDigital = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1220) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1221) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tags = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1222) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tape = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1223) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tarp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1224) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TarpDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1225) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Taxi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1226) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Teeth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1227) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TeethOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1228) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1229) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1230) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1231) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1232) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1233) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1234) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureLow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1235) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureQuarter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1236) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureThreeQuarters = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1237) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TengeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1238) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1239) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1240) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1241) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowsDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1243) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowTurnLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1242) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tents = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1244) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Terminal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1245) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextHeight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1246) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1247) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextWidth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1248) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Thermometer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1249) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbsDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1250) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbsUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1251) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Thumbtack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1252) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbtackSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1253) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ticket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1254) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TicketSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1255) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Timeline = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1256) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToggleOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1257) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToggleOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1258) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Toilet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1259) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPaper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1260) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPaperSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1261) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPortable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1262) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletsPortable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1263) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Toolbox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1264) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tooth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1265) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToriiGate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1266) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tornado = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1267) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerBroadcast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1268) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerCell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1269) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerObservation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1270) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tractor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1271) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trademark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1272) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrafficLight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1273) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trailer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1274) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Train = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1275) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrainSubway = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1276) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrainTram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1277) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Transgender = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1278) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1279) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1280) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1281) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashCanArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1282) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1283) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TreeCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1284) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TriangleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1285) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trophy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1286) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trowel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1287) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrowelBricks = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1288) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Truck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1289) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1290) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1291) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1292) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckField = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1293) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFieldUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1294) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFront = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1295) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1296) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMonster = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1297) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMoving = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1298) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckPickup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1299) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckPlane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1300) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckRampBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1301) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1302) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurkishLiraSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1303) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurnDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1304) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurnUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1305) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tv = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1306) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.U = "U"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1307) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Umbrella = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1308) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UmbrellaBeach = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1309) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Underline = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1310) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UniversalAccess = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1311) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Unlock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1312) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UnlockKeyhole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1313) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1314) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpDownLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1315) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Upload = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1319) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1316) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpRightAndDownLeftFromCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1317) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpRightFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1318) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.User = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1320) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserAstronaut = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1321) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1322) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserClock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1323) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserDoctor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1324) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1325) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGraduate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1326) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1327) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserInjured = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1328) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLarge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1329) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLargeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1330) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1331) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1332) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserNinja = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1333) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserNurse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1334) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserPen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1335) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1336) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Users = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1343) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersBetweenLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1344) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserSecret = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1337) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersGear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1345) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1338) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1339) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1346) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersRays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1347) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersRectangle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1348) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1349) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersViewfinder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1350) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserTag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1340) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserTie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1341) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1342) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Utensils = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1351) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.V = "V"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1352) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VanShuttle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1353) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vault = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1354) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VectorSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1355) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Venus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1356) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VenusDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1357) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VenusMars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1358) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vest = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1359) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VestPatches = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1360) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vial = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1361) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VialCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1362) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vials = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1364) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VialVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1363) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Video = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1365) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VideoSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1366) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vihara = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1367) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Virus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1368) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusCovid = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1369) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusCovidSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1370) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Viruses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1372) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1371) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Voicemail = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1373) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Volcano = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1374) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Volleyball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1375) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1376) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeLow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1377) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1378) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1379) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VrCardboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1380) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.W = "W"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1381) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WalkieTalkie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1382) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wallet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1383) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandMagic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1384) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandMagicSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1385) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1386) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Warehouse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1387) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Water = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1388) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WaterLadder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1389) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WaveSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1390) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WebAwesome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1391) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WeightHanging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1392) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WeightScale = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1393) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheatAwn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1394) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheatAwnCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1395) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wheelchair = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1396) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheelchairMove = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1397) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WhiskeyGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1398) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wifi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1399) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wind = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1400) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowMaximize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1401) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowMinimize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1402) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowRestore = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1403) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineBottle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1404) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1405) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineGlassEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1406) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WonSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1407) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Worm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1408) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wrench = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1409) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.X = "X"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1410) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Xmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1412) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.XmarksLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1413) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.XRay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1411) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Y = "Y"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1414) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.YenSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1415) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.YinYang = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1416) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Z = "Z"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1417) | 编译期常量;值及类型见当前声明。 | — | — | -| [`public static class Inno.Adapter.Presentation.ImGui.ImGuiIcon`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L12) | — | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions.ImGuiInteractionOptions(bool commandKeyBehavior, int horizontalWheelDirection)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L22) | Captures interaction conventions without inferring them from the current operating system.
`commandKeyBehavior`:Whether ImGui should use Command-based editing and selection conventions.
`horizontalWheelDirection`:Either one or minus one, applied to the platform event's horizontal wheel value. | — | `System.ArgumentOutOfRangeException`:The wheel direction is not one or minus one. | +| [`public sealed class Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L8) | Freezes product-selected keyboard conventions and horizontal wheel interpretation. | — | — | +| [`public Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions.ImGuiInteractionOptions(bool commandKeyBehavior, int horizontalWheelDirection)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L22) | Captures interaction conventions without inferring them from the current operating system.
`commandKeyBehavior`: Whether ImGui should use Command-based editing and selection conventions.
`horizontalWheelDirection`: Either one or minus one, applied to the platform event's horizontal wheel value. | — | `System.ArgumentOutOfRangeException`: The wheel direction is not one or minus one. | | [`public bool Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions.commandKeyBehavior { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L35) | Gets the explicitly chosen editing and selection conventions. | — | — | | [`public int Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions.horizontalWheelDirection { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L40) | Gets the horizontal platform wheel multiplier. | — | — | -| [`public sealed class Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/ImGuiInteractionOptions.cs#L8) | Freezes product-selected keyboard conventions and horizontal wheel interpretation. | — | — | - -### `Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.ImGuiTextureHandle(ulong value)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L19) | Creates an opaque texture token allocated by an ImGui presentation backend.
`value`:Non-zero backend token. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.Equals(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle other)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.Equals(object obj)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.GetHashCode()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.operator ==(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle left, Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle right)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.operator !=(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle left, Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle right)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.ToString()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.isValid { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L37) | Gets whether this token identifies a registered texture. | — | — | -| [`public ulong Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.value { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L32) | Gets the opaque token consumed only by the active ImGui backend. | — | — | | [`public readonly record struct Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle : System.IEquatable`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Identifies a backend-owned texture through an opaque ImGui token instead of a native graphics handle. | — | — | - -### `Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer` - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.CreateViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L123) | Creates backend presentation state for one detached viewport.
`target`:New viewport target. | — | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.DrainViewportRetirements()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L175) | Drains requested retirements at a closed-frame owner-thread point outside native UI callbacks. | Failure or timeout retains all unfinished viewport and renderer owners. | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.PresentViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L153) | Marks one detached viewport ready for presentation.
`target`:Viewport destination. | — | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RenderMain(nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L101) | Consumes current main-window draw data before the next ImGui frame begins.
`drawData`:Opaque native ImDrawData pointer valid for the current frame. | — | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RenderViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target, nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L142) | Consumes draw data for one detached viewport.
`target`:Viewport destination.
`drawData`:Opaque native ImDrawData pointer valid for the current frame. | — | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.ResizeViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L131) | Resizes backend presentation state for one detached viewport.
`target`:Updated viewport target. | — | — | -| [`System.Threading.Tasks.Task Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RetireViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L167) | Removes the viewport from future rendering and schedules release of its borrowed native window.
返回:A task completed only when the renderer no longer uses the native window. This is not a GPU fence.
`target`:The viewport whose original owner keeps its native window alive during retirement. | Native callbacks may request retirement but must not wait or advance rendering. | — | -| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.SynchronizeMainOutput(int pixelWidth, int pixelHeight)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L112) | Synchronizes the primary drawable after a platform resize.
`pixelWidth`:Current drawable width in physical pixels.
`pixelHeight`:Current drawable height in physical pixels. | — | — | -| [`bool Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.supportsViewports { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L93) | Gets whether detached viewport windows are rendered by this backend. | — | — | -| [`public interface Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer : System.IDisposable`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L88) | Presents ImGui draw data through a replaceable renderer while platform input and windows stay shared. | — | — | - -### `Inno.Adapter.Presentation.ImGui.PlatformImGuiContext` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L130) | Releases the resources owned by this instance. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.DrawImage(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle texture, System.Numerics.Vector2 size, System.Numerics.Vector2 uv0 = default(System.Numerics.Vector2), System.Numerics.Vector2 uv1 = default(System.Numerics.Vector2))`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L120) | Renders the image presentation for the current editor frame.
`texture`:The texture consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`size`:The size consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`uv0`:The uv0 consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`uv1`:The uv1 consumed by draw image; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.LoadIniSettings(string? settings)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L72) | Loads the current editor layout text into Dear ImGui without retaining the source string.
`settings`:The validated configuration that controls this operation. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.RegisterFontStyle(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style, string filePath, float fontSizePixels = 16)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L46) | Registers a named font style and its glyph ranges in the active ImGui font atlas.
`style`:The style consumed by register font style; ownership remains with the caller unless explicitly stated otherwise.
`filePath`:The file path text validated by the register font style operation.
`fontSizePixels`:The font size pixels consumed by register font style; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public nint Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.RenderFrame(System.Action drawFrame)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L100) | Records frame rendering for the current frame.
返回:The validated int ptr that represents the completed operation.
`drawFrame`:The callback invoked by render frame within the operation's owned lifetime. | — | — | -| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.SetIniFile(string? filePath)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L61) | Updates the ini file state and applies the resulting invariants.
`filePath`:The file path text validated by the set ini file operation. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.TryCaptureIniSettings(out string settings, bool force = false)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L86) | Attempts to capture ini settings without changing state when the operation cannot complete.
返回:true when the requested condition is satisfied; otherwise, false.
`settings`:The validated configuration that controls this operation.
`force`:Whether force behavior is enabled while try capture ini settings executes. | — | — | -| [`public bool Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.TryGetWindowId(uint viewportId, out uint windowId)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L26) | Resolves the SDL window currently owned by an ImGui viewport.
返回:true when the viewport has a live SDL window.
`viewportId`:The ImGui viewport identity.
`windowId`:The SDL window identity when available. | — | `System.ObjectDisposedException`:Thrown after the presentation context has been disposed. | -| [`public sealed class Inno.Adapter.Presentation.ImGui.PlatformImGuiContext : System.IDisposable`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L9) | Manages Dear ImGui frame lifecycle and rendering integration for a platform window. | — | — | - -### `Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public int Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.height { get; internal set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L82) | Gets the current framebuffer height. | — | — | -| [`public Inno.Adapter.Platform.PlatformNativeHandles Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.nativeHandles { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L72) | Gets backend-neutral native-window integration handles. | — | — | +| [`public Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.ImGuiTextureHandle(ulong value)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L19) | Creates an opaque texture token allocated by an ImGui presentation backend.
`value`: Non-zero backend token. | — | — | +| [`public ulong Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.value { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L32) | Gets the opaque token consumed only by the active ImGui backend. | — | — | +| [`public bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.isValid { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L37) | Gets whether this token identifies a registered texture. | — | — | +| [`public override string Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.ToString()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.operator !=(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle left, Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle right)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.operator ==(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle left, Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle right)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.GetHashCode()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.Equals(object obj)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle.Equals(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle other)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L11) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L43) | Describes one detached ImGui viewport without exposing SDL or graphics-backend handles. | — | — | | [`public uint Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.viewportId { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L62) | Gets the stable Dear ImGui viewport identity. | — | — | -| [`public int Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.width { get; internal set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L77) | Gets the current framebuffer width. | — | — | | [`public uint Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.windowId { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L67) | Gets the platform window identity. | — | — | -| [`public sealed class Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L43) | Describes one detached ImGui viewport without exposing SDL or graphics-backend handles. | — | — | - -### `Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Adapter.Presentation.ImGui.PlatformImGuiContext Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions.CreateImGuiContext(Inno.Adapter.Platform.Sdl3.Sdl3PlatformApplication application, Inno.Adapter.Platform.Sdl3.Sdl3PlatformWindow window, Inno.Adapter.Presentation.ImGui.ImGuiContextFlags contextFlags, Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions interaction, Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer? renderer = null)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L98) | Creates one ImGui context and transfers the supplied renderer ownership only after successful creation.
返回:The newly created context, which owns its renderer until complete retirement and disposal.
`application`:Target platform application instance.
`window`:Target platform window.
`interaction`:Explicit immutable keyboard and wheel conventions supplied by the product.
`contextFlags`:ImGui context feature flags.
`renderer`:Optional presentation backend; ownership transfers on success and remains with the caller on failure. | — | `System.ArgumentNullException`:An application, window, or interaction configuration is null.
`System.ArgumentException`:The window does not belong to the supplied application.
`System.InvalidOperationException`:The window is closed or already owns an ImGui context. | -| [`public static void Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions.DestroyImGuiContext(Inno.Adapter.Platform.Sdl3.Sdl3PlatformApplication application, Inno.Adapter.Platform.Sdl3.Sdl3PlatformWindow window)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L141) | Retires and disposes the window's context, retaining its application registration if retirement fails.
`application`:Target platform application instance.
`window`:Target platform window. | — | `System.ArgumentNullException`:The application or window is null.
`System.InvalidOperationException`:Retirement cannot complete at the current owner-thread safe point; the context remains owned. | -| [`public static class Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L14) | Extension APIs that add ImGui integration on top of Inno.Adapter.Platform.Sdl3.Sdl3PlatformApplication. | — | — | +| [`public Inno.Adapter.Platform.PlatformNativeHandles Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.nativeHandles { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L72) | Gets backend-neutral native-window integration handles. | — | — | +| [`public int Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.width { get; internal set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L77) | Gets the current framebuffer width. | — | — | +| [`public int Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget.height { get; internal set; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L82) | Gets the current framebuffer height. | — | — | +| [`public interface Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer : System.IDisposable`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L88) | Presents ImGui draw data through a replaceable renderer while platform input and windows stay shared. | — | — | +| [`bool Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.supportsVertexOffsets { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L93) | Gets whether every draw-data consumer applies per-command vertex offsets to 16-bit indices. | — | — | +| [`bool Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.supportsViewports { get; }`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L98) | Gets whether detached viewport windows are rendered by this backend. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RenderMain(nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L106) | Consumes current main-window draw data before the next ImGui frame begins.
`drawData`: Opaque native ImDrawData pointer valid for the current frame. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.SynchronizeMainOutput(int pixelWidth, int pixelHeight)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L117) | Synchronizes the primary drawable after a platform resize.
`pixelWidth`: Current drawable width in physical pixels.
`pixelHeight`: Current drawable height in physical pixels. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.CreateViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L128) | Creates backend presentation state for one detached viewport.
`target`: New viewport target. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.ResizeViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L136) | Resizes backend presentation state for one detached viewport.
`target`: Updated viewport target. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RenderViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target, nint drawData)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L147) | Consumes draw data for one detached viewport.
`target`: Viewport destination.
`drawData`: Opaque native ImDrawData pointer valid for the current frame. | — | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.PresentViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L158) | Marks one detached viewport ready for presentation.
`target`: Viewport destination. | — | — | +| [`System.Threading.Tasks.Task Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.RetireViewport(Inno.Adapter.Presentation.ImGui.PlatformImGuiViewportTarget target)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L172) | Removes the viewport from future rendering and schedules release of its borrowed native window.
`target`: The viewport whose original owner keeps its native window alive during retirement. | A task completed only when the renderer no longer uses the native window. This is not a GPU fence. | — | +| [`void Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer.DrainViewportRetirements()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/IPlatformImGuiRenderer.Api.cs#L180) | Drains requested retirements at a closed-frame owner-thread point outside native UI callbacks. | — | — | +| [`public sealed class Inno.Adapter.Presentation.ImGui.PlatformImGuiContext : System.IDisposable`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L9) | Manages Dear ImGui frame lifecycle and rendering integration for a platform window. | — | — | +| [`public bool Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.TryGetWindowId(uint viewportId, out uint windowId)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L26) | Resolves the SDL window currently owned by an ImGui viewport.
`viewportId`: The ImGui viewport identity.
`windowId`: The SDL window identity when available. | when the viewport has a live SDL window. | `System.ObjectDisposedException`: Thrown after the presentation context has been disposed. | +| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.RegisterFontStyle(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style, string filePath, float fontSizePixels = 16)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L46) | Registers a named font style and its glyph ranges in the active ImGui font atlas.
`style`: The style consumed by register font style; ownership remains with the caller unless explicitly stated otherwise.
`filePath`: The file path text validated by the register font style operation.
`fontSizePixels`: The font size pixels consumed by register font style; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.SetIniFile(string? filePath)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L61) | Updates the ini file state and applies the resulting invariants.
`filePath`: The file path text validated by the set ini file operation. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.LoadIniSettings(string? settings)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L72) | Loads the current editor layout text into Dear ImGui without retaining the source string.
`settings`: The validated configuration that controls this operation. | — | — | +| [`public bool Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.TryCaptureIniSettings(out string settings, bool force = false)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L86) | Attempts to capture ini settings without changing state when the operation cannot complete.
`settings`: The validated configuration that controls this operation.
`force`: Whether force behavior is enabled while try capture ini settings executes. | when the requested condition is satisfied; otherwise, . | — | +| [`public nint Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.RenderFrame(System.Action drawFrame, System.Action? prepareFrame = null)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L112) | Records frame rendering for the current frame.
`drawFrame`: The callback invoked by render frame within the operation's owned lifetime.
`prepareFrame`: Optional callback invoked with the owned context before native frame creation. | Borrowed native draw data for the completed frame, valid until the next frame or retirement. | — | +| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.DrawImage(Inno.Adapter.Presentation.ImGui.ImGuiTextureHandle texture, System.Numerics.Vector2 size, System.Numerics.Vector2 uv0 = default(System.Numerics.Vector2), System.Numerics.Vector2 uv1 = default(System.Numerics.Vector2))`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L135) | Renders the image presentation for the current editor frame.
`texture`: The texture consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`size`: The size consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`uv0`: The uv0 consumed by draw image; ownership remains with the caller unless explicitly stated otherwise.
`uv1`: The uv1 consumed by draw image; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.PlatformImGuiContext.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/PlatformImGuiContext.Api.cs#L148) | Retires the context and its owned resources after the current frame operation has completed. | — | `System.InvalidOperationException`: A frame operation still owns the context; the caller must retry after that operation completes. | +| [`public static class Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L14) | Extension APIs that add ImGui integration on top of . | — | — | +| [`public static Inno.Adapter.Presentation.ImGui.PlatformImGuiContext Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions.CreateImGuiContext(Inno.Adapter.Platform.Sdl3.Sdl3PlatformApplication application, Inno.Adapter.Platform.Sdl3.Sdl3PlatformWindow window, Inno.Adapter.Presentation.ImGui.ImGuiContextFlags contextFlags, Inno.Adapter.Presentation.ImGui.ImGuiInteractionOptions interaction, Inno.Adapter.Presentation.ImGui.IPlatformImGuiRenderer? renderer = null)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L98) | Creates one ImGui context and transfers the supplied renderer ownership only after successful creation.
`application`: Target platform application instance.
`window`: Target platform window.
`interaction`: Explicit immutable keyboard and wheel conventions supplied by the product.
`contextFlags`: ImGui context feature flags.
`renderer`: Optional presentation backend; ownership transfers on success and remains with the caller on failure. | The newly created context, which owns its renderer until complete retirement and disposal. | `System.ArgumentNullException`: An application, window, or interaction configuration is null.
`System.ArgumentException`: The window does not belong to the supplied application.
`System.InvalidOperationException`: The window is closed or already owns an ImGui context. | +| [`public static void Inno.Adapter.Presentation.ImGui.Sdl3PlatformApplicationImGuiExtensions.DestroyImGuiContext(Inno.Adapter.Platform.Sdl3.Sdl3PlatformApplication application, Inno.Adapter.Platform.Sdl3.Sdl3PlatformWindow window)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs#L141) | Retires and disposes the window's context, retaining its application registration if retirement fails.
`application`: Target platform application instance.
`window`: Target platform window. | — | `System.ArgumentNullException`: The application or window is null.
`System.InvalidOperationException`: Retirement cannot complete at the current owner-thread safe point; the context remains owned. | +| [`public enum Inno.Adapter.Presentation.ImGui.ImGuiContextFlags : int`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L9) | Identifies the supported im gui context flags values for this contract. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.None = 0`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L14) | The none key. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableViewports = 1`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L18) | The enable viewports key. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableDocking = 2`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L22) | The enable docking key. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiContextFlags.EnableSmoothResize = 4`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiContextFlags.cs#L26) | The enable smooth resize key. | — | — | +| [`public static class Inno.Adapter.Presentation.ImGui.ImGuiFont`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L12) | Provides scoped access to font faces registered for the current ImGui context. | — | — | +| [`public static bool Inno.Adapter.Presentation.ImGui.ImGuiFont.IsAvailable(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L26) | Gets whether an exact font style is available in the current ImGui context.
`style`: The composable style to query. | when the exact face is registered. | — | +| [`public static Inno.Adapter.Presentation.ImGui.ImGuiFontScope Inno.Adapter.Presentation.ImGui.ImGuiFont.PushStyle(Inno.Adapter.Presentation.ImGui.ImGuiFontStyle style)`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L49) | Pushes a font style for the current ImGui context and returns a scope that restores it.
`style`: The composable style to use. | A disposable scope. Missing faces gracefully fall back to the nearest registered face. | — | +| [`public ref struct Inno.Adapter.Presentation.ImGui.ImGuiFontScope`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L157) | Restores the previous ImGui font when a scoped style ends. | — | — | +| [`public void Inno.Adapter.Presentation.ImGui.ImGuiFontScope.Dispose()`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFont.cs#L173) | Restores the font that was active before this scope. | — | — | +| [`public enum Inno.Adapter.Presentation.ImGui.ImGuiFontStyle : int`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L9) | Describes composable visual variants of an ImGui font family. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Regular = 0`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L14) | Uses the regular font face. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Bold = 1`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L19) | Uses a bold font face. | — | — | +| [`Inno.Adapter.Presentation.ImGui.ImGuiFontStyle.Italic = 2`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiFontStyle.cs#L24) | Uses an italic font face. | — | — | +| [`public static class Inno.Adapter.Presentation.ImGui.ImGuiIcon`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shader = "S"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L15) | Letter mark used for authored Shader graph assets. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num0 = "0"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num1 = "1"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num2 = "2"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num3 = "3"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L19) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num4 = "4"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L20) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num5 = "5"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L21) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num6 = "6"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L22) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num7 = "7"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L23) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num8 = "8"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L24) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Num9 = "9"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L25) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.A = "A"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L26) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AddressBook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L27) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AddressCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L28) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L29) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignJustify = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L30) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L31) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AlignRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L32) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Anchor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L33) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L34) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L35) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L36) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnchorLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L37) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L38) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L39) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L40) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AngleUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L41) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L42) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L43) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L44) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AnglesUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L45) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ankh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L46) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AppleWhole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L47) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Archway = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L48) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L49) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown19 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L50) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDown91 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L51) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownAZ = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L52) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L53) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownShortWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L54) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownUpAcrossLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L55) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownUpLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L56) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownWideShort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L57) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowDownZA = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L58) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L59) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowLeftLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L60) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowPointer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L61) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L62) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L63) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L64) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L65) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightToBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L66) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRightToCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L67) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L68) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowRotateRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L69) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTrendDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L70) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTrendUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L71) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTurnDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L72) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowTurnUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L73) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L74) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp19 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L75) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUp91 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L76) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpAZ = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L77) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L78) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromGroundWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L79) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpFromWaterPump = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L80) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L81) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpRightDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L82) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpRightFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L83) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpShortWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L84) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpWideShort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L85) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowUpZA = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L86) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L87) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsDownToPeople = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L88) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L89) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsLeftRightToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L90) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L91) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsSpin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L92) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsSplitUpAndLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L93) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToCircle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L94) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L95) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsToEye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L96) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsTurnRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L97) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsTurnToDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L98) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L99) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpDownLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L100) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ArrowsUpToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L101) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Asterisk = "*"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L102) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.At = "@"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L103) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Atom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L104) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AudioDescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L105) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.AustralSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L106) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Award = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L107) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.B = "B"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L108) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Baby = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L109) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BabyCarriage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L110) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Backward = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L111) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BackwardFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L112) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BackwardStep = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L113) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L114) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacteria = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L115) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bacterium = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L116) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BagShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L117) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bahai = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L118) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BahtSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L119) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ban = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L120) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BanSmoking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L121) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bandage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L122) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BangladeshiTakaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L123) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Barcode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L124) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L125) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BarsProgress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L126) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BarsStaggered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L127) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Baseball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L128) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BaseballBatBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L129) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BasketShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L130) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Basketball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L131) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bath = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L132) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L133) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L134) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L135) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryQuarter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L136) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BatteryThreeQuarters = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L137) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L138) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BedPulse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L139) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BeerMugEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L140) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L141) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BellConcierge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L142) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BellSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L143) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BezierCurve = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L144) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bicycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L145) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Binoculars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L146) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Biohazard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L147) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BitcoinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L148) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Blender = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L149) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BlenderPhone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L150) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Blog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L151) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bold = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L152) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L153) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoltLightning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L154) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bomb = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L155) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L156) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L157) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Book = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L158) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookAtlas = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L159) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookBible = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L160) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookBookmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L161) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookJournalWhills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L162) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L163) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L164) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookOpenReader = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L165) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookQuran = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L166) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookSkull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L167) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BookTanakh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L168) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bookmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L169) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderAll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L170) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderNone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L171) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BorderTopLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L172) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoreHole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L173) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BottleDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L174) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BottleWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L175) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlFood = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L176) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlRice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L177) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BowlingBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L178) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Box = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L179) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxArchive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L180) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L181) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxTissue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L182) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxesPacking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L183) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BoxesStacked = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L184) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Braille = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L185) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Brain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L186) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BrazilianRealSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L187) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BreadSlice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L188) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bridge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L189) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L190) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L191) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L192) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L193) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BridgeWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L194) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Briefcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L195) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BriefcaseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L196) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Broom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L197) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BroomBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L198) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Brush = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L199) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bucket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L200) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L201) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BugSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L202) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bugs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L203) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Building = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L204) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L205) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L206) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L207) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L208) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingColumns = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L209) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L210) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L211) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingNgo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L212) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L213) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L214) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L215) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BuildingWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L216) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bullhorn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L217) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bullseye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L218) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Burger = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L219) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Burst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L220) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Bus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L221) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BusSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L222) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.BusinessTime = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L223) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.C = "C"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L224) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CableCar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L225) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CakeCandles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L226) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Calculator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L227) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Calendar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L228) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L229) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarDay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L230) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarDays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L231) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L232) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L233) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarWeek = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L234) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CalendarXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L235) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Camera = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L236) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CameraRetro = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L237) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CameraRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L238) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Campground = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L239) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CandyCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L240) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cannabis = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L241) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Capsules = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L242) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Car = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L243) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarBattery = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L244) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L245) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L246) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarRear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L247) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarSide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L248) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CarTunnel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L249) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Caravan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L250) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L251) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L252) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L253) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CaretUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L254) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Carrot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L255) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L256) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartFlatbed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L257) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartFlatbedSuitcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L258) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L259) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CartShopping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L260) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CashRegister = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L261) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L262) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CediSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L263) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CentSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L264) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Certificate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L265) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chair = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L266) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chalkboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L267) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChalkboardUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L268) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChampagneGlasses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L269) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChargingStation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L270) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartArea = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L271) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartBar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L272) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartColumn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L273) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartDiagram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L274) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartGantt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L275) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L276) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartPie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L277) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChartSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L278) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Check = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L279) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CheckDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L280) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CheckToSlot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L281) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cheese = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L282) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Chess = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L283) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessBishop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L284) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessBoard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L285) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessKing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L286) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessKnight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L287) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessPawn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L288) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessQueen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L289) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChessRook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L290) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L291) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L292) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L293) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChevronUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L294) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Child = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L295) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildCombatant = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L296) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L297) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ChildReaching = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L298) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Children = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L299) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Church = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L300) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Circle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L301) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L302) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L303) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L304) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L305) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L306) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L307) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L308) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L309) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleChevronUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L310) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDollarToSlot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L311) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L312) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L313) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L314) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleH = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L315) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleHalfStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L316) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleInfo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L317) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L318) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L319) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L320) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleNotch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L321) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePause = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L322) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePlay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L323) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L324) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L325) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleRadiation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L326) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L327) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleStop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L328) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L329) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L330) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L331) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.City = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L332) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clapperboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L333) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clipboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L334) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L335) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L336) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L337) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClipboardUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L338) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L339) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClockRotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L340) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L341) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ClosedCaptioning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L342) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cloud = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L343) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L344) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L345) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L346) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMeatball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L347) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMoon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L348) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudMoonRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L349) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L350) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudShowersHeavy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L351) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudShowersWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L352) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudSun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L353) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CloudSunRain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L354) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Clover = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L355) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Code = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L356) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeBranch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L357) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeCommit = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L358) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeCompare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L359) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeFork = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L360) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodeMerge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L361) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CodePullRequest = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L362) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Coins = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L363) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ColonSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L364) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Comment = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L365) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L366) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L367) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L368) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L369) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L370) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentSms = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L371) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Comments = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L372) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CommentsDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L373) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CompactDisc = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L374) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Compass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L375) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CompassDrafting = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L376) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Compress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L377) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Computer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L378) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ComputerMouse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L379) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cookie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L380) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CookieBite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L381) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Copy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L382) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Copyright = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L383) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Couch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L384) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L385) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CreditCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L386) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L387) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CropSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L388) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cross = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L389) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crosshairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L390) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L391) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L392) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Crutch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L393) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CruzeiroSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L394) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cube = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L395) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Cubes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L396) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.CubesStacked = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L397) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.D = "D"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L398) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Database = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L399) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DeleteLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L400) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Democrat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L401) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Desktop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L402) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dharmachakra = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L403) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramNext = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L404) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramPredecessor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L405) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramProject = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L406) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiagramSuccessor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L407) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Diamond = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L408) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiamondTurnRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L409) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L410) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceD20 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L411) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceD6 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L412) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceFive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L413) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceFour = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L414) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceOne = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L415) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceSix = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L416) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceThree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L417) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DiceTwo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L418) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Disease = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L419) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Display = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L420) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Divide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L421) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dna = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L422) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L423) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DollarSign = "$"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L424) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dolly = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L425) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DongSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L426) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DoorClosed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L427) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DoorOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L428) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dove = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L429) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DownLeftAndUpRightToCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L430) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DownLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L431) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Download = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L432) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dragon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L433) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrawPolygon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L434) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Droplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L435) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DropletSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L436) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Drum = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L437) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrumSteelpan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L438) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DrumstickBite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L439) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dumbbell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L440) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dumpster = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L441) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.DumpsterFire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L442) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Dungeon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L443) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.E = "E"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L444) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarDeaf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L445) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarListen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L446) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAfrica = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L447) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAmericas = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L448) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthAsia = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L449) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthEurope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L450) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EarthOceania = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L451) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Egg = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L452) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eject = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L453) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Elevator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L454) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ellipsis = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L455) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EllipsisVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L456) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Envelope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L457) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L458) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L459) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopeOpenText = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L460) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EnvelopesBulk = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L461) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Equals = "="`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L462) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eraser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L463) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ethernet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L464) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EuroSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L465) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Exclamation = "!"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L466) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Expand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L467) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Explosion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L468) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Eye = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L469) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeDropper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L470) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeLowVision = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L471) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.EyeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L472) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.F = "F"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L473) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceAngry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L474) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceDizzy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L475) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFlushed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L476) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFrown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L477) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceFrownOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L478) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrimace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L479) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L480) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L481) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinBeamSweat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L482) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinHearts = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L483) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L484) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinSquintTears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L485) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinStars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L486) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L487) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L488) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongueSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L489) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinTongueWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L490) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinWide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L491) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceGrinWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L492) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKiss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L493) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKissBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L494) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceKissWinkHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L495) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaugh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L496) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L497) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughSquint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L498) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceLaughWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L499) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceMeh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L500) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceMehBlank = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L501) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceRollingEyes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L502) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSadCry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L503) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSadTear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L504) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L505) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmileBeam = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L506) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSmileWink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L507) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceSurprise = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L508) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaceTired = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L509) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L510) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Faucet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L511) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FaucetDrip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L512) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fax = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L513) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Feather = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L514) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FeatherPointed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L515) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ferry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L516) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.File = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L517) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L518) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L519) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileAudio = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L520) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L521) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L522) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L523) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L524) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L525) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L526) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L527) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileContract = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L528) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileCsv = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L529) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileExcel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L530) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileExport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L531) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileFragment = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L532) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileHalfDashed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L533) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileImage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L534) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileImport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L535) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileInvoice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L536) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileInvoiceDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L537) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L538) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L539) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePdf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L540) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L541) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePowerpoint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L542) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilePrescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L543) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L544) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileSignature = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L545) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileVideo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L546) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileWaveform = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L547) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileWord = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L548) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FileZipper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L549) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fill = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L550) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FillDrip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L551) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Film = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L552) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Filter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L553) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilterCircleDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L554) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FilterCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L555) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fingerprint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L556) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L557) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireBurner = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L558) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireExtinguisher = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L559) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireFlameCurved = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L560) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FireFlameSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L561) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Fish = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L562) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FishFins = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L563) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Flag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L564) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlagCheckered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L565) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlagUsa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L566) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Flask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L567) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlaskVial = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L568) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FloppyDisk = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L569) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FlorinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L570) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Folder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L571) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderClosed = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L572) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L573) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L574) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L575) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FolderTree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L576) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Font = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L577) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FontAwesome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L578) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Football = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L579) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Forward = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L580) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ForwardFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L581) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ForwardStep = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L582) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.FrancSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L583) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Frog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L584) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Futbol = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L585) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.G = "G"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L586) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gamepad = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L587) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GasPump = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L588) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gauge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L589) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L590) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L591) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GaugeSimpleHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L592) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gavel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L593) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L594) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gears = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L595) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gem = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L596) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Genderless = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L597) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ghost = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L598) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gift = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L599) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gifts = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L600) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GlassWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L601) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GlassWaterDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L602) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Glasses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L603) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Globe = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L604) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GolfBallTee = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L605) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gopuram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L606) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GraduationCap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L607) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GreaterThan = ">"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L608) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GreaterThanEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L609) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Grip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L610) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L611) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripLinesVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L612) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GripVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L613) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GroupArrowsRotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L614) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.GuaraniSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L615) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Guitar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L616) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Gun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L617) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.H = "H"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L618) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hammer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L619) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hamsa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L620) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L621) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandBackFist = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L622) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandDots = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L623) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandFist = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L624) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHolding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L625) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L626) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L627) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingHand = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L628) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L629) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandHoldingMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L630) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandLizard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L631) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandMiddleFinger = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L632) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPeace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L633) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L634) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L635) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L636) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L637) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandPointer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L638) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandScissors = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L639) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L640) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandSpock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L641) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Handcuffs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L642) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hands = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L643) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsAslInterpreting = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L644) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsBound = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L645) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsBubbles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L646) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsClapping = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L647) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHolding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L648) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHoldingChild = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L649) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsHoldingCircle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L650) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandsPraying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L651) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Handshake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L652) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeAngle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L653) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L654) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSimpleSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L655) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HandshakeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L656) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hanukiah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L657) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HardDrive = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L658) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hashtag = "#"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L659) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatCowboy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L660) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatCowboySide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L661) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HatWizard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L662) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideCough = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L663) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideCoughSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L664) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideMask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L665) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadSideVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L666) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Heading = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L667) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Headphones = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L668) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeadphonesSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L669) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Headset = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L670) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Heart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L671) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L672) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L673) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L674) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L675) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L676) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L677) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L678) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HeartPulse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L679) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Helicopter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L680) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelicopterSymbol = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L681) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelmetSafety = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L682) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HelmetUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L683) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HexagonNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L684) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HexagonNodesBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L685) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Highlighter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L686) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HillAvalanche = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L687) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HillRockslide = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L688) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hippo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L689) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HockeyPuck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L690) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HollyBerry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L691) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Horse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L692) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HorseHead = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L693) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hospital = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L694) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HospitalUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L695) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HotTubPerson = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L696) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hotdog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L697) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hotel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L698) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hourglass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L699) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassEnd = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L700) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L701) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HourglassStart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L702) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.House = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L703) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimney = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L704) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L705) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L706) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L707) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseChimneyWindow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L708) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L709) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L710) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L711) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseCrack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L712) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFire = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L713) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L714) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFloodWater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L715) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseFloodWaterCircleArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L716) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseLaptop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L717) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L718) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L719) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L720) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L721) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L722) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseMedicalFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L723) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseSignal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L724) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseTsunami = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L725) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HouseUser = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L726) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.HryvniaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L727) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Hurricane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L728) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.I = "I"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L729) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ICursor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L730) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IceCream = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L731) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Icicles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L732) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Icons = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L733) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdBadge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L734) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L735) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IdCardClip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L736) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Igloo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L737) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Image = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L738) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ImagePortrait = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L739) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Images = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L740) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Inbox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L741) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Indent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L742) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.IndianRupeeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L743) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Industry = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L744) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Infinity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L745) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Info = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L746) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Italic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L747) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.J = "J"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L748) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Jar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L749) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JarWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L750) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Jedi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L751) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JetFighter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L752) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JetFighterUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L753) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Joint = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L754) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.JugDetergent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L755) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.K = "K"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L756) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Kaaba = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L757) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Key = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L758) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Keyboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L759) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Khanda = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L760) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KipSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L761) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KitMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L762) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KitchenSet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L763) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.KiwiBird = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L764) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.L = "L"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L765) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandMineOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L766) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Landmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L767) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandmarkDome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L768) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LandmarkFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L769) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Language = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L770) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Laptop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L771) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopCode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L772) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopFile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L773) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LaptopMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L774) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LariSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L775) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LayerGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L776) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Leaf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L777) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LeftLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L778) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L779) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lemon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L780) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LessThan = "<"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L781) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LessThanEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L782) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LifeRing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L783) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lightbulb = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L784) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LinesLeaning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L785) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Link = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L786) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LinkSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L787) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LiraSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L788) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.List = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L789) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L790) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListOl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L791) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ListUl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L792) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LitecoinSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L793) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationArrow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L794) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationCrosshairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L795) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L796) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationPin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L797) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LocationPinLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L798) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L799) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LockOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L800) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Locust = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L801) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Lungs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L802) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.LungsVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L803) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.M = "M"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L804) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Magnet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L805) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L806) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L807) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassChart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L808) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L809) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassLocation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L810) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L811) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MagnifyingGlassPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L812) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ManatSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L813) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Map = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L814) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapLocation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L815) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapLocationDot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L816) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MapPin = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L817) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Marker = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L818) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L819) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsAndVenus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L820) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsAndVenusBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L821) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L822) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L823) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStrokeRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L824) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MarsStrokeUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L825) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L826) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlassCitrus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L827) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MartiniGlassEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L828) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mask = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L829) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MaskFace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L830) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MaskVentilator = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L831) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MasksTheater = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L832) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MattressPillow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L833) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Maximize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L834) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Medal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L835) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Memory = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L836) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Menorah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L837) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mercury = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L838) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Message = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L839) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Meteor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L840) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microchip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L841) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microphone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L842) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L843) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneLinesSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L844) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MicrophoneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L845) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Microscope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L846) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MillSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L847) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Minimize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L848) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Minus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L849) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mitten = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L850) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mobile = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L851) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L852) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileRetro = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L853) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileScreen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L854) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MobileScreenButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L855) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L856) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill1 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L857) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBill1Wave = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L858) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillTransfer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L859) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillTrendUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L860) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillWave = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L861) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBillWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L862) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyBills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L863) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L864) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MoneyCheckDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L865) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Monument = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L866) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Moon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L867) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MortarPestle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L868) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mosque = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L869) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mosquito = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L870) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MosquitoNet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L871) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Motorcycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L872) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mound = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L873) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Mountain = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L874) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MountainCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L875) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MountainSun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L876) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MugHot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L877) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.MugSaucer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L878) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Music = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L879) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.N = "N"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L880) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NairaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L881) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NetworkWired = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L882) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Neuter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L883) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Newspaper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L884) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NotEqual = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L885) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Notdef = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L886) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NoteSticky = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L887) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.NotesMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L888) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.O = "O"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L889) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ObjectGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L890) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ObjectUngroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L891) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.OilCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L892) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.OilWell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L893) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Om = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L894) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Otter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L895) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Outdent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L896) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.P = "P"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L897) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pager = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L898) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PaintRoller = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L899) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paintbrush = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L900) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Palette = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L901) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pallet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L902) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Panorama = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L903) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PaperPlane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L904) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paperclip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L905) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ParachuteBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L906) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paragraph = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L907) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Passport = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L908) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paste = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L909) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pause = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L910) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Paw = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L911) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Peace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L912) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L913) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenClip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L914) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenFancy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L915) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenNib = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L916) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenRuler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L917) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PenToSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L918) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pencil = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L919) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleArrows = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L920) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleCarryBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L921) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L922) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L923) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeoplePulling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L924) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleRobbery = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L925) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PeopleRoof = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L926) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PepperHot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L927) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Percent = "%"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L928) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Person = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L929) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonArrowDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L930) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonArrowUpFromLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L931) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBiking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L932) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBooth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L933) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBreastfeeding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L934) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L935) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L936) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonChalkboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L937) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L938) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L939) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L940) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L941) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleQuestion = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L942) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L943) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDigging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L944) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDotsFromLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L945) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L946) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDressBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L947) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonDrowning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L948) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonFalling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L949) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonFallingBurst = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L950) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHalfDress = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L951) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHarassing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L952) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonHiking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L953) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryPointing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L954) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryRifle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L955) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonMilitaryToPerson = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L956) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonPraying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L957) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonPregnant = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L958) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L959) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRifle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L960) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonRunning = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L961) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonShelter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L962) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkating = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L963) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkiing = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L964) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSkiingNordic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L965) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSnowboarding = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L966) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonSwimming = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L967) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonThroughWindow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L968) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L969) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingArrowLoopLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L970) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L971) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingDashedLineArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L972) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingLuggage = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L973) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PersonWalkingWithCane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L974) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PesetaSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L975) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PesoSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L976) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Phone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L977) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L978) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L979) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhoneVolume = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L980) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PhotoFilm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L981) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PiggyBank = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L982) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Pills = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L983) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PizzaSlice = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L984) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaceOfWorship = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L985) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L986) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneArrival = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L987) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L988) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L989) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L990) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneDeparture = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L991) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L992) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L993) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlaneUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L994) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlantWilt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L995) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlateWheat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L996) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Play = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L997) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L998) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleBolt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L999) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1000) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1001) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1002) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCirclePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1003) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlugCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1004) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Plus = "+"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1005) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PlusMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1006) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Podcast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1007) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Poo = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1008) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PooStorm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1009) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Poop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1010) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PowerOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1011) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Prescription = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1012) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PrescriptionBottle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1013) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PrescriptionBottleMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1014) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Print = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1015) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PumpMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1016) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PumpSoap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1017) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.PuzzlePiece = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1018) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Q = "Q"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1019) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Qrcode = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1020) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Question = "?"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1021) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.QuoteLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1022) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.QuoteRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1023) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.R = "R"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1024) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Radiation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1025) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Radio = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1026) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rainbow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1027) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RankingStar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1028) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Receipt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1029) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RecordVinyl = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1030) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleAd = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1031) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1032) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RectangleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1033) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Recycle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1034) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Registered = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1035) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Repeat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1036) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Reply = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1037) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ReplyAll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1038) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Republican = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1039) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Restroom = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1040) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Retweet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1041) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ribbon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1042) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightFromBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1043) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1044) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1045) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RightToBracket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1046) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ring = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1047) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Road = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1048) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadBarrier = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1049) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadBridge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1050) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1051) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1052) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1053) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1054) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RoadSpikes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1055) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Robot = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1056) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rocket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1057) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rotate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1058) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RotateLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1059) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RotateRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1060) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Route = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1061) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1062) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RubleSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1063) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Rug = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1064) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ruler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1065) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerCombined = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1066) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerHorizontal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1067) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RulerVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1068) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupeeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1069) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.RupiahSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1070) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.S = "S"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1071) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SackDollar = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1072) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SackXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1073) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sailboat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1074) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Satellite = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1075) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SatelliteDish = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1076) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleBalanced = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1077) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleUnbalanced = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1078) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScaleUnbalancedFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1079) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.School = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1080) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1081) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1082) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolCircleXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1083) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolFlag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1084) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SchoolLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1085) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Scissors = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1086) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Screwdriver = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1087) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScrewdriverWrench = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1088) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Scroll = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1089) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ScrollTorah = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1090) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SdCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1091) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Section = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1092) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Seedling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1093) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Server = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1094) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shapes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1095) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Share = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1096) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShareFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1097) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShareNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1098) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SheetPlastic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1099) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShekelSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1100) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1101) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldCat = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1102) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldDog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1103) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldHalved = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1104) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldHeart = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1105) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShieldVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1106) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ship = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1107) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shirt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1108) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShoePrints = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1109) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1110) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShopLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1111) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShopSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1112) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shower = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1113) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shrimp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1114) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Shuffle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1115) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ShuttleSpace = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1116) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SignHanging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1117) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Signal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1118) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Signature = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1119) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SignsPost = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1120) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SimCard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1121) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sink = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1122) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sitemap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1123) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Skull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1124) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SkullCrossbones = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1125) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Slash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1126) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sleigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1127) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sliders = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1128) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Smog = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1129) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Smoking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1130) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowflake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1131) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowman = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1132) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Snowplow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1133) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Soap = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1134) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Socks = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1135) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SolarPanel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1136) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sort = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1137) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SortDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1138) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SortUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1139) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spa = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1140) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SpaghettiMonsterFlying = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1141) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SpellCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1142) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spider = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1143) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spinner = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1144) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Splotch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1145) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Spoon = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1146) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SprayCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1147) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SprayCanSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1148) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Square = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1149) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareArrowUpRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1150) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareBinary = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1151) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1152) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1153) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1154) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCaretUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1155) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1156) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareEnvelope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1157) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1158) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareH = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1159) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1160) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareNfi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1161) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareParking = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1162) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1163) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePersonConfined = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1164) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePhone = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1165) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePhoneFlip = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1166) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1167) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePollHorizontal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1168) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquarePollVertical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1169) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareRootVariable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1170) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareRss = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1171) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareShareNodes = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1172) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareUpRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1173) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1174) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SquareXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1175) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StaffSnake = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1176) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stairs = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1177) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stamp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1178) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stapler = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1179) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Star = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1180) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarAndCrescent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1181) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1182) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarHalfStroke = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1183) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarOfDavid = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1184) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StarOfLife = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1185) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SterlingSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1186) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stethoscope = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1187) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stop = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1188) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stopwatch = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1189) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stopwatch20 = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1190) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Store = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1191) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StoreSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1192) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.StreetView = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1193) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Strikethrough = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1194) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Stroopwafel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1195) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Subscript = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1196) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Suitcase = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1197) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SuitcaseMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1198) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SuitcaseRolling = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1199) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Sun = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1200) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.SunPlantWilt = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1201) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Superscript = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1202) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Swatchbook = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1203) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Synagogue = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1204) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Syringe = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1205) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.T = "T"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1206) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Table = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1207) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCells = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1208) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsColumnLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1209) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsLarge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1210) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsRowLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1211) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableCellsRowUnlock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1212) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableColumns = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1213) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableList = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1214) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TableTennisPaddleBall = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1215) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tablet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1216) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TabletButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1217) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TabletScreenButton = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1218) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tablets = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1219) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TachographDigital = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1220) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1221) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tags = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1222) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tape = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1223) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tarp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1224) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TarpDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1225) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Taxi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1226) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Teeth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1227) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TeethOpen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1228) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureArrowDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1229) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1230) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1231) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureFull = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1232) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureHalf = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1233) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1234) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureLow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1235) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureQuarter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1236) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TemperatureThreeQuarters = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1237) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TengeSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1238) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tent = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1239) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowDownToLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1240) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1241) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowTurnLeft = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1242) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TentArrowsDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1243) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tents = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1244) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Terminal = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1245) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextHeight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1246) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1247) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TextWidth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1248) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Thermometer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1249) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbsDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1250) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbsUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1251) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Thumbtack = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1252) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ThumbtackSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1253) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Ticket = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1254) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TicketSimple = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1255) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Timeline = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1256) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToggleOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1257) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToggleOn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1258) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Toilet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1259) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPaper = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1260) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPaperSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1261) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletPortable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1262) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToiletsPortable = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1263) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Toolbox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1264) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tooth = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1265) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.ToriiGate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1266) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tornado = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1267) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerBroadcast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1268) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerCell = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1269) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TowerObservation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1270) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tractor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1271) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trademark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1272) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrafficLight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1273) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trailer = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1274) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Train = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1275) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrainSubway = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1276) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrainTram = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1277) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Transgender = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1278) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1279) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1280) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashCan = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1281) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrashCanArrowUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1282) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tree = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1283) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TreeCity = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1284) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TriangleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1285) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trophy = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1286) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Trowel = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1287) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TrowelBricks = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1288) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Truck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1289) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckArrowRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1290) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckDroplet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1291) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFast = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1292) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckField = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1293) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFieldUn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1294) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckFront = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1295) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMedical = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1296) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMonster = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1297) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckMoving = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1298) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckPickup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1299) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckPlane = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1300) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TruckRampBox = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1301) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1302) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurkishLiraSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1303) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurnDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1304) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.TurnUp = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1305) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Tv = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1306) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.U = "U"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1307) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Umbrella = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1308) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UmbrellaBeach = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1309) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Underline = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1310) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UniversalAccess = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1311) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Unlock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1312) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UnlockKeyhole = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1313) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpDown = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1314) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpDownLeftRight = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1315) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpLong = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1316) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpRightAndDownLeftFromCenter = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1317) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UpRightFromSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1318) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Upload = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1319) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.User = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1320) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserAstronaut = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1321) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1322) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserClock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1323) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserDoctor = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1324) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1325) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGraduate = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1326) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserGroup = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1327) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserInjured = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1328) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLarge = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1329) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLargeSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1330) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserLock = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1331) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserMinus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1332) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserNinja = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1333) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserNurse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1334) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserPen = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1335) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserPlus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1336) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserSecret = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1337) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserShield = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1338) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1339) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserTag = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1340) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserTie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1341) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UserXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1342) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Users = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1343) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersBetweenLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1344) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersGear = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1345) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersLine = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1346) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersRays = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1347) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersRectangle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1348) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1349) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.UsersViewfinder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1350) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Utensils = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1351) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.V = "V"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1352) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VanShuttle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1353) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vault = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1354) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VectorSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1355) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Venus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1356) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VenusDouble = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1357) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VenusMars = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1358) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vest = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1359) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VestPatches = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1360) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vial = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1361) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VialCircleCheck = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1362) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VialVirus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1363) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vials = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1364) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Video = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1365) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VideoSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1366) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Vihara = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1367) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Virus = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1368) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusCovid = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1369) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusCovidSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1370) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VirusSlash = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1371) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Viruses = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1372) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Voicemail = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1373) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Volcano = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1374) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Volleyball = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1375) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeHigh = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1376) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeLow = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1377) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeOff = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1378) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VolumeXmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1379) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.VrCardboard = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1380) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.W = "W"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1381) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WalkieTalkie = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1382) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wallet = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1383) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandMagic = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1384) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandMagicSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1385) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WandSparkles = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1386) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Warehouse = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1387) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Water = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1388) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WaterLadder = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1389) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WaveSquare = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1390) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WebAwesome = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1391) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WeightHanging = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1392) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WeightScale = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1393) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheatAwn = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1394) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheatAwnCircleExclamation = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1395) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wheelchair = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1396) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WheelchairMove = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1397) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WhiskeyGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1398) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wifi = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1399) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wind = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1400) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowMaximize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1401) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowMinimize = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1402) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WindowRestore = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1403) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineBottle = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1404) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineGlass = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1405) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WineGlassEmpty = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1406) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.WonSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1407) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Worm = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1408) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Wrench = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1409) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.X = "X"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1410) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.XRay = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1411) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Xmark = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1412) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.XmarksLines = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1413) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Y = "Y"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1414) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.YenSign = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1415) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.YinYang = ""`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1416) | Inherited or compiler-provided contract. | — | — | +| [`public const string Inno.Adapter.Presentation.ImGui.ImGuiIcon.Z = "Z"`](../../../backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/ImGuiIcon.cs#L1417) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 -- [Inno.Core.Execution](../../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Native.Sdl3](../Sdl3/Inno.Native.Sdl3.md):公开引用边界由实际签名核对。 -- [Inno.Native.ImGui](Inno.Native.ImGui.md):公开引用边界由实际签名核对。 -- `BGCS.Runtime`:通过固定版本 NuGet 包提供互操作运行时,不引用生成器。 -- [Inno.Adapter.Platform](../../platform/Inno.Adapter.Platform.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Presentation](../../platform/Inno.Adapter.Presentation.md):公开引用边界由实际签名核对。 -- [Inno.Platform](../../platform/Inno.Platform.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Platform.Sdl3](../Sdl3/Inno.Adapter.Platform.Sdl3.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Native.Sdl3` | 公开签名依赖 | +| `Inno.Native.ImGui` | 公开签名依赖 | +| `Inno.Adapter.Platform` | 公开签名依赖 | +| `Inno.Adapter.Presentation` | 公开签名依赖 | +| `Inno.Platform` | 公开签名依赖 | +| `Inno.Adapter.Platform.Sdl3` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/build/Inno.Build.Cli.md b/docs/build/Inno.Build.Cli.md index 24bf22bf5..5f86dd3d5 100644 --- a/docs/build/Inno.Build.Cli.md +++ b/docs/build/Inno.Build.Cli.md @@ -90,14 +90,60 @@ Support Pack 准备前缓存、缓存预算回收和完整性能矩阵尚未验 ## 当前源码公开 API 清单 -当前声明由源码语义清单核对,包含 public 和可供外部派生实现使用的 protected 成员。构建契约不作为游戏脚本导出;线程、失败与所有权要求见前文及每项 XML。 - -### `Inno.Build.Cli.BuildApplication` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 -| 声明 | 行为、参数与结果 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public static class Inno.Build.Cli.BuildApplication`](../../build/cli/Inno.Build.Cli/BuildApplication.cs#L11) | Executes explicit engine and project build commands for a caller-owned development lifetime. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Cli.BuildApplication.Run(string[] arguments, System.Threading.CancellationToken cancellationToken)`](../../build/cli/Inno.Build.Cli/BuildApplication.cs#L28) | Runs one build command without taking ownership of the caller's cancellation source.
`arguments`: The command and its documented options; help prints the supported command names.
`cancellationToken`: Cancels owned work and preserves completed publications according to their pipeline contracts. | Zero on success, two on cancellation, or one on a command or execution failure. | `System.ArgumentNullException`: The argument array is absent. | + +## 项目依赖 + +| 工程 | 边界 | | --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Cli.BuildApplication.Run(string[] arguments, System.Threading.CancellationToken cancellationToken)`](../../build/cli/Inno.Build.Cli/BuildApplication.cs#L28) | Runs one build command without taking ownership of the caller's cancellation source.
`arguments`:The command and its documented options; help prints the supported command names.
`cancellationToken`:Cancels owned work and preserves completed publications according to their pipeline contracts.
返回:Zero on success, two on cancellation, or one on a command or execution failure.
T:System.ArgumentNullException:The argument array is absent. | -| [`public static class Inno.Build.Cli.BuildApplication`](../../build/cli/Inno.Build.Cli/BuildApplication.cs#L11) | Executes explicit engine and project build commands for a caller-owned development lifetime. | +| `Inno.Build.Bindings` | 实现依赖;不传递 compile surface | +| `Inno.Build.Composition` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains` | 实现依赖;不传递 compile surface | +| `Inno.Build` | 实现依赖;不传递 compile surface | +| `Inno.Build.Managed` | 实现依赖;不传递 compile surface | +| `Inno.Build.Distribution.Standard` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains.Bgfx` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.UI.RmlUi.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains.Bgfx.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains.Bgfx.Tools` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Modules.DotNet` | 实现依赖;不传递 compile surface | +| `Inno.Adapter.Serialization.DotNet` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Annotations` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Core` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Interactions` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Inspection` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Graph` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Scene` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Rendering` | 实现依赖;不传递 compile surface | +| `Inno.Editor.PlayMode` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Shaders` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Assets.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Shaders.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Graph.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Panel.Hierarchy` | 实现依赖;不传递 compile surface | +| `Inno.Editor.Panel.Inspector` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 实现依赖;不传递 compile surface | +| `Inno.Assets.Pipeline` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 实现依赖;不传递 compile surface | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Plugins.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Animation.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Audio.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Text.Assets` | 实现依赖;不传递 compile surface | +| `Inno.UI.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Catalogs` | 实现依赖;不传递 compile surface | +| `Inno.Tooling.Architecture` | 实现依赖;不传递 compile surface | diff --git a/docs/build/Inno.Build.Distribution.Standard.md b/docs/build/Inno.Build.Distribution.Standard.md index 1d9b3c4d5..870e29edb 100644 --- a/docs/build/Inno.Build.Distribution.Standard.md +++ b/docs/build/Inno.Build.Distribution.Standard.md @@ -57,69 +57,47 @@ Windows Editor 导出 Browser 游戏时,离线工具仍需能在 Windows 执 ## 当前源码公开 API 清单 -当前声明由源码语义清单核对,包含 public 和可供外部派生实现使用的 protected 成员。构建契约不作为游戏脚本导出;线程、失败与所有权要求见前文及每项 XML。 - -### `Inno.Build.Distribution.Standard.StandardBuildCaches` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Collections.Generic.IReadOnlyList Inno.Build.Distribution.Standard.StandardBuildCaches.CreateOwners(string engineRoot, System.Collections.Generic.IReadOnlyList components)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildCaches.cs#L30) | Registers exact Native, binding and Support Pack ownership recorded by their publishers. Unrecorded directories remain outside the cleanup authority.
`engineRoot`:The absolute engine checkout containing the artifact roots.
`components`:The actual registered component owners; shared toolchain roots are registered once.
返回:Immutable owner registrations with current-generation and transitive dependency protection.
T:System.ArgumentException:The root is blank or relative. | -| [`public static class Inno.Build.Distribution.Standard.StandardBuildCaches`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildCaches.cs#L12) | Registers the standard distribution's actual immutable artifact publishers for coordinated cleanup. | - -### `Inno.Build.Distribution.Standard.StandardBuildDistribution` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static Inno.Build.Distribution.Standard.StandardBuildDistribution Inno.Build.Distribution.Standard.StandardBuildDistribution.Create(Inno.Build.Composition.BuildCompositionContext context, Inno.Build.Toolchains.Bgfx.Tools.ToolRunner? tools = null)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L52) | Composes one explicit binding per implemented platform without resolving tools.
`context`:The explicitly selected SDK and execution host.
`tools`:Borrowed frozen content-compilation tools, or null to consume this application's explicit deployment.
返回:A frozen standard distribution with backend configuration bound to the same platform identities. | -| [`public Inno.Build.Toolchains.Bgfx.Tools.BgfxShaderTargetProfile Inno.Build.Distribution.Standard.StandardBuildDistribution.ResolveShaderTarget(Inno.Build.BuildTargetId target)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L91) | Resolves explicitly registered shader configuration independently of tool execution target.
`target`:The artifact's actual publication target.
返回:The borrowed immutable configuration; unavailable targets fail before native preparation.
T:System.NotSupportedException:The distribution has no shader configuration for this target. | -| [`public Inno.Build.Composition.BuildDistribution Inno.Build.Distribution.Standard.StandardBuildDistribution.build { get; }`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L38) | Gets the neutral distribution borrowed by product hosts and shared pipeline mechanisms. | -| [`public sealed class Inno.Build.Distribution.Standard.StandardBuildDistribution`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L23) | Binds platform publication and backend configuration once at the standard distribution boundary. | - -### `Inno.Build.Distribution.Standard.StandardBuildEnvironment` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static Inno.Build.Composition.BuildCompositionContext Inno.Build.Distribution.Standard.StandardBuildEnvironment.Capture(string applicationDirectory, Inno.Build.BuildTargetId toolsTarget, string? dotnetHost = null)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs#L34) | Resolves the selected managed host and captures execution capabilities at the composition boundary.
`applicationDirectory`:The product or tool's installed location used for source provisioning.
`toolsTarget`:The native target explicitly selected for executable build tools, independently of exported games.
`dotnetHost`:An explicit managed SDK executable, or null to use the current tool process's selected host.
返回:A frozen execution context; publication targets must be selected independently.
T:System.PlatformNotSupportedException:The distribution has no build execution integration for this system or processor.
T:System.IO.FileNotFoundException:The selected managed SDK host cannot be located. | -| [`public static class Inno.Build.Distribution.Standard.StandardBuildEnvironment`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs#L11) | Captures the actual machine running distribution tools without choosing an output target. | - -### `Inno.Build.Distribution.Standard.StandardBuildHosts` - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static Inno.Build.Toolchains.BuildHostDescriptor Inno.Build.Distribution.Standard.StandardBuildHosts.Capture()`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildHosts.cs#L22) | Captures the current execution machine from registered platform providers.
返回:The selected immutable host and execution capabilities.
T:System.PlatformNotSupportedException:No registered provider supports the current machine. | -| [`public static class Inno.Build.Distribution.Standard.StandardBuildHosts`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildHosts.cs#L11) | Registers supported build hosts without duplicating their execution facts. | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public static class Inno.Build.Distribution.Standard.StandardBuildCaches`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildCaches.cs#L12) | Registers the standard distribution's actual immutable artifact publishers for coordinated cleanup. | — | — | +| [`public static System.Collections.Generic.IReadOnlyList Inno.Build.Distribution.Standard.StandardBuildCaches.CreateOwners(string engineRoot, System.Collections.Generic.IReadOnlyList components)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildCaches.cs#L30) | Registers exact Native, binding and Support Pack ownership recorded by their publishers. Unrecorded directories remain outside the cleanup authority.
`engineRoot`: The absolute engine checkout containing the artifact roots.
`components`: The actual registered component owners; shared toolchain roots are registered once. | Immutable owner registrations with current-generation and transitive dependency protection. | `System.ArgumentException`: The root is blank or relative. | +| [`public sealed class Inno.Build.Distribution.Standard.StandardBuildDistribution`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L23) | Binds platform publication and backend configuration once at the standard distribution boundary. | — | — | +| [`public Inno.Build.Composition.BuildDistribution Inno.Build.Distribution.Standard.StandardBuildDistribution.build { get; }`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L38) | Gets the neutral distribution borrowed by product hosts and shared pipeline mechanisms. | — | — | +| [`public static Inno.Build.Distribution.Standard.StandardBuildDistribution Inno.Build.Distribution.Standard.StandardBuildDistribution.Create(Inno.Build.Composition.BuildCompositionContext context, Inno.Build.Toolchains.Bgfx.Tools.ToolRunner? tools = null)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L52) | Composes one explicit binding per implemented platform without resolving tools.
`context`: The explicitly selected SDK and execution host.
`tools`: Borrowed frozen content-compilation tools, or null to consume this application's explicit deployment. | A frozen standard distribution with backend configuration bound to the same platform identities. | — | +| [`public Inno.Build.Toolchains.Bgfx.Tools.BgfxShaderTargetProfile Inno.Build.Distribution.Standard.StandardBuildDistribution.ResolveShaderTarget(Inno.Build.BuildTargetId target)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildDistribution.cs#L91) | Resolves explicitly registered shader configuration independently of tool execution target.
`target`: The artifact's actual publication target. | The borrowed immutable configuration; unavailable targets fail before native preparation. | `System.NotSupportedException`: The distribution has no shader configuration for this target. | +| [`public static class Inno.Build.Distribution.Standard.StandardBuildEnvironment`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs#L12) | Captures the actual machine running distribution tools without choosing an output target. | — | — | +| [`public static Inno.Build.Composition.BuildCompositionContext Inno.Build.Distribution.Standard.StandardBuildEnvironment.Capture(string applicationDirectory, Inno.Build.BuildTargetId toolsTarget, string? dotnetHost = null)`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildEnvironment.cs#L36) | Resolves the selected managed host and captures execution capabilities at the composition boundary.
`applicationDirectory`: The product or tool's installed location used for source provisioning.
`toolsTarget`: The native target explicitly selected for executable build tools, independently of exported games.
`dotnetHost`: An explicit SDK executable, or null to use DOTNET_HOST_PATH, the loaded shared runtime's muxer, then normal executable discovery when this process has no shared runtime installation. | A frozen execution context; publication targets must be selected independently. | `System.PlatformNotSupportedException`: The distribution has no build execution integration for this system or processor.
`System.IO.FileNotFoundException`: The selected managed SDK host cannot be located. | +| [`public static class Inno.Build.Distribution.Standard.StandardBuildHosts`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildHosts.cs#L11) | Registers supported build hosts without duplicating their execution facts. | — | — | +| [`public static Inno.Build.Toolchains.BuildHostDescriptor Inno.Build.Distribution.Standard.StandardBuildHosts.Capture()`](../../build/distributions/Inno.Build.Distribution.Standard/StandardBuildHosts.cs#L22) | Captures the current execution machine from registered platform providers. | The selected immutable host and execution capabilities. | `System.PlatformNotSupportedException`: No registered provider supports the current machine. | ## 项目依赖 -- [Inno.Build.Bindings](Inno.Build.Bindings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Integration.WindowsX64.Bgfx](../platform/WindowsX64/Inno.Integration.WindowsX64.Bgfx.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Integration.MacOSArm64.Bgfx](../platform/MacOSArm64/Inno.Integration.MacOSArm64.Bgfx.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Integration.BrowserWasm32.Bgfx](../platform/BrowserWasm32/Inno.Integration.BrowserWasm32.Bgfx.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.Bgfx.Tools](../backends/Bgfx/Inno.Build.Toolchains.Bgfx.Tools.md):公开引用边界由实际签名核对。 -- [Inno.Build.Composition](Inno.Build.Composition.md):公开引用边界由实际签名核对。 -- [Inno.Build.Managed.DotNet](../backends/DotNet/Inno.Build.Managed.DotNet.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.WindowsX64](../platform/WindowsX64/Inno.Build.WindowsX64.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.MacOSArm64](../platform/MacOSArm64/Inno.Build.MacOSArm64.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.BrowserWasm32](../platform/BrowserWasm32/Inno.Build.BrowserWasm32.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.Bgfx](../backends/Bgfx/Inno.Build.Toolchains.Bgfx.md):公开引用边界由实际签名核对。 -- [Inno.Build.Toolchains.ImGui](../backends/ImGui/Inno.Build.Toolchains.ImGui.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.ImGuizmo](../backends/ImGui/Inno.Build.Toolchains.ImGuizmo.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.MiniAudio](../backends/MiniAudio/Inno.Build.Toolchains.MiniAudio.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.Sdl3](../backends/Sdl3/Inno.Build.Toolchains.Sdl3.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.Text](../backends/Text/Inno.Build.Toolchains.Text.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains.UI](../backends/RmlUi/Inno.Build.Toolchains.UI.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Build.Toolchains](Inno.Build.Toolchains.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Native.LibraryLoading` | 实现依赖;不传递 compile surface | +| `Inno.Build.Windows.Host` | 实现依赖;不传递 compile surface | +| `Inno.Build.Bindings` | 实现依赖;不传递 compile surface | +| `Inno.Integration.WindowsX64.Bgfx` | 实现依赖;不传递 compile surface | +| `Inno.Integration.MacOSArm64.Bgfx` | 实现依赖;不传递 compile surface | +| `Inno.Integration.BrowserWasm32.Bgfx` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains.Bgfx.Tools` | 公开签名依赖 | +| `Inno.Build.Composition` | 公开签名依赖 | +| `Inno.Build.Managed.DotNet` | 实现依赖;不传递 compile surface | +| `Inno.Integration.WindowsX64.Build` | 实现依赖;不传递 compile surface | +| `Inno.Integration.MacOSArm64.Build` | 实现依赖;不传递 compile surface | +| `Inno.Integration.BrowserWasm32.Build` | 实现依赖;不传递 compile surface | +| `Inno.Build.Toolchains.Bgfx` | 公开签名依赖 | +| `Inno.Build.Toolchains` | 公开签名依赖 | diff --git a/docs/build/Inno.Build.Toolchains.md b/docs/build/Inno.Build.Toolchains.md index f737a7e26..344e0bcdf 100644 --- a/docs/build/Inno.Build.Toolchains.md +++ b/docs/build/Inno.Build.Toolchains.md @@ -158,24 +158,191 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 缓存清理的 entry 路由仅准备 Toolchains 及 Foundation 闭包,不加载完整 CLI、BGCS 生成器、Editor 或第三方后端。产品清理需要产品预检,仍属于完整 Build 命令。 -## 当前源码公开 API 清单 -当前声明由源码语义清单核对,包含 public 和可供外部派生实现使用的 protected 成员。构建契约不作为游戏脚本导出;线程、失败与所有权要求见前文及每项 XML。 -### `Inno.Build.Toolchains.BuildArtifactManifest` +## 缓存管理公开契约 - - - -| 声明 | 行为、参数与结果 | +| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| --- | --- | --- | --- | +| [`public static Inno.Build.Toolchains.BuildCacheCleanupPlan Inno.Build.Toolchains.BuildCacheCleanupPlan.Create(string workspace, System.Collections.Generic.IReadOnlyList owners, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L84) | 明确注册 owner 的物理边界、协调路径和借用的清单/保护根 callbacks。 | Callbacks 在本次清理期间有效;发布及根更新使用同一协调 lease。 | 不得通过目录名猜 owner;拒绝重复、越界或重叠注册。 | + +### 进程加载保护 + + +| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| --- | --- | --- | --- | + + +## 进程与构建所有权 API + +| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| --- | --- | --- | --- | +| [`public Inno.Build.Toolchains.BuildCacheArtifactDescriptor.BuildCacheArtifactDescriptor(string directory, string leasePath, string? targetId, long bytes, System.DateTime lastUseUtc, System.Collections.Generic.IReadOnlyList dependencies, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L48) | 冻结目录、目标、角色、协调路径、字节、UTC 使用时间与依赖;大小只服务预算。 | 描述不接管 producer 或 reader lease。 | 绝对路径、非负大小、UTC 时间和有效角色必须成立。 | +| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCacheArtifactDescriptor.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L104) | Gets the declared lifetime used for retirement and preferred-workspace accounting. | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`public enum Inno.Build.Toolchains.BuildCacheArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L6) | 明确区分不可变产物、可复用 workspace 与进程临时目录。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ImmutableArtifact = 0`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L11) | 完整产物由当前产品/Pack/活动读取保护。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ReusableWorkspace = 1`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L16) | 编译活动时保护;无活动时可按预算退休,即使最近使用。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ProcessWorkingDirectory = 2`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L21) | 进程活动或 Unknown 时保护,退出后可退休。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`public Inno.Build.Toolchains.BuildCachePublisher.BuildCachePublisher(string directory, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L34) | 为明确 cache 根选择生命周期,之后通过 Describe 提供登记与保护关系。 | 构造不取得 lease;发布/读取交接由调用方持有 activity 与产物保护。 | 根必须绝对且不越界;不认领未登记目录。 | +| [`public void Inno.Build.Toolchains.BuildCachePublisher.BeginWorkingDirectory(string directory, string leasePath, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L168) | 调用者取得 activity 与 producer 独占 lease 后,先登记 reusable workspace,再允许 payload 写入。 | lease 仍由调用方持有;不永久固定闲置 workspace。 | 已有无登记目录拒绝认领;目标归属按显式参数记录。 | +| [`public Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.WithDependency(Inno.Build.Toolchains.NativeBuildInput input, string fingerprint)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L190) | 把实际依赖产物和指纹冻结进后端 recipe,保留选定工具;依赖产品仅在 recipe 构造期间借用。 | 不转移 NativeBuildProduct 的读取 leases。 | 没有声明转换的依赖步骤走正常冷路径,不能猜测链接参数。 | +| [`public Inno.Build.Toolchains.ProductNativeBuildStep.ProductNativeBuildStep(string id, Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options, System.Collections.Generic.IReadOnlyList dependencies, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? build, System.Func? createRecipe, System.Func deploymentPath, string? configuration = null, System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? bindDependencies = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L52) | Freezes a component step without embedding any backend list in the shared executor. | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | +| [`public System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? Inno.Build.Toolchains.ProductNativeBuildStep.bindDependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L134) | 把实际依赖产物和指纹冻结进后端 recipe,保留选定工具;依赖产品仅在 recipe 构造期间借用。 | 不转移 NativeBuildProduct 的读取 leases。 | 没有声明转换的依赖步骤走正常冷路径,不能猜测链接参数。 | + + + + + + + + + + + + + +有效 MSBuild 图的节点按项目路径与完整 global properties 排序,避免同路径多配置节点的无序枚举造成错误失效。参数、include 与编译项顺序继续参与身份;验证失败会报告文件或声明变化。内部 capture 只服务诊断,不构成新的公开缓存权威。 + +工具 publication 在固定输入前,使用同一 SDK 还原入口项目及 InnoBuildInputProject 声明的辅助输入项目。辅助项目来自有效图,清理的 global properties 保持一致;Capture/Verify 本身仍只读取,不通过缺 assets 的 fallback 掩盖错误。 + +### 输入路径别名与创作归属 + +同一校验阶段按最终物理路径去重哈希,但保留每次读取的原路径和 checkout 相对逻辑身份。macOS 的 `/var` 别名、目录链接与 Windows junction 不会把所属源码误记成外部 SDK 输入;固定编译副本仍按所属源码布局准备。独立锁等待、编译及提交后重新校验,不跨阶段复用内容结论。 + +## 原生生成编译契约 API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +| 当前签名 | 语义 | | --- | --- | -| [`public static bool Inno.Build.Toolchains.BuildArtifactManifest.IsComplete(string directory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, Inno.Build.Toolchains.NativeBuildContext? context = null)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L88) | Checks the identity, exact output file set and bytes before reusing a cached artifact.
`directory`:The published artifact directory.
`fingerprint`:The requested input identity.
`outputDirectories`:The same relative output subdirectories supplied when recording the artifact.
`context`:The optional operation collecting actual output integrity reads.
返回:True only for complete, unmodified outputs; false for a missing, malformed or mismatched manifest.
T:System.ArgumentException:An output path escapes the artifact root.
T:System.IO.IOException:An existing output cannot be read. | -| [`public static void Inno.Build.Toolchains.BuildArtifactManifest.Write(string directory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, Inno.Build.Toolchains.NativeBuildContext? context = null)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L40) | Records a completed staging tree before its owner publishes the directory.
`directory`:The complete staging root owned exclusively by the caller.
`fingerprint`:The immutable identity of every input used to produce these outputs.
`outputDirectories`:Relative output subdirectories included in this artifact; each must stay within the root.
`context`:The optional operation collecting independent output-read statistics.
T:System.ArgumentException:An identity or output path is invalid.
T:System.IO.IOException:An output or manifest cannot be read or written. | -| [`public static class Inno.Build.Toolchains.BuildArtifactManifest`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L15) | Validates complete build outputs against their input identity and recorded file hashes. | +| [`public sealed class Inno.Build.Toolchains.NativeBindingCompilation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | Describes a selected component's native requirements independently of its source generator. | +| [`public required Inno.Build.Toolchains.NativeBindingSourceRoot[] Inno.Build.Toolchains.NativeBindingCompilation.roots { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L27) | Gets root bindings whose identities are local to this component. | +| [`public required Inno.Build.Toolchains.NativeBindingSourceFile[] Inno.Build.Toolchains.NativeBindingCompilation.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L32) | Gets all exact input files; only files marked compile are translation units. | +| [`public required Inno.Build.Toolchains.NativeBindingIncludeDirectory[] Inno.Build.Toolchains.NativeBindingCompilation.includeDirectories { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L37) | Gets include requirements in their original search order. | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.defines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L42) | Gets ordered definitions for manifest translation units; upstream target configuration remains component-owned. | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.compilerDefines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L47) | Gets parser target macro facts; these must not override the consuming compiler's builtins. | +| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cxxStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L52) | Gets the minimum C++ standard, or zero when no C++ translation unit is required. | +| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L57) | Gets the minimum C standard, or zero when no C translation unit is required. | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.requiredExports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L62) | Gets the exact native symbols that the component must provide. | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportSymbols { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L67) | Gets static table entry points, or an empty array for dynamic-only bindings. | +| [`public required string Inno.Build.Toolchains.NativeBindingCompilation.staticExportAbi { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L72) | Gets the static table ABI digest, or an empty value when no static table exists. | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportNames { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L77) | Gets static table entries in address-resolution order. | +| [`public static Inno.Build.Toolchains.NativeBindingCompilation Inno.Build.Toolchains.NativeBindingCompilation.Load(string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L97) | Reads the current engine-owned compilation description from a selected native output. | +| [`public string Inno.Build.Toolchains.NativeBindingCompilation.ResolveRoot(string engineRoot, string nativeDirectory, string rootId)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L124) | Resolves a component-local root using the current checkout and selected output. | +| [`public void Inno.Build.Toolchains.NativeBindingCompilation.VerifyInputs(System.Func resolveRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L152) | Verifies the exact bytes at original or remapped roots before their use. | +| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingCompilation.GetInputs(string engineRoot, string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L179) | Enumerates exact file declarations for standalone or aggregate recipe fingerprinting. | +| [`public Inno.Build.Toolchains.NativeBindingCompilation.NativeBindingCompilation()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public sealed record Inno.Build.Toolchains.NativeBindingIncludeDirectory : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement. | +| [`public Inno.Build.Toolchains.NativeBindingIncludeDirectory.NativeBindingIncludeDirectory(string rootId, string relativePath, bool system, bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement. | +| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L19) | The component-local input root. | +| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L20) | The root-relative directory, or empty for the root itself. | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.system { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L21) | Whether this directory contains system headers. | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.framework { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L22) | Whether the compiler must use framework search semantics. | +| [`public override string Inno.Build.Toolchains.NativeBindingIncludeDirectory.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Returns a string that represents the current object. | +| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator !=(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator ==(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public override int Inno.Build.Toolchains.NativeBindingIncludeDirectory.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Serves as the default hash function. | +| [`public override bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Determines whether the specified object is equal to the current object. | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(Inno.Build.Toolchains.NativeBindingIncludeDirectory? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Indicates whether the current object is equal to another object of the same type. | +| [`public void Inno.Build.Toolchains.NativeBindingIncludeDirectory.Deconstruct(out string rootId, out string relativePath, out bool system, out bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceFile : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation. | +| [`public Inno.Build.Toolchains.NativeBindingSourceFile.NativeBindingSourceFile(string rootId, string relativePath, long length, string sha256, bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation. | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L22) | The component-local input root. | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L23) | The portable root-relative filename. | +| [`public long Inno.Build.Toolchains.NativeBindingSourceFile.length { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L24) | The expected byte length. | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.sha256 { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L25) | The lowercase SHA-256 content identity. | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.compile { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L26) | Whether this file is a translation unit rather than an include dependency. | +| [`public override string Inno.Build.Toolchains.NativeBindingSourceFile.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Returns a string that represents the current object. | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator !=(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator ==(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public override int Inno.Build.Toolchains.NativeBindingSourceFile.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Serves as the default hash function. | +| [`public override bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Determines whether the specified object is equal to the current object. | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(Inno.Build.Toolchains.NativeBindingSourceFile? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Indicates whether the current object is equal to another object of the same type. | +| [`public void Inno.Build.Toolchains.NativeBindingSourceFile.Deconstruct(out string rootId, out string relativePath, out long length, out string sha256, out bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceRoot : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract. | +| [`public Inno.Build.Toolchains.NativeBindingSourceRoot.NativeBindingSourceRoot(string id, string directory, bool generated, bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract. | +| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.id { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L19) | An ordinal identity scoped to this component's selected generation. | +| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.directory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L20) | The checkout-relative or absolute external location; empty for generated output. | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.generated { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L21) | Whether this root denotes the selected native output directory. | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.frozen { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L22) | Whether source files must be copied before compilation. | +| [`public override string Inno.Build.Toolchains.NativeBindingSourceRoot.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Returns a string that represents the current object. | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator !=(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator ==(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | +| [`public override int Inno.Build.Toolchains.NativeBindingSourceRoot.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Serves as the default hash function. | +| [`public override bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Determines whether the specified object is equal to the current object. | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(Inno.Build.Toolchains.NativeBindingSourceRoot? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Indicates whether the current object is equal to another object of the same type. | +| [`public void Inno.Build.Toolchains.NativeBindingSourceRoot.Deconstruct(out string id, out string directory, out bool generated, out bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | + + -### `Inno.Build.Toolchains.BuildArtifactOptions` +| 声明 | 行为与约束 | +| --- | --- | +| [`public required bool Inno.Build.Toolchains.NativeBindingGenerationDescriptor.usesRuntimeImports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L99) | Gets whether the generated context uses runtime-resolved imports instead of a module provider. This initialization choice is independent of static or shared native linkage. | +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + @@ -190,60 +357,14 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildArtifactOptions.BuildArtifactOptions(string buildDirName, System.Collections.Generic.IReadOnlyCollection libraryTokens, System.Collections.Generic.IReadOnlyCollection extensions, System.Collections.Generic.IReadOnlyCollection? requiredPathTokens, System.Func normalizeOutputName)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Defines the deterministic filter and naming policy used to collect one native product.
`buildDirName`:The dependency-relative directory searched for native build outputs.
`libraryTokens`:File-name tokens that identify artifacts belonging to the product.
`extensions`:The accepted native artifact extensions.
`requiredPathTokens`:Optional normalized path tokens used to reject unrelated intermediate files.
`normalizeOutputName`:The deterministic output naming policy. | -| [`public void Inno.Build.Toolchains.BuildArtifactOptions.Deconstruct(out string buildDirName, out System.Collections.Generic.IReadOnlyCollection libraryTokens, out System.Collections.Generic.IReadOnlyCollection extensions, out System.Collections.Generic.IReadOnlyCollection? requiredPathTokens, out System.Func normalizeOutputName)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.BuildArtifactOptions.Equals(Inno.Build.Toolchains.BuildArtifactOptions? other)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.BuildArtifactOptions.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.BuildArtifactOptions.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.BuildArtifactOptions.ToString()`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.BuildArtifactOptions.operator ==(Inno.Build.Toolchains.BuildArtifactOptions? left, Inno.Build.Toolchains.BuildArtifactOptions? right)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.BuildArtifactOptions.operator !=(Inno.Build.Toolchains.BuildArtifactOptions? left, Inno.Build.Toolchains.BuildArtifactOptions? right)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.Toolchains.BuildArtifactOptions.buildDirName { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L27) | The dependency-relative directory searched for native build outputs. | -| [`public System.Collections.Generic.IReadOnlyCollection Inno.Build.Toolchains.BuildArtifactOptions.extensions { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L29) | The accepted native artifact extensions. | -| [`public System.Collections.Generic.IReadOnlyCollection Inno.Build.Toolchains.BuildArtifactOptions.libraryTokens { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L28) | File-name tokens that identify artifacts belonging to the product. | -| [`public System.Func Inno.Build.Toolchains.BuildArtifactOptions.normalizeOutputName { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L31) | The deterministic output naming policy. | -| [`public System.Collections.Generic.IReadOnlyCollection? Inno.Build.Toolchains.BuildArtifactOptions.requiredPathTokens { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L30) | Optional normalized path tokens used to reject unrelated intermediate files. | -| [`public sealed record Inno.Build.Toolchains.BuildArtifactOptions : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Defines the deterministic filter and naming policy used to collect one native product.
`buildDirName`:The dependency-relative directory searched for native build outputs.
`libraryTokens`:File-name tokens that identify artifacts belonging to the product.
`extensions`:The accepted native artifact extensions.
`requiredPathTokens`:Optional normalized path tokens used to reject unrelated intermediate files.
`normalizeOutputName`:The deterministic output naming policy. | - -### `Inno.Build.Toolchains.BuildArtifactCopier` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static void Inno.Build.Toolchains.BuildArtifactCopier.CopyArtifacts(string buildRoot, string outputDir, string config, Inno.Build.Toolchains.BuildArtifactOptions options)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L56) | Copies all artifacts accepted by one product policy into its output directory.
`buildRoot`:The native dependency root containing the configured build directory.
`outputDir`:The destination directory receiving normalized artifacts.
`config`:The normalized native build configuration.
`options`:The immutable filtering and naming policy. | -| [`public static class Inno.Build.Toolchains.BuildArtifactCopier`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L37) | Copies filtered native outputs into the engine's rebuildable dependency store. | - -### `Inno.Build.Toolchains.BuildArtifactReadLease` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildArtifactReadLease.AcquireAsync(string directory, string ownerDirectory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Build.Toolchains.NativeBuildContext? statistics = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L55) | Acquires reading ownership and verifies the complete artifact before exposing it to a consumer.
`ownerDirectory`:The exact publisher root retaining the persistent owner.lock gate.
`directory`:The absolute artifact directory owned by its publisher; its persistent lease is stored alongside it.
`fingerprint`:The exact input identity required by the consumer.
`outputDirectories`:The complete relative output roots declared by the publisher.
`cancellationToken`:Cancels ownership acquisition without releasing another reader's protection.
`statistics`:Optional borrowed operation receiving output-integrity read statistics; its input cache is not used.
返回:Reading ownership which the caller must retain until all artifact access completes.
T:System.ArgumentException:The directory is not absolute or a required identity is blank.
T:System.IO.InvalidDataException:The complete artifact does not match the requested identity and output inventory.
T:System.TimeoutException:Repair or retirement retains exclusive ownership for more than thirty seconds.
T:System.OperationCanceledException:Waiting was canceled before ownership could be returned. | -| [`public void Inno.Build.Toolchains.BuildArtifactReadLease.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L68) | Ends artifact access and allows repair or retirement; repeated disposal has no effect. | -| [`public sealed class Inno.Build.Toolchains.BuildArtifactReadLease : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L13) | Pins a verified immutable build artifact against cooperating repair and retirement operations. | - -### `Inno.Build.Toolchains.BuildArtifactWriteLease` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildArtifactWriteLease.AcquireAsync(string directory, string ownerDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L42) | Waits for all readers before allowing mutation of one immutable artifact directory.
`ownerDirectory`:The exact publisher root retaining the persistent owner.lock gate.
`directory`:The absolute artifact location controlled by the publisher.
`cancellationToken`:Cancels this waiter without disturbing existing readers.
返回:Exclusive protection; retain it through replacement and release it before acquiring a reading lease.
T:System.ArgumentException:The directory is empty or relative.
T:System.TimeoutException:Other owners retain the artifact for more than thirty seconds.
T:System.OperationCanceledException:Waiting was canceled. | -| [`public void Inno.Build.Toolchains.BuildArtifactWriteLease.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L58) | Ends exclusive access without removing the persistent coordination file. | -| [`public sealed class Inno.Build.Toolchains.BuildArtifactWriteLease : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L12) | Coordinates artifact replacement or retirement with every cooperating reader and writer. | - -### `Inno.Build.Toolchains.BuildCacheArtifactDescriptor` - @@ -253,115 +374,35 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildCacheArtifactDescriptor.BuildCacheArtifactDescriptor(string directory, string leasePath, string? targetId, long bytes, System.DateTime lastUseUtc, System.Collections.Generic.IReadOnlyList dependencies, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L48) | Freezes ownership metadata supplied by the artifact's actual publisher.
`directory`:The absolute published artifact or producer-owned workspace directory.
`leasePath`:The absolute persistent file used by this entry's actual producer and readers for Core.IO coordination.
`bytes`:The publisher-recorded sum of owned file lengths, captured under writing ownership for cache budgeting. It does not measure allocated disk blocks or count shared hardlinks as recovered volume space.
`targetId`:The explicit product target, or null for a shared host artifact that target-scoped cleanup must preserve.
`lastUseUtc`:The publisher's last recorded use, used only to order retirement.
`dependencies`:Absolute artifact directories required by this generation.
`role`:The producer-declared lifetime; workspace preference alone does not pin an immutable product.
T:System.ArgumentException:A path is relative or the timestamp is not UTC.
T:System.ArgumentOutOfRangeException:The measured size is negative.
T:System.ArgumentNullException:The dependency collection is absent. | -| [`public long Inno.Build.Toolchains.BuildCacheArtifactDescriptor.bytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L99) | Gets the logical file size used for budgeting, without weakening content validation. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheArtifactDescriptor.dependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L114) | Gets the frozen dependency directories protected whenever this artifact is a live root. | -| [`public string Inno.Build.Toolchains.BuildCacheArtifactDescriptor.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L84) | Gets the absolute cache entry controlled by the declaring producer. | -| [`public System.DateTime Inno.Build.Toolchains.BuildCacheArtifactDescriptor.lastUseUtc { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L109) | Gets the UTC use timestamp, which is never used to validate cached content. | -| [`public string Inno.Build.Toolchains.BuildCacheArtifactDescriptor.leasePath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L89) | Gets the exact existing producer/reader coordination path; retirement does not infer another lock protocol. | -| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCacheArtifactDescriptor.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L104) | Gets the declared lifetime used for retirement and preferred-workspace accounting. | -| [`public string? Inno.Build.Toolchains.BuildCacheArtifactDescriptor.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L94) | Gets the producer-declared target, or null when this artifact is shared by host operations. | -| [`public sealed class Inno.Build.Toolchains.BuildCacheArtifactDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L12) | Freezes one publisher-owned cache entry and the generations it requires. | - -### `Inno.Build.Toolchains.BuildCacheArtifactRole` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ImmutableArtifact = 0`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L11) | A complete immutable generation remains protected by selected products and active readers. | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ProcessWorkingDirectory = 2`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L21) | An operation directory remains protected until its exact process owners have exited. | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ReusableWorkspace = 1`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L16) | A mutable compiler workspace may retire when no producer or consumer is active. | -| [`public enum Inno.Build.Toolchains.BuildCacheArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L6) | Declares the producer's file lifetime independently of target identity and directory naming. | - -### `Inno.Build.Toolchains.BuildCacheCleanupPlan` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static Inno.Build.Toolchains.BuildCacheCleanupPlan Inno.Build.Toolchains.BuildCacheCleanupPlan.Create(string workspace, System.Collections.Generic.IReadOnlyList owners, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll = false, string? targetId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L95) | Plans retirement by budget pressure or an explicit request to retire every unreferenced artifact.
`workspace`:The absolute checkout boundary; no owner may manage it or its parent.
`owners`:Explicit registrations; callbacks remain borrowed until execution completes.
`policy`:The immutable size, free-space and duration budgets.
`retireAll`:Whether to propose all unreferenced generations regardless of size pressure.
`cancellationToken`:Cancels planning before any directory is removed.
`targetId`:An exact producer-declared target filter, or null to manage all registered scopes. Outside-scope generations and their dependencies remain protected.
返回:A fixed proposal; execution rechecks ownership, roots and active readers before deletion.
T:System.ArgumentException:Owners overlap, identities repeat, or dependency metadata forms a cycle.
T:System.IO.IOException:An owner, artifact or lease escapes its boundary or traverses a link.
T:System.IO.InvalidDataException:An inventory has ambiguous ownership or undeclared dependencies.
T:System.OperationCanceledException:Planning was canceled.
T:System.TimeoutException:Inventory exceeded the complete operation budget; no artifact was removed. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildCacheCleanupPlan.ExecuteAsync(bool dryRun = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L184) | Executes the original proposal under fresh owner and artifact leases without waiting for active readers.
`dryRun`:Whether to report the proposal without acquiring mutation leases or deleting files.
`cancellationToken`:Cancels remaining work; successful earlier retirements are not rolled back.
返回:Actual retirement, activity skips and IO failures; protected generations are never forced out.
T:System.IO.InvalidDataException:Fresh ownership metadata violates the registered boundary.
T:System.OperationCanceledException:The caller canceled; no later candidate is processed. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupPlan.candidates { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L43) | Gets the original eligible candidates shared by preview and execution. | -| [`public long Inno.Build.Toolchains.BuildCacheCleanupPlan.protectedBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L53) | Gets the size protected by current roots and their transitive dependencies when planning. | -| [`public long Inno.Build.Toolchains.BuildCacheCleanupPlan.totalBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L48) | Gets the measured total of explicitly inventoried artifact files. | -| [`public sealed class Inno.Build.Toolchains.BuildCacheCleanupPlan`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L15) | Freezes a reviewable retirement proposal for explicitly registered cache owners in one checkout. | - -### `Inno.Build.Toolchains.BuildCacheCleanupResult` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.BuildCacheCleanupResult.failures { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L40) | Gets failed paths and their observable IO or permission diagnostics. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupResult.retired { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L30) | Gets directories actually retired; a preview always returns an empty collection. | -| [`public long Inno.Build.Toolchains.BuildCacheCleanupResult.retiredBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L46) | Gets the declared file bytes of successfully retired artifacts, excluding persistent leases. This is a logical size; hardlinks, compression and other writers can affect actual volume recovery. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupResult.skipped { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L35) | Gets candidates preserved because ownership, protection or dependencies changed. | -| [`public bool Inno.Build.Toolchains.BuildCacheCleanupResult.timeLimitReached { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L51) | Gets whether the bounded attempt ended before processing all eligible candidates. | -| [`public sealed class Inno.Build.Toolchains.BuildCacheCleanupResult`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L11) | Reports completed retirement independently from protected, active and failed artifacts. | - -### `Inno.Build.Toolchains.BuildCacheOwnerCatalog` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheOwnerCatalog.ReadOwners(string engineRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L90) | Reads explicitly registered roots using each publisher's existing inventory and protection rules.
`engineRoot`:The absolute checkout containing registrations.
`cancellationToken`:Cancels reading before a cleanup plan exists.
返回:Frozen owner descriptors; an unprepared checkout returns an empty list.
T:System.IO.InvalidDataException:A registration is malformed or has an ambiguous identity.
T:System.IO.IOException:A registered path crosses a link or leaves the checkout. | -| [`public static void Inno.Build.Toolchains.BuildCacheOwnerCatalog.Register(string engineRoot, string directory, string id, Inno.Build.Toolchains.BuildCacheArtifactRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L45) | Registers a publisher root; this never adopts unrecorded artifacts inside that root.
`engineRoot`:The absolute checkout owning the registration.
`directory`:The exact publisher root beneath the checkout.
`id`:The distribution's unique owner identity.
`role`:The publisher's actual artifact lifetime.
`cancellationToken`:Cancels before registration commits.
T:System.IO.IOException:A registration crosses a link or ownership boundary.
T:System.IO.InvalidDataException:An existing root has a different registration.
T:System.OperationCanceledException:Cancellation precedes commit. | -| [`public static class Inno.Build.Toolchains.BuildCacheOwnerCatalog`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L16) | Persists explicit publisher registrations for maintenance without loading the production distribution. | - -### `Inno.Build.Toolchains.BuildCacheOwnerDescriptor` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildCacheOwnerDescriptor.BuildCacheOwnerDescriptor(string id, string directory, string coordinationPath, System.Func> readArtifacts, System.Func> readProtectedArtifacts, System.Action? retireMetadata = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L47) | Borrows publisher callbacks without discovering artifacts from directory names or file extensions.
`id`:The unique registration identity within this cleanup operation.
`directory`:The absolute cache root exclusively managed by this publisher.
`coordinationPath`:The persistent owner lease also acquired by publication and protected-root updates. It must remain within the cache root and is never deleted by cleanup.
`readArtifacts`:Returns only generations proven to belong to this owner, with their dependency metadata. The callback must observe cancellation and must not create, repair or publish artifacts.
`readProtectedArtifacts`:Returns current product and Pack roots; retirement follows their complete dependency closure. Root updates must hold coordinationPath and retain each referenced artifact's reading lease.
`retireMetadata`:Optional owner-specific metadata retirement, called only under exclusive owner coordination.
T:System.ArgumentException:An identity is blank or a path is relative.
T:System.ArgumentNullException:An inventory callback is absent. | -| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.coordinationPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L83) | Gets the persistent lease shared with publication and protected-root changes. | -| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L78) | Gets the exact registered cache root; its parent directories are never managed implicitly. | -| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L73) | Gets the unique publisher identity registered for this cleanup operation. | -| [`public sealed class Inno.Build.Toolchains.BuildCacheOwnerDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L11) | Registers a publisher's exact cache boundary and its current artifact ownership inventory. | - -### `Inno.Build.Toolchains.BuildCachePolicy` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildCachePolicy.BuildCachePolicy(long maximumBytes = 68719476736, long minimumFreeBytes = 21474836480, System.TimeSpan? timeBudget = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L25) | Creates an immutable cache budget and automatic retirement time limit.
`maximumBytes`:The managed cache budget; defaults to 64 GiB.
`minimumFreeBytes`:The free-space threshold on the checkout volume; defaults to 20 GiB.
`timeBudget`:The retirement duration, or null for the five-second default.
T:System.ArgumentOutOfRangeException:A budget is negative or the time limit is not positive. | -| [`public long Inno.Build.Toolchains.BuildCachePolicy.maximumBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L45) | Gets the maximum size of explicitly registered build caches. | -| [`public long Inno.Build.Toolchains.BuildCachePolicy.minimumFreeBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L50) | Gets the free-space threshold below which additional retirement is requested. | -| [`public System.TimeSpan Inno.Build.Toolchains.BuildCachePolicy.timeBudget { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L56) | Gets the duration after which an attempt starts no further retirement work. An already executing filesystem operation finishes before ownership is released. | -| [`public sealed class Inno.Build.Toolchains.BuildCachePolicy`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L8) | Defines bounded retirement pressure without allowing active or current generations to be removed. | - -### `Inno.Build.Toolchains.BuildCachePublisher` - @@ -371,153 +412,43 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildCachePublisher.BuildCachePublisher(string directory, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L34) | Creates a publisher for one absolute, non-overlapping cache boundary.
`directory`:The cache root exclusively controlled by this publisher.
`role`:The lifetime declared for this publisher's completed entries.
T:System.ArgumentException:The directory is blank or relative. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildCachePublisher.AcquireUseAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L64) | Pins publisher activity until publication, reading handoff and current-root updates finish.
`cancellationToken`:Cancels this wait without disturbing other owners.
返回:Caller-owned shared activity; dispose after the final artifact reading lease is acquired.
T:System.OperationCanceledException:Acquisition was canceled. | -| [`public void Inno.Build.Toolchains.BuildCachePublisher.BeginWorkingDirectory(string directory, string leasePath, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L168) | Records a reusable workspace before its first payload write while the caller holds activity and producer leases. Existing unregistered directories are rejected, rather than adopted by cleanup authority.
`directory`:The new or previously registered workspace within this publisher root.
`leasePath`:The caller's persistent exclusive producer lease outside the workspace.
`targetId`:The explicit product target, or null for shared host work.
`cancellationToken`:Cancels registration before payload writes.
T:System.InvalidOperationException:The publisher does not manage reusable workspaces or an existing directory has no current registration.
T:System.IO.IOException:The workspace escapes its boundary or ownership cannot be written. | -| [`public Inno.Build.Toolchains.BuildCacheOwnerDescriptor Inno.Build.Toolchains.BuildCachePublisher.Describe(string id)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L143) | Registers only explicitly recorded ownership, without discovering artifacts by extension or directory age.
`id`:The unique publisher registration identity.
返回:Borrowed inventory callbacks for this publisher's lifetime. | -| [`public void Inno.Build.Toolchains.BuildCachePublisher.Record(string directory, string leasePath, System.Collections.Generic.IReadOnlyList dependencies, string? currentKey, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L105) | Records a completed generation and optionally selects it as a protected current root. The caller must hold this publisher's activity lease and the artifact's producer or reading lease.
`directory`:An existing artifact within this publisher's exact boundary.
`leasePath`:The persistent lease used by its producer and readers.
`dependencies`:Exact immutable generations required while this generation is protected.
`currentKey`:A product/target/configuration root identity, or null for an unselected generation.
`cancellationToken`:Cancels before metadata selection.
`targetId`:The producer's product target, or null for a shared host generation.
T:System.IO.IOException:An owned path escapes the boundary, crosses a directory link, or contains an invalid file link.
T:System.IO.DirectoryNotFoundException:The completed generation is absent.
约束:Reusable workspaces may contain compiler-created file aliases only when their complete link chain resolves to a regular file within the same workspace. Aliases are not counted as duplicate payloads. Immutable artifacts and process working directories reject all links. | -| [`public void Inno.Build.Toolchains.BuildCachePublisher.RecordIndex(string path, System.Collections.Generic.IReadOnlyList dependencies)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L257) | Registers an exact cache index and the publication directories required to keep it useful.
`path`:The producer-owned index file within this publisher root.
`dependencies`:Exact publication roots; missing dependencies make the index retireable.
约束:The caller must hold this publisher's shared activity gate through the index write and registration. | -| [`public void Inno.Build.Toolchains.BuildCachePublisher.RecordProcessUse(string directory, string leasePath, System.Collections.Generic.IReadOnlyList dependencies, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L212) | Registers a process-owned loader directory and its dependencies before returning it to a loader. Retirement preserves the directory until every recorded process has exited.
`directory`:The completed directory within this publisher's boundary.
`leasePath`:The persistent producer and retirement lease outside the directory.
`dependencies`:Immutable generations required by this loader directory.
`cancellationToken`:Cancels ownership acquisition before the process claim is recorded.
T:System.IO.IOException:The directory escapes ownership or its registration cannot be written.
T:System.OperationCanceledException:Ownership acquisition was canceled. | -| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCachePublisher.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L50) | Gets the declared lifecycle used by this publisher's inventory and maintenance registration. | -| [`public sealed class Inno.Build.Toolchains.BuildCachePublisher`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L17) | Records exact publisher-owned generations and coordinates them with bounded cache retirement. | - -### `Inno.Build.Toolchains.BuildHostServices` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildHostServices.BuildHostServices(Inno.Build.Toolchains.IToolchainWorkspaceProvider workspaces, System.StringComparer? environmentComparer = null, System.Collections.Generic.IEnumerable? inputPaths = null, string executableExtension = "")`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L27) | Captures execution policy and its implementation inputs.
`workspaces`:The host's scoped execution path provider.
`environmentComparer`:The host's environment variable name semantics.
`inputPaths`:Checkout-relative implementation files included in native recipe identity.
`executableExtension`:The host executable suffix, or an empty string for suffix-free executable names. | -| [`public System.StringComparer Inno.Build.Toolchains.BuildHostServices.environmentComparer { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L47) | Gets environment variable name semantics. | -| [`public string Inno.Build.Toolchains.BuildHostServices.executableExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L55) | Gets the host-owned executable suffix. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildHostServices.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L51) | Gets checkout-relative execution implementation inputs. | -| [`public Inno.Build.Toolchains.IToolchainWorkspaceProvider Inno.Build.Toolchains.BuildHostServices.workspaces { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L43) | Gets the host's scoped execution path provider. | -| [`public sealed class Inno.Build.Toolchains.BuildHostServices`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L10) | Freezes host execution capabilities independently of a product target SDK. | - -### `Inno.Build.Toolchains.IBuildHostProvider` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.Toolchains.BuildHostDescriptor? Inno.Build.Toolchains.IBuildHostProvider.TryCapture()`](../../build/toolchains/Inno.Build.Toolchains/Host/IBuildHostProvider.cs#L14) | Captures the current execution machine when this provider supports it.
返回:The immutable host, or null when the current machine is outside this provider's capabilities. | -| [`public interface Inno.Build.Toolchains.IBuildHostProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/IBuildHostProvider.cs#L6) | Discovers one explicitly registered host implementation. | - -### `Inno.Build.Toolchains.IToolchainWorkspaceProvider` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.IToolchainWorkspaceProvider.OpenAsync(string physicalPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L29) | Acquires an execution path for an absolute build-owned directory.
`physicalPath`:The retained physical directory.
`cancellationToken`:Cancellation while waiting for exclusive alias ownership.
返回:A scoped path whose processes must finish before disposal. | -| [`string Inno.Build.Toolchains.IToolchainWorkspaceProvider.identity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L15) | Gets the stable execution namespace included in compiler workspace identity. | -| [`public interface Inno.Build.Toolchains.IToolchainWorkspaceProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L10) | Owns the relationship between physical build storage and host tool execution paths. | - -### `Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.PhysicalToolchainWorkspaceProvider()`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L11) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.OpenAsync(string physicalPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L17) | Acquires an execution path for an absolute build-owned directory.
`physicalPath`:The retained physical directory.
`cancellationToken`:Cancellation while waiting for exclusive alias ownership.
返回:A scoped path whose processes must finish before disposal. | -| [`public string Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.identity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L14) | Gets the stable execution namespace included in compiler workspace identity. | -| [`public sealed class Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider : Inno.Build.Toolchains.IToolchainWorkspaceProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L11) | Executes tools directly in their physical owner directory. | - -### `Inno.Build.Toolchains.BuildMaintenanceApplication` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.BuildMaintenanceApplication.CleanAsync(string engineRoot, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll, bool dryRun, string? targetId, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L117) | Uses the same owner inventory, dependency protection and retirement plan for automatic and explicit cleanup.
`engineRoot`:The absolute checkout containing registered publishers.
`policy`:The bounded size and duration policy.
`retireAll`:Whether to select every unreferenced generation instead of pressure candidates.
`dryRun`:Whether to report the plan without retiring it.
`targetId`:An optional exact producer target filter; shared dependencies remain protected.
`cancellationToken`:Cancels inventory and retirement.
返回:A completed, bounded cleanup operation with protected owners retained.
T:System.InvalidOperationException:Explicit retirement encountered failures.
T:System.TimeoutException:Owner discovery or planning exhausts the operation budget before retirement can begin.
T:System.IO.IOException:Ownership metadata or protected input cannot be validated.
T:System.OperationCanceledException:The caller canceled before completion. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.BuildMaintenanceApplication.Run(string[] arguments, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L28) | Executes the cache-clean command using the checkout's explicit publisher catalog.
`arguments`:The clean command and its validated named options.
`cancellationToken`:Cancels planning or retirement without deleting protected outputs.
返回:Zero on success, two on cancellation, or one with a diagnostic on failure. | -| [`public static class Inno.Build.Toolchains.BuildMaintenanceApplication`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L14) | Runs publisher-owned cache maintenance without backend, generator or Editor dependencies. | - -### `Inno.Build.Toolchains.DotNetSdkDescriptor` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.cliPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L32) | Gets the selected SDK's managed CLI entry assembly, invoked with the recorded host without resolving another SDK from a temporary execution directory. | -| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.hostPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L21) | Gets the executable used for project-scoped SDK and workload resolution. | -| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.sdkIdentity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L26) | Gets the exact SDK identity selected by the project's global.json resolution rules. | -| [`public sealed class Inno.Build.Toolchains.DotNetSdkDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L6) | Freezes the host, SDK identity and managed CLI entry selected by one managed entry project. | - -### `Inno.Build.Toolchains.DotNetSdkEnvironment` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.DotNetSdkEnvironment.Create(string hostPath, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkEnvironment.cs#L40) | Freezes child-only overrides so the selected executable resolves the project's own SDK. Null SDK overrides remove inherited variables rather than borrowing a parent's loaded MSBuild.
`hostPath`:The explicitly selected managed executable, resolved to an absolute path before invocation.
`environment`:Additional selected toolchain variables to copy, or null when no native SDK environment is required.
返回:A read-only child environment that preserves selected toolchain settings and never changes the parent.
T:System.ArgumentException:The managed executable is blank.
T:System.IO.FileNotFoundException:The selected managed executable cannot be resolved. | -| [`public static class Inno.Build.Toolchains.DotNetSdkEnvironment`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkEnvironment.cs#L11) | Isolates a managed tool invocation from the invoking IDE's SDK and MSBuild process identity. | - -### `Inno.Build.Toolchains.DotNetSdkResolver` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.DotNetSdkResolver.ResolveAsync(string hostPath, string projectPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs#L35) | Asks the selected host to resolve its SDK from the project location without guessing installed versions.
`hostPath`:The .NET executable selected by the build composition root.
`projectPath`:The prepared entry project whose directory controls SDK selection.
`cancellationToken`:Cancels SDK discovery and drains its child process.
返回:The exact project-selected SDK, managed entry assembly and host; missing SDKs fail explicitly.
T:System.IO.FileNotFoundException:The selected host, prepared entry project or selected SDK entry assembly does not exist.
T:System.InvalidOperationException:The selected host cannot resolve a valid SDK identity and absolute base path. | -| [`public static class Inno.Build.Toolchains.DotNetSdkResolver`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs#L12) | Resolves the managed SDK using the prepared entry project's directory and normal global.json rules. | - -### `Inno.Build.Toolchains.ManagedBuildExecution` - + - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public void Inno.Build.Toolchains.ManagedBuildExecution.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L93) | Releases exclusive workspace ownership after every compiler and source consumer has finished. Repeated disposal has no effect; reusable owned files remain available for the next operation.
T:System.IO.IOException:Completed workspace ownership metadata cannot be recorded; the execution lease is still released. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildExecution.VerifyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L73) | Checks that owned source copies still match the bytes accepted before compilation.
`cancellationToken`:Cancels complete source verification before the consumer publishes its output.
返回:Completion only when every owned input remains unchanged.
T:System.InvalidOperationException:A tool changed a fixed input during execution.
T:System.ObjectDisposedException:This execution no longer owns the workspace. | -| [`public string Inno.Build.Toolchains.ManagedBuildExecution.engineRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L51) | Gets the fixed source root used by compilation and source extraction until disposal. SDKs and restored packages retain their explicitly selected external locations. | -| [`public string Inno.Build.Toolchains.ManagedBuildExecution.project { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L56) | Gets the entry project within this execution's copied source root. | -| [`public sealed class Inno.Build.Toolchains.ManagedBuildExecution : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L17) | Owns a reusable, exclusively leased source workspace containing verified copies of an operation's inputs. | - -### `Inno.Build.Toolchains.ManagedBuildInputSnapshot` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.CaptureAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.DotNetSdkDescriptor sdk, string project, System.Collections.Generic.IReadOnlyDictionary properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L102) | Evaluates an explicit managed graph using its frozen SDK and hashes its declared compilation inputs.
`context`:The borrowed operation that owns phase-local content reads and tool statistics.
`sdk`:The SDK already selected for this project; evaluation must use its exact CLI entry.
`project`:The existing entry project whose references define the managed source closure.
`properties`:The immutable compiler and product properties also passed to the actual managed build.
`cancellationToken`:Cancels SDK evaluation, drains its process and removes this operation's request directory.
返回:A snapshot that can be freshly verified after waiting or executing; no Player compilation is performed.
约束:Restore and evaluation use an owned SDK output layout, independently of ordinary IDE outputs. An explicit ArtifactsPath remains the caller's responsibility, including its concurrency protection.
T:System.ArgumentNullException:A required operation, SDK or property collection is null.
T:System.IO.FileNotFoundException:A required source, compiler, restored package input or SDK declaration is absent.
T:System.IO.IOException:The source entry escapes the physical checkout or a filesystem alias cannot be resolved.
T:System.InvalidOperationException:The selected SDK cannot evaluate the declared graph.
T:System.OperationCanceledException:Evaluation or hashing was canceled. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.CreateExecutionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L59) | Materializes the operation's declared repository inputs into an exclusively owned source workspace. Compilers and source extraction must use the returned project and root, never the mutable checkout.
`cancellationToken`:Cancels ownership acquisition or verified copying before execution ownership is returned.
返回:Caller-owned fixed execution inputs. Dispose only after all compiler and source consumers finish.
T:System.InvalidOperationException:An input differs from its planned bytes while being copied.
T:System.ArgumentException:The project does not belong to the operation's source root. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.VerifyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L140) | Re-evaluates the graph and re-reads its current bytes in a fresh verification phase.
`cancellationToken`:Cancels evaluation and verification without changing any published output.
返回:Completion when the complete current input identity still matches this snapshot.
T:System.InvalidOperationException:The graph, SDK, source, resource, analyzer or reference inputs changed.
T:System.OperationCanceledException:Verification was canceled. | -| [`public string Inno.Build.Toolchains.ManagedBuildInputSnapshot.fingerprint { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L41) | Gets the content identity of the evaluated graph, compiler inputs and selected SDK. | -| [`public sealed class Inno.Build.Toolchains.ManagedBuildInputSnapshot`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L15) | Freezes the selected SDK's evaluated managed source graph without compiling a product. | - -### `Inno.Build.Toolchains.NativeBuildContext` - @@ -539,33 +470,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBuildContext.NativeBuildContext(string engineRoot, string configuration)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L44) | Creates a build context without consulting the tool assembly's checkout.
`engineRoot`:The checkout containing InnoEngine.sln and the native source tree; aliases resolve to one physical owner.
`configuration`:The normalized debug or release configuration.
T:System.ArgumentException:The root is empty or the configuration is unsupported.
T:System.IO.DirectoryNotFoundException:The selected checkout has no repository marker.
T:System.IO.IOException:A checkout alias cannot be resolved to a physical owner. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.BeginInputVerification(string phase)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L401) | Starts a fresh verification phase whose reads can be shared only within this derived context.
`phase`:The operation phase recorded in statistics, such as binding-locks or binding-generation.
返回:A context with a new input inventory and hash cache; existing scopes are unchanged.
约束:Create another phase after each independent ownership wait, generation or compilation. | -| [`public string Inno.Build.Toolchains.NativeBuildContext.GetNativeBuildRoot(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L180) | Resolves native intermediates under the owning toolchain project in this checkout.
`component`:The explicit component descriptor whose recipe project owns native intermediates.
返回:The absolute project-local obj/native path, without creating the directory. During publication, the path also contains the operation's target and compatible workspace identity. A Windows producer receives a temporary junction to the same physical directory so SDK tools can use bounded paths.
T:System.ArgumentNullException:The component owner is null.
T:System.InvalidOperationException:The selected checkout does not contain the owning toolchain project. | -| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordBindingBatch()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L209) | Records one complete binding batch executed by the borrowed provider. | -| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordBindingGeneration()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L214) | Records one actual bridge or managed generator invocation. | -| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordManagedProcess()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L227) | Records a managed SDK process owned by generator extension preparation. | -| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordOutputRead(long bytes)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L222) | Records one full artifact output read without treating it as an input snapshot hash.
`bytes`:The actual number of bytes consumed by output integrity verification. | -| [`public Inno.Build.Toolchains.INativeBindingGenerator Inno.Build.Toolchains.NativeBuildContext.RequireBindingGenerator()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L386) | Requires generation to be configured explicitly before creating staging or launching tools.
返回:The borrowed provider without discovering or constructing an implementation.
T:System.InvalidOperationException:Composition has not supplied a binding generator. | -| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBuildContext.RequireBindings(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L356) | Requires the target binding generation prepared for an explicitly declared Native owner.
`component`:The component whose facade and managed binding share one generation.
返回:The immutable completed generation descriptor.
T:System.InvalidOperationException:The owner has no prepared generation in this operation.
T:System.ObjectDisposedException:The caller released the borrowed binding result before this operation finished. | -| [`public Inno.Build.Toolchains.NativeComponentBuildOptions Inno.Build.Toolchains.NativeBuildContext.RequireComponentOptions(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L316) | Requires product configuration for the exact component before its recipe or tools execute.
`component`:The component requesting its scoped configuration.
返回:The immutable configuration; absent or foreign ownership fails explicitly.
T:System.InvalidOperationException:No options are assigned to this component owner. | -| [`public Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeBuildContext.RequireToolchain()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L277) | Requires an explicit selection before a component can build or start a native tool.
返回:The already frozen tool selection without performing discovery.
T:System.InvalidOperationException:Composition has not resolved a toolchain. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithBindingGenerator(Inno.Build.Toolchains.INativeBindingGenerator generator)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L369) | Borrows a generator while preserving this operation's input and tool ownership.
`generator`:The shared generation implementation selected by composition.
返回:A derived context; neither context owns or disposes the provider. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithBindings(Inno.Build.Toolchains.NativeBindingGenerationResult bindings)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L333) | Creates a scoped operation with an immutable, explicitly prepared target binding closure.
`bindings`:Borrowed completed generations indexed by their Native owners. Retain this result until all derived contexts finish.
返回:A context sharing the existing tool selection, inputs and statistics. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithComponentOptions(Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L295) | Scopes one component's explicit product configuration without changing the frozen SDK.
`component`:The sole source and intermediate owner receiving the configuration.
`options`:The immutable linkage, arguments and configuration input closure.
返回:A context sharing inputs, bindings and statistics with the current operation.
T:System.ArgumentNullException:An owner or configuration is null. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithConfiguration(string configuration)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L265) | Selects an explicit component configuration while borrowing this operation's frozen SDK and input state.
`configuration`:The debug or release configuration owned by the product step.
返回:A derived context with no inherited workspace or artifact identity from another configuration.
T:System.ArgumentException:The requested configuration is unsupported. | -| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithToolchain(Inno.Build.Toolchains.NativeToolchainSelection toolchain)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L247) | Attaches a provider's immutable tool selection while preserving this operation's input ownership.
`toolchain`:The complete selection resolved for the requested target.
返回:A scoped context sharing input snapshots and statistics with this operation.
T:System.ArgumentNullException:The selection is null. | -| [`public string Inno.Build.Toolchains.NativeBuildContext.configuration { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L155) | Gets the normalized debug or release configuration prepared by this operation. | -| [`public string Inno.Build.Toolchains.NativeBuildContext.engineRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L150) | Gets the frozen physical checkout used for every source, intermediate and output path. | -| [`public Inno.Build.Toolchains.NativeBuildStatistics Inno.Build.Toolchains.NativeBuildContext.statistics { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L198) | Gets actual hashing and native execution work accumulated by this operation and its scoped contexts. | -| [`public Inno.Build.Toolchains.NativeToolchainSelection? Inno.Build.Toolchains.NativeBuildContext.toolchain { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L161) | Gets the explicit frozen target compiler and SDK selection, or null before composition resolves it. Browser toolchains resolve their own workload-specific SDK independently. | -| [`public sealed class Inno.Build.Toolchains.NativeBuildContext`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L13) | Identifies the checkout, configuration and frozen initial inputs owned by one native build operation. Create a new context for each operation; changes during its lifetime fail stability verification. | - -### `Inno.Build.Toolchains.BuildHostDescriptor` - @@ -577,83 +481,21 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.BuildHostDescriptor.BuildHostDescriptor(string system, string architecture, Inno.Build.Toolchains.BuildHostServices? services = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L25) | Captures host facts supplied by a product or command composition boundary.
`system`:The execution operating system identity.
`architecture`:The execution processor architecture.
`services`:Explicit host execution services, or null for direct physical execution.
T:System.ArgumentException:A host fact is unassigned. | -| [`public bool Inno.Build.Toolchains.BuildHostDescriptor.Equals(Inno.Build.Toolchains.BuildHostDescriptor? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.BuildHostDescriptor.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.BuildHostDescriptor.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.BuildHostDescriptor.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.BuildHostDescriptor.operator ==(Inno.Build.Toolchains.BuildHostDescriptor? left, Inno.Build.Toolchains.BuildHostDescriptor? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.BuildHostDescriptor.operator !=(Inno.Build.Toolchains.BuildHostDescriptor? left, Inno.Build.Toolchains.BuildHostDescriptor? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.Toolchains.BuildHostDescriptor.architecture { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L45) | Gets the processor architecture on which tools execute. | -| [`public Inno.Build.Toolchains.BuildHostServices Inno.Build.Toolchains.BuildHostDescriptor.services { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L50) | Gets frozen host execution services. | -| [`public string Inno.Build.Toolchains.BuildHostDescriptor.system { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L40) | Gets the operating system on which tools execute. | -| [`public sealed record Inno.Build.Toolchains.BuildHostDescriptor : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Identifies the tool execution machine without selecting any product target. | - -### `Inno.Build.Toolchains.ComponentNativeProductExecutor` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.ComponentNativeProductExecutor.ComponentNativeProductExecutor()`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L13) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public System.Threading.Tasks.Task Inno.Build.Toolchains.ComponentNativeProductExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.ProductNativeBuildPlan plan, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L16) | Builds or reuses the complete product with the operation's frozen tools.
`context`:The frozen operation.
`plan`:The complete dependency-ordered component plan.
`cancellationToken`:Cancels waits and unpublished work.
返回:The owned complete output and binding closure. | -| [`public sealed class Inno.Build.Toolchains.ComponentNativeProductExecutor : Inno.Build.Toolchains.INativeProductExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L13) | Executes portable component producers in their declared dependency order. | - -### `Inno.Build.Toolchains.INativeBindingGenerator` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeBindingGenerator.ComputeInputFingerprintAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBindingGenerationRequest request, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L34) | Computes the selected generator source input identity without producing bindings or compiling a product.
`context`:The borrowed operation with frozen target tools and phase-local input reads.
`request`:The exact component, configuration target and output mode whose source closure is planned.
`cancellationToken`:Cancels configuration loading, dependency evaluation and content hashing.
返回:The complete source preparation identity, distinct from any generated artifact fingerprint. It remains computable when reconstructible generation outputs have been retired.
T:System.InvalidOperationException:The selected tools, configuration, generator or extension inputs cannot be resolved.
T:System.OperationCanceledException:Input planning was canceled. | -| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeBindingGenerator.GenerateAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBindingGenerationRequest request, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L67) | Generates or verifies the requested closure under publication ownership.
`context`:The borrowed operation owning frozen inputs, tool selection and statistics. Configuration path variables must resolve from its selected toolchain without changing process state.
`request`:The immutable target, component closure and publication policy.
`cancellationToken`:Cancels preparation; active work must drain before returning.
返回:An owned complete immutable map indexed by Native owner project, never a partial closure. Dispose the result only after all generated-source and native-bridge consumers finish.
T:System.InvalidOperationException:Inputs change during preparation, a required frozen SDK value is absent, or required generator capabilities are unavailable.
T:System.IO.InvalidDataException:A definition or generated output fails its declared contract.
T:System.OperationCanceledException:The request was canceled and its active work has completed cleanup. | -| [`public interface Inno.Build.Toolchains.INativeBindingGenerator`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L10) | Prepares an entire binding closure without coupling native recipes to a generator or build host. | - -### `Inno.Build.Toolchains.INativeProductExecutor` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.Task Inno.Build.Toolchains.INativeProductExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.ProductNativeBuildPlan plan, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeProductExecutor.cs#L26) | Builds or reuses the complete product with the operation's frozen tools.
`context`:The frozen operation.
`plan`:The complete dependency-ordered component plan.
`cancellationToken`:Cancels waits and unpublished work.
返回:The owned complete output and binding closure. | -| [`public interface Inno.Build.Toolchains.INativeProductExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeProductExecutor.cs#L9) | Executes an explicitly selected native product graph without target discovery. | - -### `Inno.Build.Toolchains.INativeToolchainProvider` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeToolchainProvider.ResolveAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.BuildHostDescriptor host, string targetId, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeToolchainProvider.cs#L29) | Freezes compiler tools, SDK inputs and child environment before a native operation starts.
`context`:The checkout and configuration owned by the requesting operation.
`host`:The explicitly declared execution host, independent of the target.
`targetId`:The exact requested target; unsupported combinations must fail.
`cancellationToken`:Cancels SDK discovery before any product staging.
返回:A complete immutable tool selection for the requested host and target. | -| [`public interface Inno.Build.Toolchains.INativeToolchainProvider`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeToolchainProvider.cs#L9) | Resolves one platform's SDK for an explicit target on a declared tool execution host. | - -### `Inno.Build.Toolchains.NativeArtifactRole` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.Toolchains.NativeArtifactRole.ImportLibrary = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L22) | A separate linker input referring to a shared library. | -| [`Inno.Build.Toolchains.NativeArtifactRole.SharedLibrary = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L18) | A library loaded by the runtime. | -| [`Inno.Build.Toolchains.NativeArtifactRole.StaticArchive = 2`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L26) | An archive linked into the final executable. | -| [`Inno.Build.Toolchains.NativeArtifactRole.SupportFile = 3`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L30) | A tool, header or other supporting payload. | -| [`public enum Inno.Build.Toolchains.NativeArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L13) | Describes the link or deployment role explicitly assigned by a native producer. | - -### `Inno.Build.Toolchains.NativeArtifact` - @@ -665,33 +507,8 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeArtifact.NativeArtifact(string logicalId, string file, Inno.Build.Toolchains.NativeArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L53) | Captures an output independently of platform suffix conventions.
`logicalId`:The unique producer-owned output identity.
`file`:The absolute output path retained by the product's reading ownership.
`role`:The explicitly declared link or deployment role.
T:System.ArgumentException:The identity, path or role is invalid. | -| [`public bool Inno.Build.Toolchains.NativeArtifact.Equals(Inno.Build.Toolchains.NativeArtifact? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeArtifact.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeArtifact.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeArtifact.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeArtifact.operator ==(Inno.Build.Toolchains.NativeArtifact? left, Inno.Build.Toolchains.NativeArtifact? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeArtifact.operator !=(Inno.Build.Toolchains.NativeArtifact? left, Inno.Build.Toolchains.NativeArtifact? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.Toolchains.NativeArtifact.file { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L73) | Gets the exact absolute output path. | -| [`public string Inno.Build.Toolchains.NativeArtifact.logicalId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L69) | Gets the producer-owned output identity. | -| [`public Inno.Build.Toolchains.NativeArtifactRole Inno.Build.Toolchains.NativeArtifact.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L77) | Gets the explicit link or deployment role. | -| [`public sealed record Inno.Build.Toolchains.NativeArtifact : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Identifies one exact output and its producer-declared role. | - -### `Inno.Build.Toolchains.NativeArtifactPublisher` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeArtifactPublisher.PublishAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBuildRecipe recipe, System.Func build, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), System.Func>? describeOutputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifactPublisher.cs#L51) | Reuses a validated product or builds an isolated candidate and publishes it after input stability checks.
`context`:Checkout and configuration selected by build composition.
`recipe`:The frozen component, target, input inventory and ordered compiler recipe.
`build`:Producer receiving an identity-scoped context, private output directory and cancellation token. It must await all work and validate required outputs before returning.
`cancellationToken`:Cancels ownership acquisition or candidate preparation before publication.
`describeOutputs`:The producer's exact role inventory, or null for generic support payloads.
返回:A complete native product with an exact, read-only output closure.
T:System.ArgumentException:A component or target identifier is not a single path segment.
T:System.IO.IOException:An input, candidate or artifact cannot be read or published.
T:System.InvalidOperationException:Inputs change during preparation or the producer returns no complete output.
T:System.OperationCanceledException:Ownership acquisition or preparation is canceled; previous products remain installed. | -| [`public static class Inno.Build.Toolchains.NativeArtifactPublisher`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifactPublisher.cs#L15) | Publishes complete native products by source and toolchain identity under process-shared ownership. | - -### `Inno.Build.Toolchains.NativeBindingCompilation` - @@ -709,33 +526,11 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingCompilation.NativeBindingCompilation()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingCompilation.GetInputs(string engineRoot, string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L179) | Enumerates exact file declarations for standalone or aggregate recipe fingerprinting.
`engineRoot`:The current checkout's absolute root.
`nativeDirectory`:The selected native artifact directory.
返回:All generated, user and SDK inputs with their current physical locations. | -| [`public static Inno.Build.Toolchains.NativeBindingCompilation Inno.Build.Toolchains.NativeBindingCompilation.Load(string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L97) | Reads the current engine-owned compilation description from a selected native output.
`nativeDirectory`:The complete selected generation's native directory.
返回:The validated description; missing or malformed descriptions fail explicitly.
T:System.IO.IOException:The description cannot be read.
T:System.Text.Json.JsonException:The description does not match the current contract.
T:System.IO.InvalidDataException:A root, path or content identity is invalid. | -| [`public string Inno.Build.Toolchains.NativeBindingCompilation.ResolveRoot(string engineRoot, string nativeDirectory, string rootId)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L124) | Resolves a component-local root using the current checkout and selected output.
`engineRoot`:The current checkout's absolute root.
`nativeDirectory`:The selected generation's absolute native output.
`rootId`:An ordinal root identity from this description.
返回:The physical root; no other component's root table participates.
T:System.IO.InvalidDataException:The root identity is unknown. | -| [`public void Inno.Build.Toolchains.NativeBindingCompilation.VerifyInputs(System.Func resolveRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L152) | Verifies the exact bytes at original or remapped roots before their use.
`resolveRoot`:Resolves a root identity to the selected physical or copied directory.
`cancellationToken`:Cancels verification without publishing a result.
T:System.IO.InvalidDataException:A required file has a different length or digest.
T:System.IO.IOException:A required input is missing or cannot be read.
T:System.OperationCanceledException:Verification was canceled. | -| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L57) | Gets the minimum C standard, or zero when no C translation unit is required. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.compilerDefines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L47) | Gets parser target macro facts; these must not override the consuming compiler's builtins. | -| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cxxStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L52) | Gets the minimum C++ standard, or zero when no C++ translation unit is required. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.defines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L42) | Gets ordered definitions for manifest translation units; upstream target configuration remains component-owned. | -| [`public required Inno.Build.Toolchains.NativeBindingSourceFile[] Inno.Build.Toolchains.NativeBindingCompilation.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L32) | Gets all exact input files; only files marked compile are translation units. | -| [`public required Inno.Build.Toolchains.NativeBindingIncludeDirectory[] Inno.Build.Toolchains.NativeBindingCompilation.includeDirectories { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L37) | Gets include requirements in their original search order. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.requiredExports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L62) | Gets the exact native symbols that the component must provide. | -| [`public required Inno.Build.Toolchains.NativeBindingSourceRoot[] Inno.Build.Toolchains.NativeBindingCompilation.roots { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L27) | Gets root bindings whose identities are local to this component. | -| [`public required string Inno.Build.Toolchains.NativeBindingCompilation.staticExportAbi { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L72) | Gets the static table ABI digest, or an empty value when no static table exists. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportNames { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L77) | Gets static table entries in address-resolution order. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportSymbols { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L67) | Gets static table entry points, or an empty array for dynamic-only bindings. | -| [`public sealed class Inno.Build.Toolchains.NativeBindingCompilation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | Describes a selected component's native requirements independently of its source generator. | - -### `Inno.Build.Toolchains.NativeBindingGenerationDescriptor` - + @@ -743,35 +538,9 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor.NativeBindingGenerationDescriptor()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L14) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBindingGenerationDescriptor.Load(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L140) | Reads and validates the result of a completed component generation request.
`path`:The request-owned descriptor file written after successful generation.
返回:The selected generation whose managed source and optional bridge are present.
T:System.IO.InvalidDataException:The descriptor is missing required output identities or contains unavailable outputs.
T:System.Text.Json.JsonException:The descriptor cannot be read as the current contract.
T:System.IO.IOException:An immutable artifact escapes its declared publisher or traverses a linked directory. | -| [`public static void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.ValidateSelection(string path, System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IReadOnlyDictionary linkage)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L81) | Rejects publication when the managed selection differs from the complete native generations to deploy.
`path`:The operation-owned selection consumed during managed compilation.
`generations`:The complete current native project closure and its selected generations.
`linkage`:The exact component linkage used to build the native closure.
T:System.IO.InvalidDataException:The selection differs in project closure, fingerprint, source location, linkage, or structure.
T:System.ArgumentException:The current generation closure has an unsafe or repeated project name.
T:System.ArgumentNullException:The current generation closure is null.
T:System.IO.FileNotFoundException:The managed selection is no longer available.
T:System.Xml.XmlException:The selection is not a valid XML document. | -| [`public void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.Write(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L166) | Atomically writes this completed generation to a request-owned descriptor file.
`path`:The descriptor destination; a unique path keeps concurrent requests independent. | -| [`public static void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.WriteSelection(string path, System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IReadOnlyDictionary linkage)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L38) | Writes the exact native binding identities for a subsequent managed build without reselecting generations. Identical content preserves the existing import timestamp; changed content is installed atomically.
`path`:The MSBuild selection document in an intermediate directory exclusively owned by the caller.
`generations`:Explicit checkout-relative native project paths and their complete selected generations.
`linkage`:Explicit linkage of each selected component, used by its managed native initialization boundary.
T:System.ArgumentException:A project name is unsafe or repeated, or component linkage is invalid or incomplete.
T:System.ArgumentNullException:The generation or linkage closure is null.
T:System.IO.IOException:The selection cannot be read or its complete replacement cannot be installed. | -| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.artifactDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L114) | Gets the immutable artifact root protected by reading leases, or an empty value for mutable host sources. | -| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.artifactOwnerDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L120) | Gets the publisher root coordinating immutable artifact readers and retirement, or empty for host sources. This owner remains unchanged when component sources are copied into a frozen build workspace. | -| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.bindingsPath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L104) | Gets the absolute managed source path selected by the component project. | -| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.fingerprint { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L93) | Gets the immutable input identity assigned to this target generation. | -| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.nativeDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L109) | Gets the complete native artifact and compilation-requirement directory, or empty when none is required. | -| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L14) | Describes the complete binding generation selected by one native build request. | - -### `Inno.Build.Toolchains.NativeBindingOutputMode` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.Toolchains.NativeBindingOutputMode.HostSource = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L16) | Replaces the component's authored host source and bridge outputs together. | -| [`Inno.Build.Toolchains.NativeBindingOutputMode.TargetArtifacts = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L20) | Publishes immutable target outputs under the component's generation fingerprint. | -| [`public enum Inno.Build.Toolchains.NativeBindingOutputMode : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L11) | Selects the publication owner of generated sources. | - -### `Inno.Build.Toolchains.NativeBindingGenerationRequest` - @@ -780,20 +549,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingGenerationRequest.NativeBindingGenerationRequest(System.Collections.Generic.IEnumerable components, string targetId, Inno.Build.Toolchains.NativeBindingOutputMode outputMode, bool checkOnly = false, System.Collections.Generic.IReadOnlyDictionary? expectedFingerprints = null, System.Collections.Generic.IReadOnlyDictionary? componentOptions = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L52) | Captures explicit owners and a target without discovering components or parsing a build project.
`components`:The component owners, each declaring one binding definition.
`targetId`:The target identity selecting a declared configuration; host output uses the host definition.
`outputMode`:Whether to write authored host sources or isolated target artifacts.
`checkOnly`:Whether to compare complete output without publishing a candidate.
`expectedFingerprints`:Optional expected identities indexed by Native project; mismatches reject the request.
`componentOptions`:Explicit component configurations keyed by native owner project, or null for component defaults.
T:System.ArgumentException:A target, owner, output mode or expected identity is invalid or duplicated. | -| [`public bool Inno.Build.Toolchains.NativeBindingGenerationRequest.checkOnly { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L98) | Gets whether publication is forbidden and current output must match fresh generation. | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeBindingGenerationRequest.componentOptions { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L107) | Gets explicit component binding and linkage choices. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBindingGenerationRequest.components { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L86) | Gets the immutable component closure owned by this request. | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeBindingGenerationRequest.expectedFingerprints { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L102) | Gets the expected identities which must match before any candidate is generated. | -| [`public Inno.Build.Toolchains.NativeBindingOutputMode Inno.Build.Toolchains.NativeBindingGenerationRequest.outputMode { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L94) | Gets the selected output ownership policy. | -| [`public string Inno.Build.Toolchains.NativeBindingGenerationRequest.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L90) | Gets the explicit configuration target, independent of the build execution host. | -| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationRequest`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L26) | Freezes the complete binding request before ownership waits or candidate creation. | - -### `Inno.Build.Toolchains.NativeBindingGenerationResult` - @@ -804,22 +559,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingGenerationResult.NativeBindingGenerationResult(System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IEnumerable ownership)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L34) | Freezes a complete generation map and accepts its already acquired reading ownership.
`generations`:The complete Native project to generation mapping.
`ownership`:Protection transferred on successful construction; retained by the caller on failure.
T:System.ArgumentNullException:Either collection is null.
T:System.ArgumentException:A generation or protection entry is null. | -| [`public bool Inno.Build.Toolchains.NativeBindingGenerationResult.ContainsKey(string key)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L93) | Determines whether the read-only dictionary contains an element that has the specified key.
`key`:The key to locate.
T:System.ArgumentNullException:is .
返回:if the read-only dictionary contains an element that has the specified key; otherwise, .
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public void Inno.Build.Toolchains.NativeBindingGenerationResult.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L122) | Releases all protection in reverse acquisition order; repeated disposal has no effect.
T:System.AggregateException:One or more owners failed to release after every owner was attempted. | -| [`public System.Collections.Generic.IEnumerator> Inno.Build.Toolchains.NativeBindingGenerationResult.GetEnumerator()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L114) | Returns an enumerator that iterates through the collection.
返回:An enumerator that can be used to iterate through the collection.
约束:Enumeration and all descriptor consumers must finish before the result's reading protection ends.
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public bool Inno.Build.Toolchains.NativeBindingGenerationResult.TryGetValue(string key, out Inno.Build.Toolchains.NativeBindingGenerationDescriptor value)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L102) | Gets the value that is associated with the specified key.
`key`:The key to locate.
`value`:When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized.
T:System.ArgumentNullException:is .
返回:if the object that implements the interface contains an element that has the specified key; otherwise, .
约束:A returned descriptor is borrowed and must not be used to access files after this result is disposed.
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public int Inno.Build.Toolchains.NativeBindingGenerationResult.Count { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L52) | Gets the number of elements in the collection.
返回:The number of elements in the collection.
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBindingGenerationResult.this[string key] { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L87) | Gets a borrowed descriptor whose outputs remain protected until this result is disposed.
`key`:The checkout-relative Native owner project.
返回:The selected generation; neither its paths nor retained descriptors extend reading ownership.
T:System.ObjectDisposedException:Reading ownership has ended.
T:System.Collections.Generic.KeyNotFoundException:The project is outside this closure. | -| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingGenerationResult.Keys { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L61) | Gets an enumerable collection that contains the keys in the read-only dictionary.
返回:An enumerable collection that contains the keys in the read-only dictionary.
约束:The returned sequence is borrowed and must be consumed before this result is disposed.
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingGenerationResult.Values { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L70) | Gets an enumerable collection that contains the values in the read-only dictionary.
返回:An enumerable collection that contains the values in the read-only dictionary.
约束:Neither the returned sequence nor its descriptors extend the lifetime of reading protection.
T:System.ObjectDisposedException:Reading ownership has ended. | -| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationResult : System.Collections.Generic.IReadOnlyDictionary, System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L14) | Owns the reading protection of a complete binding closure until its final compiler consumer finishes. | - -### `Inno.Build.Toolchains.NativeBindingIncludeDirectory` - @@ -833,35 +572,8 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingIncludeDirectory.NativeBindingIncludeDirectory(string rootId, string relativePath, bool system, bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement.
`rootId`:The component-local input root.
`relativePath`:The root-relative directory, or empty for the root itself.
`system`:Whether this directory contains system headers.
`framework`:Whether the compiler must use framework search semantics. | -| [`public void Inno.Build.Toolchains.NativeBindingIncludeDirectory.Deconstruct(out string rootId, out string relativePath, out bool system, out bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(Inno.Build.Toolchains.NativeBindingIncludeDirectory? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeBindingIncludeDirectory.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBindingIncludeDirectory.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator ==(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator !=(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.framework { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L22) | Whether the compiler must use framework search semantics. | -| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L20) | The root-relative directory, or empty for the root itself. | -| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L19) | The component-local input root. | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.system { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L21) | Whether this directory contains system headers. | -| [`public sealed record Inno.Build.Toolchains.NativeBindingIncludeDirectory : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement.
`rootId`:The component-local input root.
`relativePath`:The root-relative directory, or empty for the root itself.
`system`:Whether this directory contains system headers.
`framework`:Whether the compiler must use framework search semantics. | - -### `Inno.Build.Toolchains.NativeBindingPreparation` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeBindingPreparation.PrepareAsync(Inno.Build.Toolchains.NativeBuildContext context, System.Collections.Generic.IReadOnlyList components, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? componentOptions = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingPreparation.cs#L35) | Requests one binding batch for all generated components of a product.
`context`:The operation owning a frozen target selection and configured generator.
`components`:The declared component closure; components without bindings are excluded.
`cancellationToken`:Cancels generation and drains active work before returning.
`componentOptions`:Exact per-owner component choices, or null for component defaults.
返回:An owned generation closure, empty for a product without bindings. Retain it through final consumption.
T:System.InvalidOperationException:The tool selection or required binding generator is absent. | -| [`public static class Inno.Build.Toolchains.NativeBindingPreparation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingPreparation.cs#L12) | Prepares a coherent target closure through the borrowed generator, without invoking MSBuild. | - -### `Inno.Build.Toolchains.NativeBindingSourceFile` - @@ -876,26 +588,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingSourceFile.NativeBindingSourceFile(string rootId, string relativePath, long length, string sha256, bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation.
`rootId`:The component-local input root.
`relativePath`:The portable root-relative filename.
`length`:The expected byte length.
`sha256`:The lowercase SHA-256 content identity.
`compile`:Whether this file is a translation unit rather than an include dependency. | -| [`public void Inno.Build.Toolchains.NativeBindingSourceFile.Deconstruct(out string rootId, out string relativePath, out long length, out string sha256, out bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(Inno.Build.Toolchains.NativeBindingSourceFile? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeBindingSourceFile.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBindingSourceFile.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator ==(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator !=(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.compile { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L26) | Whether this file is a translation unit rather than an include dependency. | -| [`public long Inno.Build.Toolchains.NativeBindingSourceFile.length { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L24) | The expected byte length. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L23) | The portable root-relative filename. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L22) | The component-local input root. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.sha256 { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L25) | The lowercase SHA-256 content identity. | -| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceFile : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation.
`rootId`:The component-local input root.
`relativePath`:The portable root-relative filename.
`length`:The expected byte length.
`sha256`:The lowercase SHA-256 content identity.
`compile`:Whether this file is a translation unit rather than an include dependency. | - -### `Inno.Build.Toolchains.NativeBindingSourceRoot` - @@ -909,37 +601,9 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBindingSourceRoot.NativeBindingSourceRoot(string id, string directory, bool generated, bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract.
`id`:An ordinal identity scoped to this component's selected generation.
`directory`:The checkout-relative or absolute external location; empty for generated output.
`generated`:Whether this root denotes the selected native output directory.
`frozen`:Whether source files must be copied before compilation. | -| [`public void Inno.Build.Toolchains.NativeBindingSourceRoot.Deconstruct(out string id, out string directory, out bool generated, out bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(Inno.Build.Toolchains.NativeBindingSourceRoot? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeBindingSourceRoot.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBindingSourceRoot.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator ==(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator !=(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.directory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L20) | The checkout-relative or absolute external location; empty for generated output. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.frozen { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L22) | Whether source files must be copied before compilation. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.generated { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L21) | Whether this root denotes the selected native output directory. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.id { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L19) | An ordinal identity scoped to this component's selected generation. | -| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceRoot : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract.
`id`:An ordinal identity scoped to this component's selected generation.
`directory`:The checkout-relative or absolute external location; empty for generated output.
`generated`:Whether this root denotes the selected native output directory.
`frozen`:Whether source files must be copied before compilation. | - -### `Inno.Build.Toolchains.NativeBuildFingerprint` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static string Inno.Build.Toolchains.NativeBuildFingerprint.Create(Inno.Build.Toolchains.NativeBuildContext context, System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L68) | Hashes declarations using the current operation's initial snapshot or explicit fresh verification phase.
`context`:The owner of this phase's complete input inventory and content hashes.
`declarations`:Ordered non-file inputs; declaration order remains significant.
`inputs`:Complete inputs with stable logical identities and physical read locations.
`cancellationToken`:Cancels complete enumeration and hashing.
返回:The SHA-256 identity; repeated physical reads within this phase are reused. | -| [`public static string Inno.Build.Toolchains.NativeBuildFingerprint.Create(System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L39) | Hashes a complete declared input closure independently of checkout location and file timestamps.
`declarations`:Ordered target, configuration, SDK, compiler and linker arguments. Order is significant.
`inputs`:Files and directories with explicit logical identities; overlapping reading locations are hashed once.
`cancellationToken`:Cancels enumeration or complete content hashing before a fingerprint is returned.
返回:A lowercase SHA-256 identity that changes when a declaration, input inventory or input byte changes.
T:System.ArgumentNullException:A required input collection is null.
T:System.IO.IOException:An input cannot be read or contains a directory-link cycle.
T:System.OperationCanceledException:Content hashing was canceled. | -| [`public static class Inno.Build.Toolchains.NativeBuildFingerprint`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L13) | Derives recipe identities from ordered declarations, logical input names and complete source bytes. | - -### `Inno.Build.Toolchains.NativeBuildInput` - @@ -951,23 +615,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBuildInput.NativeBuildInput(string logicalPath, string physicalPath)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L23) | Declares one file or directory consumed by a native recipe.
`logicalPath`:A nonempty, slash-separated identity relative to a declared logical root.
`physicalPath`:The absolute file or directory used only for reading and execution.
T:System.ArgumentException:An identity is empty, contains a control character or backslash, or the physical path is relative. | -| [`public bool Inno.Build.Toolchains.NativeBuildInput.Equals(Inno.Build.Toolchains.NativeBuildInput? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBuildInput.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public static Inno.Build.Toolchains.NativeBuildInput Inno.Build.Toolchains.NativeBuildInput.FromPath(string engineRoot, string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L61) | Declares a checkout-relative input, or an external SDK location whose path affects tool selection.
`engineRoot`:The absolute checkout root assigned the logical prefix engine/.
`path`:A checkout-relative input or an absolute SDK input.
返回:A normalized declaration preserving external SDK location identity. | -| [`public override int Inno.Build.Toolchains.NativeBuildInput.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBuildInput.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBuildInput.operator ==(Inno.Build.Toolchains.NativeBuildInput? left, Inno.Build.Toolchains.NativeBuildInput? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBuildInput.operator !=(Inno.Build.Toolchains.NativeBuildInput? left, Inno.Build.Toolchains.NativeBuildInput? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.Toolchains.NativeBuildInput.logicalPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L42) | Gets the identity included in the recipe fingerprint. | -| [`public string Inno.Build.Toolchains.NativeBuildInput.physicalPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L47) | Gets the reading location, which is not implicitly included in the fingerprint. | -| [`public sealed record Inno.Build.Toolchains.NativeBuildInput : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Separates the reproducible identity of an input from its physical reading location. | - -### `Inno.Build.Toolchains.NativeBuildPhaseStatistics` - @@ -980,24 +627,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBuildPhaseStatistics.NativeBuildPhaseStatistics()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.Equals(Inno.Build.Toolchains.NativeBuildPhaseStatistics? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeBuildPhaseStatistics.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBuildPhaseStatistics.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.operator ==(Inno.Build.Toolchains.NativeBuildPhaseStatistics? left, Inno.Build.Toolchains.NativeBuildPhaseStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.operator !=(Inno.Build.Toolchains.NativeBuildPhaseStatistics? left, Inno.Build.Toolchains.NativeBuildPhaseStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.bytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L19) | Gets the number of bytes actually read for hashing. | -| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L15) | Gets the number of complete physical file reads. | -| [`public required string Inno.Build.Toolchains.NativeBuildPhaseStatistics.phase { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L11) | Gets the named operation phase which owns these read results. | -| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.reusedReads { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L23) | Gets repeated physical hash requests satisfied from this phase's cache. | -| [`public sealed record Inno.Build.Toolchains.NativeBuildPhaseStatistics : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Describes actual full reads and physical read reuse within one explicitly bounded input phase. | - -### `Inno.Build.Toolchains.NativeBuildProduct` - @@ -1008,22 +637,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public void Inno.Build.Toolchains.NativeBuildProduct.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L88) | Releases the artifact after all deployment, compilation and linking consumers finish. File paths must not be used afterwards; repeated disposal has no effect. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildProduct.artifacts { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L82) | Gets the exact producer-declared output and link roles. | -| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor? Inno.Build.Toolchains.NativeBuildProduct.bindingGeneration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L67) | Gets the exact binding generation compiled into this product, or null for a component without bindings. Managed consumers must retain this selection rather than resolve a new generation after native preparation. | -| [`public string Inno.Build.Toolchains.NativeBuildProduct.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L46) | Gets the component identity supplied by the owning toolchain. | -| [`public string Inno.Build.Toolchains.NativeBuildProduct.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L61) | Gets the immutable artifact root containing Outputs and its integrity manifest. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildProduct.files { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L77) | Gets the exact absolute output file paths validated by the publisher. | -| [`public string Inno.Build.Toolchains.NativeBuildProduct.fingerprint { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L56) | Gets the content fingerprint covering sources, tool executables and toolchain declarations. | -| [`public string? Inno.Build.Toolchains.NativeBuildProduct.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L72) | Gets the shared-library suffix declared by the selected SDK, or null for an SDK-independent producer. | -| [`public string Inno.Build.Toolchains.NativeBuildProduct.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L51) | Gets the target ABI selected for this build. | -| [`public sealed class Inno.Build.Toolchains.NativeBuildProduct : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L11) | Identifies one complete, integrity-checked native artifact and its exact published file closure. | - -### `Inno.Build.Toolchains.NativeBuildRecipe` - @@ -1033,21 +646,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBuildRecipe.NativeBuildRecipe(Inno.Build.Toolchains.NativeComponentDescriptor owner, string component, string targetId, System.Collections.Generic.IEnumerable inputs, System.Collections.Generic.IEnumerable declarations)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L38) | Creates a complete recipe without inferring implementation identity from an assembly MVID.
`owner`:The explicit component descriptor identifying source and intermediate owners.
`component`:The component's portable artifact path segment.
`targetId`:The ABI's portable artifact path segment.
`inputs`:The complete source, generator, executor and SDK input closure.
`declarations`:Ordered compiler, linker, include-search and non-file inputs; order is significant.
T:System.ArgumentException:A component or target is not a portable segment, or an input is null.
T:System.ArgumentNullException:A required owner or collection is null. | -| [`public static Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.CreateForComponent(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor owner, string component, string targetId, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable implementationPaths)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L120) | Declares a component's source, execution and SDK inputs before binding outputs need to exist. The artifact publisher attaches the exact selected generation before hashing or compiling.
`context`:The operation's checkout and previously selected host tools.
`owner`:The component project whose implementation participates in recipe identity.
`component`:The native component artifact segment.
`targetId`:The selected target ABI.
`inputPaths`:All handwritten native sources, component tools and build definitions. Selected managed bindings and generated bridges are attached by the publisher.
`declarations`:Ordered component arguments and generation identities.
`implementationPaths`:The complete explicit implementation inputs used by this component, excluding unrelated product code.
返回:The frozen component declaration, without generating bindings or reading generated output. Unrelated build and Editor implementation assemblies do not affect it.
T:System.IO.FileNotFoundException:A required executor or component project is absent from the checkout. | -| [`public Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.WithDependency(Inno.Build.Toolchains.NativeBuildInput input, string fingerprint)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L190) | Adds one explicitly selected published dependency while preserving the recipe's frozen execution tools.
`input`:The dependency file whose bytes participate in compilation.
`fingerprint`:The immutable dependency artifact identity selected by the product.
返回:A new declaration with the dependency input and identity; this recipe is unchanged.
T:System.ArgumentException:The dependency identity is empty.
T:System.ArgumentNullException:The input is null. | -| [`public string Inno.Build.Toolchains.NativeBuildRecipe.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L70) | Gets the component's artifact path segment. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildRecipe.declarations { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L85) | Gets ordered non-file inputs; callers must explicitly sort unordered metadata before construction. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildRecipe.inputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L80) | Gets the frozen declarations of physical inputs and their logical identities. | -| [`public Inno.Build.Toolchains.NativeComponentDescriptor Inno.Build.Toolchains.NativeBuildRecipe.owner { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L65) | Gets the declared intermediate owner independently of assembly location and MVID. | -| [`public string Inno.Build.Toolchains.NativeBuildRecipe.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L75) | Gets the target ABI's artifact path segment. | -| [`public sealed class Inno.Build.Toolchains.NativeBuildRecipe`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L11) | Freezes a component's input closure, ordered tool selection and intermediate owner. | - -### `Inno.Build.Toolchains.NativeBuildStatistics` - @@ -1066,58 +664,14 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeBuildStatistics.NativeBuildStatistics()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public bool Inno.Build.Toolchains.NativeBuildStatistics.Equals(Inno.Build.Toolchains.NativeBuildStatistics? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.Toolchains.NativeBuildStatistics.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Determines whether the specified object is equal to the current object.
`obj`:The object to compare with the current object.
返回:if the specified object is equal to the current object; otherwise, . | -| [`public override int Inno.Build.Toolchains.NativeBuildStatistics.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Serves as the default hash function.
返回:A hash code for the current object. | -| [`public override string Inno.Build.Toolchains.NativeBuildStatistics.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Returns a string that represents the current object.
返回:A string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBuildStatistics.operator ==(Inno.Build.Toolchains.NativeBuildStatistics? left, Inno.Build.Toolchains.NativeBuildStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.Toolchains.NativeBuildStatistics.operator !=(Inno.Build.Toolchains.NativeBuildStatistics? left, Inno.Build.Toolchains.NativeBuildStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.bindingBatches { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L34) | Gets complete binding batches requested by this operation. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.bindingGenerations { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L39) | Gets actual bridge or managed generation invocations, excluding valid artifact reuse. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.hashedBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L18) | Gets the actual bytes consumed by input hashing, excluding output integrity validation. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.hashedFiles { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L13) | Gets the number of complete file hashes read, including required stability verification. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.managedProcesses { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L59) | Gets managed SDK processes started for generator extension preparation and managed input evaluation. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.materializedBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L44) | Gets bytes copied to freeze mutable repository inputs. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.nativeProcesses { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L24) | Gets native execution processes started through the context's frozen tool selection. SDK discovery and managed task bootstrapping are recorded by their separate build logs. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.outputBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L54) | Gets bytes read from actual outputs, separately from source snapshot verification. | -| [`public long Inno.Build.Toolchains.NativeBuildStatistics.outputFiles { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L49) | Gets independent artifact output hashes performed for integrity verification and publication. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildStatistics.phases { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L29) | Gets immutable phase-specific read accounting accumulated by this operation. | -| [`public sealed record Inno.Build.Toolchains.NativeBuildStatistics : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Reports actual input reading and compiler process work accumulated by one build context. | - -### `Inno.Build.Toolchains.NativeCMakeExecutor` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeCMakeExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor component, string sourceDirectory, string? target, System.Collections.Generic.IReadOnlyList componentArguments, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L86) | Configures and builds an explicit component target without detecting the current platform.
`context`:The recipe-owned producer scope supplied by NativeArtifactPublisher, with frozen tools and inputs.
`component`:The unique component owner of target-scoped intermediate output.
`sourceDirectory`:The declared component-owned source directory, resolved to the operation's independent copy.
`target`:The declared CMake build target, or null for the complete selected project.
`componentArguments`:Semantic component options; SDK and generator arguments come from the selection.
`cancellationToken`:Cancels and drains the complete tool process tree.
返回:The owned CMake build directory after successful compilation.
T:System.InvalidOperationException:Tools are unassigned or configuration or compilation fails.
T:System.OperationCanceledException:The operation was canceled.
T:System.ArgumentException:Product definitions conflict with a semantic definition owned by the component recipe. | -| [`public static string Inno.Build.Toolchains.NativeCMakeExecutor.FindOutput(Inno.Build.Toolchains.NativeBuildContext context, string buildDirectory, string filePattern)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L171) | Selects one declared output while excluding CMake's compiler probes and other build configurations.
`context`:The selected configuration and target generator policy.
`buildDirectory`:The component-owned CMake intermediate directory.
`filePattern`:The component's exact basename or bounded filename pattern.
返回:The sole matching output, or an explicit failure for a missing or ambiguous result.
T:System.IO.FileNotFoundException:The component did not produce exactly one expected output. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeCMakeExecutor.PrepareSourceAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor component, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L39) | Copies the recipe's declared checkout inputs before a cold CMake invocation.
`context`:The producer scope supplied by NativeArtifactPublisher while holding its intermediate lease.
`component`:The sole owner of the intermediate source copy.
`cancellationToken`:Cancels copying before the source candidate is installed.
返回:The same prepared input descriptor for subsequent invocations in this producer scope.
T:System.InvalidOperationException:No recipe scope exists or an input changed during copying.
T:System.OperationCanceledException:Copying was canceled before publication.
T:System.ArgumentNullException:The context or component is null. | -| [`public static class Inno.Build.Toolchains.NativeCMakeExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L13) | Executes component-owned CMake definitions using an already frozen target SDK selection. | - -### `Inno.Build.Toolchains.NativeCMakeSource` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public string Inno.Build.Toolchains.NativeCMakeSource.ResolveDefinition(string argument)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L94) | Resolves declared input paths in one CMake definition without redirecting output or SDK locations.
`argument`:An ordered CMake argument; definition values may contain semicolon-separated paths.
返回:The definition with known input paths redirected, or the original nondefinition argument.
T:System.ArgumentNullException:The argument is null. | -| [`public string Inno.Build.Toolchains.NativeCMakeSource.ResolvePath(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L69) | Resolves a declared checkout or selected binding input to its copied counterpart.
`path`:An absolute recipe input path. External SDK paths retain their original location.
返回:The copied checkout or binding input, or the unchanged external SDK location.
T:System.ArgumentException:The path is empty or relative.
T:System.InvalidOperationException:A checkout or selected binding path was not declared by the recipe. | -| [`public string Inno.Build.Toolchains.NativeCMakeSource.root { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L52) | Gets the isolated source root; this is neither the checkout nor an output installation directory. | -| [`public sealed class Inno.Build.Toolchains.NativeCMakeSource`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L15) | Describes copied recipe inputs owned by one cold CMake producer. External SDK locations remain assigned to the frozen toolchain. | - -### `Inno.Build.Toolchains.NativeCompilerInputs` - @@ -1127,33 +681,9 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeCompilerInputs.NativeCompilerInputs(string? cCompiler = null, string? cppCompiler = null, string? sysRoot = null, System.Collections.Generic.IEnumerable? systemIncludeDirectories = null, System.Collections.Generic.IEnumerable? cppSystemIncludeDirectories = null, System.Collections.Generic.IEnumerable? defines = null, System.Collections.Generic.IEnumerable? arguments = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L40) | Captures explicit parser inputs supplied by the target SDK provider.
`cCompiler`:The selected C compiler, or null when this selection does not provide one.
`cppCompiler`:The selected C++ compiler, or null when this selection does not provide one.
`sysRoot`:The SDK root, or null when the compiler does not use a sysroot.
`systemIncludeDirectories`:Ordered system header directories.
`cppSystemIncludeDirectories`:Ordered C++ standard library directories.
`defines`:Ordered target preprocessor definitions.
`arguments`:Ordered parser arguments supplied by the SDK.
T:System.ArgumentException:An assigned physical path is not absolute. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.arguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L85) | Gets ordered target parser arguments. | -| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.cCompiler { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L61) | Gets the explicit C compiler, or null when unavailable. | -| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.cppCompiler { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L65) | Gets the explicit C++ compiler, or null when unavailable. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.cppSystemIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L77) | Gets ordered C++ standard library directories. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.defines { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L81) | Gets ordered target definitions. | -| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.sysRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L69) | Gets the target SDK root, or null when unnecessary. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.systemIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L73) | Gets ordered SDK header directories. | -| [`public sealed class Inno.Build.Toolchains.NativeCompilerInputs`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L11) | Freezes SDK-resolved native compiler and header inputs without interpreting environment conventions. | - -### `Inno.Build.Toolchains.NativeLibraryKind` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.Toolchains.NativeLibraryKind.Shared = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L21) | Produces libraries loaded by the deployed product. | -| [`Inno.Build.Toolchains.NativeLibraryKind.Static = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L16) | Produces archives for the selected final linker. | -| [`public enum Inno.Build.Toolchains.NativeLibraryKind : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L11) | Specifies the linkage required by a product's native component operation. | - -### `Inno.Build.Toolchains.NativeComponentBuildOptions` - @@ -1162,20 +692,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeComponentBuildOptions.NativeComponentBuildOptions(Inno.Build.Toolchains.NativeLibraryKind libraryKind, System.Collections.Generic.IEnumerable? cmakeArguments = null, System.Collections.Generic.IEnumerable? inputPaths = null, string? bindingLibraryName = null, System.Collections.Generic.IEnumerable? bindingIncludeDirectories = null, Inno.Build.Toolchains.NativeLinkPlan? linkPlan = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L55) | Captures component configuration without selecting an SDK or starting a build.
`libraryKind`:The explicitly requested linkage; a recipe must support it before execution starts.
`cmakeArguments`:Ordered, unique CMake definitions belonging to this component, excluding SDK and linkage selection.
`inputPaths`:Portable checkout-relative configuration files included in content hashing and stability checks.
`linkPlan`:Component-specific platform link settings, or null for the selected SDK plan.
`bindingLibraryName`:The explicit native import identity, or null to retain the component identity.
`bindingIncludeDirectories`:Additional checkout-relative binding headers owned by an integration.
T:System.ArgumentException:Linkage is invalid, definitions are ambiguous or override SDK selection, or an input escapes the checkout. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.bindingIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L116) | Gets additional checkout-relative binding header roots. | -| [`public string? Inno.Build.Toolchains.NativeComponentBuildOptions.bindingLibraryName { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L106) | Gets the explicit native import identity, or null for the component default. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L96) | Gets the immutable ordered component definitions; order contributes to artifact identity. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L101) | Gets logical configuration input paths whose actual bytes participate in the recipe. | -| [`public Inno.Build.Toolchains.NativeLibraryKind Inno.Build.Toolchains.NativeComponentBuildOptions.libraryKind { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L91) | Gets the linkage selected by the product rather than by the execution host. | -| [`public Inno.Build.Toolchains.NativeLinkPlan? Inno.Build.Toolchains.NativeComponentBuildOptions.linkPlan { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L111) | Gets component-specific platform link settings, or null for the target defaults. | -| [`public sealed class Inno.Build.Toolchains.NativeComponentBuildOptions`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L27) | Freezes product-owned linkage, ordered component definitions and their configuration inputs. | - -### `Inno.Build.Toolchains.NativeComponentDescriptor` - @@ -1185,47 +701,13 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeComponentDescriptor.NativeComponentDescriptor(string id, string nativeProject, string toolchainProject, Inno.Build.Toolchains.NativeStaticBuildDefinition? staticBuild = null, string? bindingDefinition = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L33) | Freezes portable checkout-relative owner locations without assembly-name or directory inference.
`id`:The portable component artifact identity.
`nativeProject`:The checkout-relative native project file.
`toolchainProject`:The checkout-relative build implementation project file.
`staticBuild`:The backend-owned static recipe, or null when this component has none.
`bindingDefinition`:The portable binding definition, or null when the component generates no bindings.
T:System.ArgumentException:An identity or path is empty, absolute, or escapes its checkout. | -| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.GetNativeRoot(string engineRoot)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L90) | Resolves the declared native owner within an explicitly supplied checkout.
`engineRoot`:The absolute operation-owned checkout.
返回:The absolute native project directory, without creating it. | -| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.GetToolchainRoot(string engineRoot)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L101) | Resolves the declared recipe owner within an explicitly supplied checkout.
`engineRoot`:The absolute operation-owned checkout.
返回:The absolute toolchain project directory, without creating it. | -| [`public string? Inno.Build.Toolchains.NativeComponentDescriptor.bindingDefinition { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L79) | Gets the declared binding definition, or null for a component without generated bindings. | -| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L59) | Gets the portable artifact identity. | -| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.nativeProject { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L64) | Gets the explicit checkout-relative Native owner project. | -| [`public Inno.Build.Toolchains.NativeStaticBuildDefinition? Inno.Build.Toolchains.NativeComponentDescriptor.staticBuild { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L74) | Gets the declared static build capability without requiring a platform-specific component project. | -| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.toolchainProject { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L69) | Gets the explicit checkout-relative recipe owner project. | -| [`public sealed class Inno.Build.Toolchains.NativeComponentDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L10) | Declares the unique source and build owners of a reusable native component. | - -### `Inno.Build.Toolchains.NativeDeploymentMetadataFactory` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeDeploymentMetadataFactory.NativeDeploymentMetadataFactory(object @object, nint method)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public virtual System.IAsyncResult Inno.Build.Toolchains.NativeDeploymentMetadataFactory.BeginInvoke(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files, System.AsyncCallback callback, object @object)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public virtual System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory.EndInvoke(System.IAsyncResult result)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public virtual System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory.Invoke(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Creates explicitly selected deployment metadata from a frozen Native closure.
`products`:Borrowed complete products whose leases remain held through metadata preparation.
`files`:Exact relative deployment paths mapped to verified source files.
返回:Relative metadata paths mapped to immutable bytes; collisions with payloads are rejected. | -| [`public delegate System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Creates explicitly selected deployment metadata from a frozen Native closure.
`products`:Borrowed complete products whose leases remain held through metadata preparation.
`files`:Exact relative deployment paths mapped to verified source files.
返回:Relative metadata paths mapped to immutable bytes; collisions with payloads are rejected. | - -### `Inno.Build.Toolchains.NativeInputMaterializer` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.NativeInputMaterializer.CopyAsync(Inno.Build.Toolchains.NativeBuildContext context, string source, string destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeInputMaterializer.cs#L39) | Copies a declared input atomically when its destination differs, preserving identical file timestamps.
`context`:The operation whose initial source bytes and hashing statistics are shared across components.
`source`:The absolute input file already declared by the component recipe.
`destination`:The absolute intermediate file owned exclusively by the component producer.
`cancellationToken`:Cancels copying before publication and preserves the prior intermediate file.
返回:True when bytes were copied and published; false when the existing content was identical.
T:System.InvalidOperationException:The source changes relative to the operation's frozen input snapshot.
T:System.IO.IOException:The source, destination or atomic candidate cannot be read or published. | -| [`public static class Inno.Build.Toolchains.NativeInputMaterializer`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeInputMaterializer.cs#L13) | Materializes frozen native inputs without rewriting identical intermediate files. | - -### `Inno.Build.Toolchains.NativeLinkPlan` - @@ -1234,50 +716,15 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeLinkPlan.NativeLinkPlan(string? sharedLibraryExtension = null, string? importLibraryExtension = null, string? staticLibraryExtension = null, System.Collections.Generic.IEnumerable? cmakeArguments = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L30) | Captures explicitly supported library formats and ordered linker configuration.
`sharedLibraryExtension`:The shared-library suffix, or null when shared linkage is unsupported.
`importLibraryExtension`:The separate import-library suffix, or null when the shared library is the link input.
`staticLibraryExtension`:The archive suffix, or null when static linkage is not contributed.
`cmakeArguments`:Ordered platform-owned CMake linker definitions.
T:System.ArgumentException:No format is supported, an import format lacks shared linkage, a suffix is invalid, or an argument is null. | -| [`public string Inno.Build.Toolchains.NativeLinkPlan.GetFileExtension(Inno.Build.Toolchains.NativeLibraryKind kind)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L95) | Resolves an explicitly supported linkage format before staging or generation begins.
`kind`:The component's requested linkage.
返回:The selected file extension; unsupported linkage fails immediately.
T:System.NotSupportedException:The platform does not contribute this linkage format.
T:System.ArgumentOutOfRangeException:The linkage kind is not defined. | -| [`public string Inno.Build.Toolchains.NativeLinkPlan.GetStaticFileName(string logicalName)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L78) | Formats an explicitly declared logical archive using the selected platform format.
`logicalName`:The component-owned logical archive name.
返回:The exact platform file name.
T:System.NotSupportedException:Static linkage has no registered archive format. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeLinkPlan.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L64) | Gets ordered platform linker definitions. | -| [`public string? Inno.Build.Toolchains.NativeLinkPlan.importLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L56) | Gets the separate import suffix, or null when no separate import library is used. | -| [`public string? Inno.Build.Toolchains.NativeLinkPlan.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L52) | Gets the shared-library suffix, or null when shared linkage is unsupported. | -| [`public string? Inno.Build.Toolchains.NativeLinkPlan.staticLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L60) | Gets the static archive suffix, or null when static linkage is unavailable. | -| [`public sealed class Inno.Build.Toolchains.NativeLinkPlan`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L10) | Freezes platform library formats and linker configuration independently of component recipes. | - -### `Inno.Build.Toolchains.NativeStaticBuildDefinition` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeStaticBuildDefinition.NativeStaticBuildDefinition(string cmakeFile, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable logicalOutputs, string? nativeSourceDirectory = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L33) | Freezes the component's CMake entry point, source dependencies and exact installed archive names.
`cmakeFile`:The portable checkout-relative component definition.
`inputPaths`:The portable checkout-relative source and build dependencies.
`logicalOutputs`:The exact archive file names required after installation.
`nativeSourceDirectory`:The declared checkout-relative root used by the component's native CMake fragment, or null when no such fragment is consumed.
T:System.ArgumentException:A path escapes its root or an archive name is invalid or duplicated.
T:System.ArgumentNullException:A required collection is null. | -| [`public string Inno.Build.Toolchains.NativeStaticBuildDefinition.cmakeFile { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L58) | Gets the component-owned CMake definition, which participates in artifact identity. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeStaticBuildDefinition.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L63) | Gets the immutable complete source dependency paths relative to the checkout. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeStaticBuildDefinition.logicalOutputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L73) | Gets the exact required output closure within this component's target directory. | -| [`public string? Inno.Build.Toolchains.NativeStaticBuildDefinition.nativeSourceDirectory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L68) | Gets the declared native fragment's source root, or null when the recipe only consumes its other inputs. | -| [`public sealed class Inno.Build.Toolchains.NativeStaticBuildDefinition`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L10) | Declares a backend-owned static-library recipe and its complete checkout input closure. | - -### `Inno.Build.Toolchains.NativeToolchainPreparation` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Collections.Generic.Dictionary Inno.Build.Toolchains.NativeToolchainPreparation.CreateEnvironment()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L26) | Creates a child-process environment that clears undeclared ambient compiler policy.
返回:A caller-owned dictionary to populate with explicitly selected SDK variables. | -| [`public static Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeToolchainPreparation.Freeze(string targetId, Inno.Build.Toolchains.BuildHostDescriptor host, System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IReadOnlyDictionary environment, System.Collections.Generic.IEnumerable inputs, System.Collections.Generic.IEnumerable cmakeArguments, Inno.Build.Toolchains.NativeLinkPlan linkPlan, bool multiConfiguration, Inno.Build.Toolchains.NativeCompilerInputs? compilerInputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L65) | Validates and freezes a provider's complete tool selection before product staging.
`targetId`:The explicit requested target.
`host`:The declared tool execution host.
`tools`:Selected executable paths.
`environment`:Isolated child environment.
`inputs`:Selected compiler and SDK inputs.
`cmakeArguments`:Ordered target configuration arguments.
`multiConfiguration`:Whether configuration is chosen during build.
`linkPlan`:The sole source of target library formats and linker capabilities.
`compilerInputs`:The selected SDK parser inputs, or null for a selection without parsing capability.
返回:An immutable selection with validated physical tool and SDK inputs.
T:System.IO.FileNotFoundException:A selected SDK input or executable is absent. | -| [`public static class Inno.Build.Toolchains.NativeToolchainPreparation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L11) | Supplies shared environment isolation and SDK input validation without selecting a platform. | - -### `Inno.Build.Toolchains.NativeToolchainSelection` - @@ -1293,27 +740,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.NativeToolchainSelection.NativeToolchainSelection(string targetId, Inno.Build.Toolchains.BuildHostDescriptor host, System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IReadOnlyDictionary environment, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable cmakeArguments, Inno.Build.Toolchains.NativeLinkPlan linkPlan, bool multiConfiguration, Inno.Build.Toolchains.NativeCompilerInputs? compilerInputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L55) | Captures an immutable, explicit compiler and SDK selection supplied by a platform provider.
`targetId`:The exact native target identity.
`host`:The machine on which the selected tools execute.
`tools`:Absolute executable paths indexed by explicit command names.
`environment`:Frozen SDK values applied to owned tools and in-process binding configuration without changing process state.
`inputPaths`:Compiler, SDK and tool files participating in product identity.
`cmakeArguments`:Ordered configuration arguments pinning compiler, generator and SDK.
`multiConfiguration`:Whether configuration is selected during build rather than configure.
`linkPlan`:The sole source of target library formats and linker capabilities.
`compilerInputs`:Explicit SDK parser inputs, or null when no native parser is provided.
T:System.ArgumentException:The selection is incomplete or has unassigned target facts.
T:System.ArgumentNullException:A required descriptor or collection is null.
T:System.IO.FileNotFoundException:A selected executable or SDK input is unavailable before preparation begins. | -| [`public string Inno.Build.Toolchains.NativeToolchainSelection.ResolveExecutable(string name)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L212) | Resolves an executable from the frozen selection rather than a mutable process PATH.
`name`:The explicit command registered by the platform provider.
返回:The selected executable; an absent command fails immediately.
T:System.InvalidOperationException:The provider did not register the requested command. | -| [`public bool Inno.Build.Toolchains.NativeToolchainSelection.TryResolveExecutable(string name, out string executable)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L231) | Checks an optional tool capability without discovering a command from the process environment.
`name`:The declared tool identity to check.
`executable`:The frozen absolute path when present, or an empty string when absent.
返回:True only when this selection explicitly supplies the requested tool.
T:System.ArgumentException:The tool identity is empty. | -| [`public Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeToolchainSelection.WithTools(System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IEnumerable inputs)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L183) | Adds integration-owned tools without changing the platform SDK or replacing existing commands.
`tools`:Explicit absolute executables required by the selected backend integration.
`inputs`:Integration implementation and configuration files participating in tool selection.
返回:A new immutable selection containing both the platform and integration inputs.
T:System.ArgumentException:A contribution conflicts with an already selected command.
T:System.IO.FileNotFoundException:A contributed tool or input does not exist. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L135) | Gets configuration arguments selecting the compiler, generator and SDK. | -| [`public Inno.Build.Toolchains.NativeCompilerInputs Inno.Build.Toolchains.NativeToolchainSelection.compilerInputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L158) | Gets the target provider's explicit parser inputs. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.declarations { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L130) | Gets ordered tool and environment facts included in the recipe fingerprint. | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeToolchainSelection.environment { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L140) | Gets frozen SDK values supplied to owned tools and in-process binding configuration. | -| [`public Inno.Build.Toolchains.BuildHostDescriptor Inno.Build.Toolchains.NativeToolchainSelection.host { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L120) | Gets the declared tool execution host. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L125) | Gets the immutable SDK and compiler input closure. | -| [`public Inno.Build.Toolchains.NativeLinkPlan Inno.Build.Toolchains.NativeToolchainSelection.linkPlan { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L163) | Gets the platform-owned linker capabilities. | -| [`public bool Inno.Build.Toolchains.NativeToolchainSelection.multiConfiguration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L153) | Gets whether the selected generator supports build-time configuration selection. | -| [`public string Inno.Build.Toolchains.NativeToolchainSelection.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L148) | Gets the target's shared-library suffix.
T:System.NotSupportedException:The target does not support shared libraries. | -| [`public string Inno.Build.Toolchains.NativeToolchainSelection.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L115) | Gets the target identity, without inferring it from the execution host. | -| [`public sealed class Inno.Build.Toolchains.NativeToolchainSelection`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L12) | Freezes target compiler tools and SDK inputs independently of discovery and platform policy. | - -### `Inno.Build.Toolchains.ProductNativeBuildPlan` - @@ -1321,19 +747,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.ProductNativeBuildPlan.ProductNativeBuildPlan(string productId, System.Collections.Generic.IReadOnlyList steps, Inno.Build.Toolchains.NativeDeploymentMetadataFactory? deploymentMetadata = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L36) | Validates the complete ordered component graph before tools or staging can start.
`productId`:The product identity whose native closure is selected.
`steps`:Unique, dependency-ordered component operations.
`deploymentMetadata`:Optional product-owned metadata preparation, without backend discovery.
T:System.ArgumentException:The product, component identity or dependency ordering is invalid.
T:System.ArgumentNullException:The step collection is null. | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.ProductNativeBuildPlan.CreateDeploymentFiles(System.Collections.Generic.IReadOnlyList products)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L133) | Resolves exact deployment paths from the declared layout rather than component-name conventions.
`products`:The complete result of this plan, in declaration order.
返回:Owned immutable relative-output to source-file mappings for atomic deployment.
T:System.ArgumentException:Products differ from the plan or deployment paths collide or escape their root. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildPlan.CreateRecipes(Inno.Build.Toolchains.NativeBuildContext context)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L91) | Resolves the complete selected source recipe closure before binding generation or native compilation.
`context`:The borrowed operation with a frozen target SDK; each step supplies its own component options.
返回:Immutable recipes in declaration order, including each component's selected tools and parameters. Actual generated bytes and dependency artifacts are added by the producer before publication.
T:System.ArgumentNullException:The context is null.
T:System.InvalidOperationException:Tools are unassigned or a declaration returns a foreign owner, component or target.
T:System.NotSupportedException:A static component does not support the selected target. | -| [`public Inno.Build.Toolchains.NativeDeploymentMetadataFactory? Inno.Build.Toolchains.ProductNativeBuildPlan.deploymentMetadata { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L70) | Gets the product's explicit metadata preparation, or null when no metadata is required. | -| [`public string Inno.Build.Toolchains.ProductNativeBuildPlan.productId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L60) | Gets the explicit product identity. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildPlan.steps { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L65) | Gets the frozen dependency-ordered component closure. | -| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildPlan`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L14) | Executes a frozen product closure with one target toolchain and no implicit backend selection. | - -### `Inno.Build.Toolchains.ProductNativeBuildResult` - @@ -1341,19 +754,6 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.ProductNativeBuildResult.ProductNativeBuildResult(System.Collections.Generic.IEnumerable products, Inno.Build.Toolchains.NativeBindingGenerationResult bindings, string? aggregateDirectory = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L30) | Takes ownership of a complete native and binding closure.
`products`:Owned products in dependency order.
`bindings`:The owned matching binding batch.
`aggregateDirectory`:A complete aggregate install directory, or null for individual products. | -| [`public void Inno.Build.Toolchains.ProductNativeBuildResult.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L94) | Releases products in reverse dependency order after all consumers have finished. Repeated disposal has no effect.
T:System.AggregateException:One or more reading owners failed to release after every owner was attempted. | -| [`public System.Collections.Generic.IEnumerator Inno.Build.Toolchains.ProductNativeBuildResult.GetEnumerator()`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L81) | Enumerates borrowed products in plan order without transferring their ownership.
返回:An enumerator which must finish before this result is disposed.
T:System.ObjectDisposedException:The result has already released its leases. | -| [`public int Inno.Build.Toolchains.ProductNativeBuildResult.Count { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L43) | Gets the number of products in the completed build plan. | -| [`public Inno.Build.Toolchains.NativeBuildProduct Inno.Build.Toolchains.ProductNativeBuildResult.this[int index] { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L63) | Borrows a product whose lease is owned by this result until disposal.
`index`:The zero-based position in the plan's declaration order.
返回:The borrowed product; the caller must finish access before disposing the result.
T:System.ObjectDisposedException:The result no longer owns the products. | -| [`public string? Inno.Build.Toolchains.ProductNativeBuildResult.aggregateDirectory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L48) | Gets the complete aggregate install directory, or null for individually published products. | -| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildResult : System.Collections.Generic.IReadOnlyList, System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L12) | Owns a complete ordered Native product closure and releases its reading leases together. | - -### `Inno.Build.Toolchains.ProductNativeBuildStep` - @@ -1365,35 +765,9 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.ProductNativeBuildStep.ProductNativeBuildStep(string id, Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options, System.Collections.Generic.IReadOnlyList dependencies, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? build, System.Func? createRecipe, System.Func deploymentPath, string? configuration = null, System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? bindDependencies = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L52) | Freezes a component step without embedding any backend list in the shared executor.
`id`:The expected published component identity.
`component`:The unique source and recipe owners.
`options`:Explicit product linkage, ordered component parameters and their input closure.
`dependencies`:Earlier component identities required by this operation.
`build`:The awaited standalone producer, or null when the component is executed by a static aggregate.
`createRecipe`:Declares the standalone producer's source inputs without creating generated outputs, or null for a component whose complete source definition is owned by a static aggregate.
`deploymentPath`:Maps a relative output and target to a deployment path, or null for build-only inputs.
`configuration`:An explicit component configuration, or null to use the product configuration.
`bindDependencies`:Optional backend-owned transformation adding the exact published dependency inputs to its source recipe. It must not generate outputs or execute tools.
T:System.ArgumentException:The identity or dependency set is invalid.
T:System.ArgumentNullException:A required owner, producer or path policy is null. | -| [`public System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? Inno.Build.Toolchains.ProductNativeBuildStep.bindDependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L134) | Gets the backend-owned dependency transformation, or null for an independent source recipe. The supplied products are borrowed only during recipe construction. | -| [`public System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? Inno.Build.Toolchains.ProductNativeBuildStep.build { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L112) | Gets the standalone producer, or null for a component owned by a static aggregate executor. | -| [`public Inno.Build.Toolchains.NativeComponentDescriptor Inno.Build.Toolchains.ProductNativeBuildStep.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L97) | Gets the explicit source and recipe owners. | -| [`public string? Inno.Build.Toolchains.ProductNativeBuildStep.configuration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L128) | Gets the step's explicit configuration, or null to borrow the containing product configuration. | -| [`public System.Func? Inno.Build.Toolchains.ProductNativeBuildStep.createRecipe { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L118) | Gets the producer's source declaration, or null when a static aggregate owns the component recipe. The declaration must not generate bindings, run a compiler or create staging. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildStep.dependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L107) | Gets dependencies that must precede this step. | -| [`public System.Func Inno.Build.Toolchains.ProductNativeBuildStep.deploymentPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L123) | Gets the explicit output layout policy; null results exclude build-only files. | -| [`public string Inno.Build.Toolchains.ProductNativeBuildStep.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L92) | Gets the exact expected component identity. | -| [`public Inno.Build.Toolchains.NativeComponentBuildOptions Inno.Build.Toolchains.ProductNativeBuildStep.options { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L102) | Gets product-owned configuration independently of the selected SDK. | -| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildStep`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L12) | Binds one product component to an owned build operation and explicit deployment layout. | - -### `Inno.Build.Toolchains.ProductNativeDeployment` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ProductNativeDeployment.InstallAsync(System.Collections.Generic.IReadOnlyList products, Inno.Build.Toolchains.ProductNativeBuildPlan plan, string applicationDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L46) | Validates the application's native tree and atomically replaces it when its exact contents differ.
`products`:Validated runtime and optional authoring products returned by the declared product plan. All products must use the same target; each component can appear once.
`cancellationToken`:Cancels ownership acquisition or staging before the atomic installation starts.
返回:Completion after the complete native tree has been validated under exclusive publication ownership. Identical deployments retain their files, including libraries loaded by a running application.
`plan`:The explicit component closure and deployment mapping used to produce these products.
`applicationDirectory`:The managed output directory receiving the complete native tree.
T:System.ArgumentException:The product closure is empty, contains duplicate components or mixes targets.
T:System.IO.IOException:A product cannot be validated, copied or installed; the prior native tree remains available.
T:System.OperationCanceledException:The operation was canceled before installation; the previous native tree is unchanged. | -| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ProductNativeDeployment.StageAsync(System.Collections.Generic.IReadOnlyList products, Inno.Build.Toolchains.ProductNativeBuildPlan plan, string applicationDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L87) | Writes a new native tree into an unpublished candidate exclusively owned by the caller.
`products`:The pinned, validated products to copy into the candidate.
`plan`:The explicit component layout and neutral deployment metadata.
`applicationDirectory`:An existing caller-owned candidate with no native subtree.
`cancellationToken`:Cancels copying or final input verification before the caller publishes.
返回:Completion after copying and verifying payload, without creating transaction metadata in the candidate.
约束:The caller must retain exclusive ownership and discard the entire candidate on failure or cancellation. This operation neither selects a publication nor replaces an existing native tree.
T:System.ArgumentException:The product closure or candidate path is invalid.
T:System.IO.DirectoryNotFoundException:The caller has not created its owned candidate.
T:System.IO.IOException:The native tree already exists, or an input cannot be verified or copied.
T:System.OperationCanceledException:Cancellation prevents completing the unpublished candidate. | -| [`public static class Inno.Build.Toolchains.ProductNativeDeployment`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L15) | Materializes an explicitly built host-native closure into one application's deployment directory. | - -### `Inno.Build.Toolchains.ToolchainEnvironment` - @@ -1410,233 +784,475 @@ BuildCacheOwnerCatalog 由实际发行登记 publisher 根、ID 和生命周期 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.CaptureOutputAsync(System.Diagnostics.ProcessStartInfo start, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L512) | Captures platform tool discovery through the same cancellable process lifecycle as builds.
`start`:The prepared discovery command, including any platform-specific argument quoting.
`cancellationToken`:Cancels discovery and waits for its process tree to exit.
返回:Complete standard output after successful discovery; errors and cancellation propagate. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.CaptureOutputAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L309) | Captures a tool's standard output while forwarding errors and preserving the common process lifecycle.
`fileName`:The executable to launch.
`arguments`:Structured arguments passed without shell interpretation.
`workingDirectory`:The directory used for project and SDK resolution.
`cancellationToken`:Cancels the child process tree and waits for its complete exit.
`environment`:Optional child-only environment variables. Null values remove inherited variables; empty values remain explicitly empty.
返回:The complete standard output after successful exit; failures and cancellation propagate.
T:System.InvalidOperationException:The tool fails to start or exits unsuccessfully.
T:System.OperationCanceledException:The operation was canceled.
T:System.IO.IOException:Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | -| [`public static bool Inno.Build.Toolchains.ToolchainEnvironment.ContainsAny(string value, params string[] needles)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L417) | Determines whether a value contains at least one token without regard to casing.
`value`:The value searched for candidate tokens.
`needles`:The candidate tokens tested against the value.
返回:when at least one token occurs in the value; otherwise . | -| [`public static void Inno.Build.Toolchains.ToolchainEnvironment.DeleteDirectory(string path)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L486) | Deletes one explicitly resolved toolchain output directory when it exists.
`path`:The exact directory selected by a toolchain command.
T:System.ArgumentException:Thrown when is empty or does not resolve to an absolute path. | -| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.FindRepoRoot()`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L345) | Resolves the repository containing the currently executing toolchain assembly.
返回:The absolute path of the repository root.
T:System.InvalidOperationException:Thrown when no repository marker exists above the toolchain assembly directory. | -| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.NormalizeOutputName(string fileName, string config)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L444) | Normalizes a native artifact name and appends its build configuration.
`fileName`:The source artifact file name.
`config`:The normalized configuration appended to the artifact stem.
返回:The deterministic output file name. | -| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.ResolveExecutable(string name)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L164) | Resolves one explicitly selected executable before it becomes a build input.
`name`:An executable path or a command name available on the current process PATH.
返回:The absolute executable path used for process execution and artifact identity.
T:System.ArgumentException:The name is empty.
T:System.IO.FileNotFoundException:The executable is unavailable at the supplied path or on PATH. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L93) | Runs a host-native process with the context's frozen compiler and SDK selection.
`context`:A context resolved by NativeToolchainSelection before artifact fingerprinting.
`fileName`:A selected build command or an absolute executable declared as a component input.
`arguments`:Individual arguments passed without shell interpretation.
`workingDirectory`:The source snapshot or intermediate directory owned by the operation.
`cancellationToken`:Cancels and retires the complete process tree.
返回:Completion after successful exit and complete output delivery.
T:System.InvalidOperationException:Host tools were not resolved, a command is unavailable or a process fails.
T:System.OperationCanceledException:The operation was canceled. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary environment)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L50) | Executes a declared absolute tool with an explicitly resolved SDK environment and records the operation cost.
`context`:The operation owning execution statistics and frozen recipe inputs.
`fileName`:The absolute executable declared by the recipe; no host-native SDK is inferred.
`arguments`:Individual arguments passed without shell interpretation.
`workingDirectory`:The owned source or intermediate directory.
`cancellationToken`:Cancels execution and drains the complete process tree.
`environment`:The frozen environment supplied by the selected SDK resolver.
返回:Completion after successful exit and output delivery.
T:System.ArgumentException:The executable is not an absolute declared tool path.
T:System.InvalidOperationException:The process fails to start or exits unsuccessfully.
T:System.OperationCanceledException:Execution was canceled. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, string arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L134) | Runs a quoted host command with a frozen compiler and SDK environment.
`context`:The context resolved before artifact fingerprinting.
`fileName`:A selected command or an absolute executable declared in the component inputs.
`arguments`:Arguments quoted for the selected executable's command-line parser.
`workingDirectory`:The operation's source snapshot or intermediate directory.
`cancellationToken`:Cancels and retires the complete process tree.
返回:Completion after the command exits successfully and its streams drain.
T:System.InvalidOperationException:Tools are unresolved or execution fails.
T:System.OperationCanceledException:The operation was canceled. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L216) | Runs a child build with structured arguments, cancellation and hidden windows.
`fileName`:The executable to launch.
`arguments`:The individual arguments, passed without shell interpretation.
`workingDirectory`:The repository or staging directory owned by the operation.
`cancellationToken`:Cancels the child process and its complete process tree.
`environment`:Optional process-local toolchain variables; the parent environment is unchanged. Null values remove inherited variables, while empty values remain explicitly empty.
返回:Completion after both output streams have drained and the process succeeds.
T:System.InvalidOperationException:The process fails to start or exits unsuccessfully.
T:System.OperationCanceledException:The operation was canceled.
T:System.IO.IOException:Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment, System.IO.TextWriter standardOutput, System.IO.TextWriter standardError)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L261) | Runs a child build with caller-owned output destinations and the shared process retirement protocol.
`fileName`:The executable selected by the build composition.
`arguments`:Structured arguments passed without shell interpretation.
`workingDirectory`:The project directory controlling SDK and workload resolution.
`cancellationToken`:Cancels the complete process tree and drains both output streams.
`environment`:Optional child-only toolchain variables. Null values remove inherited variables; empty values remain explicitly empty.
`standardOutput`:The caller-owned standard output destination; the runner does not dispose it.
`standardError`:The caller-owned standard error destination; the runner does not dispose it.
返回:Completion after successful exit and complete output delivery; failures propagate.
T:System.InvalidOperationException:The executable cannot start or exits unsuccessfully.
T:System.IO.IOException:An output destination fails; the owned process tree is terminated before failure returns.
T:System.OperationCanceledException:The operation was canceled after retiring its owned process tree. | -| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, string arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L390) | Runs a child build using a complete argument string, without a shell.
`fileName`:The executable resolved by the host operating system.
`arguments`:Arguments quoted for the selected executable's command-line parser.
`workingDirectory`:The directory owned by the operation.
`cancellationToken`:Cancels the complete process tree and waits for output to finish draining.
返回:Completion after successful exit; failures and cancellation propagate.
T:System.InvalidOperationException:The process fails to start or exits unsuccessfully.
T:System.OperationCanceledException:The operation was canceled.
T:System.IO.IOException:Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | -| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.TrimConfigSuffix(string baseName)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L463) | Removes a trailing native debug or release token from an artifact stem.
`baseName`:The artifact stem to normalize.
返回:The normalized artifact stem without a configuration suffix. | -| [`public static void Inno.Build.Toolchains.ToolchainEnvironment.ValidateConfiguration(string configuration)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L330) | Validates the configuration shared by all native component builds.
`configuration`:The debug or release token supplied by the build workflow.
T:System.ArgumentException:The configuration is unsupported. | -| [`public static class Inno.Build.Toolchains.ToolchainEnvironment`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L15) | Provides deterministic host-process and workspace operations shared by native dependency toolchains. | - -### `Inno.Build.Toolchains.ToolchainLayout` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_DEBUG_CONFIGURATION = "debug"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L21) | Identifies the normalized debug configuration token. | -| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_EXTERNAL_DIRECTORY_NAME = "extern"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L16) | Identifies the directory containing checked-out native dependency sources. | -| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_RELEASE_CONFIGURATION = "release"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L26) | Identifies the normalized release configuration token. | -| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_REPOSITORY_MARKER_FILE = "InnoEngine.sln"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L11) | Identifies the repository marker used while resolving a toolchain workspace. | -| [`public static class Inno.Build.Toolchains.ToolchainLayout`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L6) | Defines stable repository-relative names shared by native dependency toolchains. | - -### `Inno.Build.Toolchains.ToolchainWorkingDirectory` - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.Toolchains.ToolchainWorkingDirectory.ToolchainWorkingDirectory(string toolPath, System.IDisposable? ownership = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L26) | Captures an absolute execution path and its optional release protocol.
`toolPath`:The execution directory selected by the host provider.
`ownership`:The alias and lease owner, or null for direct physical execution.
T:System.ArgumentException:The execution path is not absolute. | -| [`public void Inno.Build.Toolchains.ToolchainWorkingDirectory.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L44) | Releases provider resources after all dependent processes have stopped. | -| [`public string Inno.Build.Toolchains.ToolchainWorkingDirectory.toolPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L39) | Gets the execution path, valid until disposal. | -| [`public sealed class Inno.Build.Toolchains.ToolchainWorkingDirectory : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L10) | Retains a provider-owned tool execution path until all dependent processes finish. | - -## 项目依赖 - -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):公开引用边界由实际签名核对。 - -## 缓存管理公开契约 - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Build.Toolchains.BuildCacheCleanupPlan Inno.Build.Toolchains.BuildCacheCleanupPlan.Create(string workspace, System.Collections.Generic.IReadOnlyList owners, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L84) | 明确注册 owner 的物理边界、协调路径和借用的清单/保护根 callbacks。 | Callbacks 在本次清理期间有效;发布及根更新使用同一协调 lease。 | 不得通过目录名猜 owner;拒绝重复、越界或重叠注册。 | - -### 进程加载保护 - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | - - -## 进程与构建所有权 API - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Build.Toolchains.BuildCacheArtifactDescriptor.BuildCacheArtifactDescriptor(string directory, string leasePath, string? targetId, long bytes, System.DateTime lastUseUtc, System.Collections.Generic.IReadOnlyList dependencies, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L48) | 冻结目录、目标、角色、协调路径、字节、UTC 使用时间与依赖;大小只服务预算。 | 描述不接管 producer 或 reader lease。 | 绝对路径、非负大小、UTC 时间和有效角色必须成立。 | -| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCacheArtifactDescriptor.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L104) | Gets the declared lifetime used for retirement and preferred-workspace accounting. | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`public enum Inno.Build.Toolchains.BuildCacheArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L6) | 明确区分不可变产物、可复用 workspace 与进程临时目录。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ImmutableArtifact = 0`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L11) | 完整产物由当前产品/Pack/活动读取保护。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ReusableWorkspace = 1`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L16) | 编译活动时保护;无活动时可按预算退休,即使最近使用。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ProcessWorkingDirectory = 2`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L21) | 进程活动或 Unknown 时保护,退出后可退休。 | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`public Inno.Build.Toolchains.BuildCachePublisher.BuildCachePublisher(string directory, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L34) | 为明确 cache 根选择生命周期,之后通过 Describe 提供登记与保护关系。 | 构造不取得 lease;发布/读取交接由调用方持有 activity 与产物保护。 | 根必须绝对且不越界;不认领未登记目录。 | -| [`public void Inno.Build.Toolchains.BuildCachePublisher.BeginWorkingDirectory(string directory, string leasePath, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L168) | 调用者取得 activity 与 producer 独占 lease 后,先登记 reusable workspace,再允许 payload 写入。 | lease 仍由调用方持有;不永久固定闲置 workspace。 | 已有无登记目录拒绝认领;目标归属按显式参数记录。 | -| [`public Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.WithDependency(Inno.Build.Toolchains.NativeBuildInput input, string fingerprint)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L190) | 把实际依赖产物和指纹冻结进后端 recipe,保留选定工具;依赖产品仅在 recipe 构造期间借用。 | 不转移 NativeBuildProduct 的读取 leases。 | 没有声明转换的依赖步骤走正常冷路径,不能猜测链接参数。 | -| [`public Inno.Build.Toolchains.ProductNativeBuildStep.ProductNativeBuildStep(string id, Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options, System.Collections.Generic.IReadOnlyList dependencies, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? build, System.Func? createRecipe, System.Func deploymentPath, string? configuration = null, System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? bindDependencies = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L52) | Freezes a component step without embedding any backend list in the shared executor. | 不可变元数据;不持有进程或文件 lease。 | 值查询没有写入副作用。 | -| [`public System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? Inno.Build.Toolchains.ProductNativeBuildStep.bindDependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L134) | 把实际依赖产物和指纹冻结进后端 recipe,保留选定工具;依赖产品仅在 recipe 构造期间借用。 | 不转移 NativeBuildProduct 的读取 leases。 | 没有声明转换的依赖步骤走正常冷路径,不能猜测链接参数。 | - - - - - - - - - - - - - -有效 MSBuild 图的节点按项目路径与完整 global properties 排序,避免同路径多配置节点的无序枚举造成错误失效。参数、include 与编译项顺序继续参与身份;验证失败会报告文件或声明变化。内部 capture 只服务诊断,不构成新的公开缓存权威。 - -工具 publication 在固定输入前,使用同一 SDK 还原入口项目及 InnoBuildInputProject 声明的辅助输入项目。辅助项目来自有效图,清理的 global properties 保持一致;Capture/Verify 本身仍只读取,不通过缺 assets 的 fallback 掩盖错误。 +| [`public static class Inno.Build.Toolchains.BuildArtifactManifest`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L15) | Validates complete build outputs against their input identity and recorded file hashes. | — | — | +| [`public static void Inno.Build.Toolchains.BuildArtifactManifest.Write(string directory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, Inno.Build.Toolchains.NativeBuildContext? context = null)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L40) | Records a completed staging tree before its owner publishes the directory.
`directory`: The complete staging root owned exclusively by the caller.
`fingerprint`: The immutable identity of every input used to produce these outputs.
`outputDirectories`: Relative output subdirectories included in this artifact; each must stay within the root.
`context`: The optional operation collecting independent output-read statistics. | — | `System.ArgumentException`: An identity or output path is invalid.
`System.IO.IOException`: An output or manifest cannot be read or written. | +| [`public static bool Inno.Build.Toolchains.BuildArtifactManifest.IsComplete(string directory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, Inno.Build.Toolchains.NativeBuildContext? context = null)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactManifest.cs#L88) | Checks the identity, exact output file set and bytes before reusing a cached artifact.
`directory`: The published artifact directory.
`fingerprint`: The requested input identity.
`outputDirectories`: The same relative output subdirectories supplied when recording the artifact.
`context`: The optional operation collecting actual output integrity reads. | True only for complete, unmodified outputs; false for a missing, malformed or mismatched manifest. | `System.ArgumentException`: An output path escapes the artifact root.
`System.IO.IOException`: An existing output cannot be read. | +| [`public sealed record Inno.Build.Toolchains.BuildArtifactOptions : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Defines the deterministic filter and naming policy used to collect one native product.
`buildDirName`: The dependency-relative directory searched for native build outputs.
`libraryTokens`: File-name tokens that identify artifacts belonging to the product.
`extensions`: The accepted native artifact extensions.
`requiredPathTokens`: Optional normalized path tokens used to reject unrelated intermediate files.
`normalizeOutputName`: The deterministic output naming policy. | — | — | +| [`public Inno.Build.Toolchains.BuildArtifactOptions.BuildArtifactOptions(string buildDirName, System.Collections.Generic.IReadOnlyCollection libraryTokens, System.Collections.Generic.IReadOnlyCollection extensions, System.Collections.Generic.IReadOnlyCollection? requiredPathTokens, System.Func normalizeOutputName)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Defines the deterministic filter and naming policy used to collect one native product.
`buildDirName`: The dependency-relative directory searched for native build outputs.
`libraryTokens`: File-name tokens that identify artifacts belonging to the product.
`extensions`: The accepted native artifact extensions.
`requiredPathTokens`: Optional normalized path tokens used to reject unrelated intermediate files.
`normalizeOutputName`: The deterministic output naming policy. | — | — | +| [`public string Inno.Build.Toolchains.BuildArtifactOptions.buildDirName { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L27) | The dependency-relative directory searched for native build outputs. | — | — | +| [`public System.Collections.Generic.IReadOnlyCollection Inno.Build.Toolchains.BuildArtifactOptions.libraryTokens { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L28) | File-name tokens that identify artifacts belonging to the product. | — | — | +| [`public System.Collections.Generic.IReadOnlyCollection Inno.Build.Toolchains.BuildArtifactOptions.extensions { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L29) | The accepted native artifact extensions. | — | — | +| [`public System.Collections.Generic.IReadOnlyCollection? Inno.Build.Toolchains.BuildArtifactOptions.requiredPathTokens { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L30) | Optional normalized path tokens used to reject unrelated intermediate files. | — | — | +| [`public System.Func Inno.Build.Toolchains.BuildArtifactOptions.normalizeOutputName { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L31) | The deterministic output naming policy. | — | — | +| [`public override string Inno.Build.Toolchains.BuildArtifactOptions.ToString()`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.BuildArtifactOptions.operator !=(Inno.Build.Toolchains.BuildArtifactOptions? left, Inno.Build.Toolchains.BuildArtifactOptions? right)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.BuildArtifactOptions.operator ==(Inno.Build.Toolchains.BuildArtifactOptions? left, Inno.Build.Toolchains.BuildArtifactOptions? right)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.BuildArtifactOptions.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.BuildArtifactOptions.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.BuildArtifactOptions.Equals(Inno.Build.Toolchains.BuildArtifactOptions? other)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Build.Toolchains.BuildArtifactOptions.Deconstruct(out string buildDirName, out System.Collections.Generic.IReadOnlyCollection libraryTokens, out System.Collections.Generic.IReadOnlyCollection extensions, out System.Collections.Generic.IReadOnlyCollection? requiredPathTokens, out System.Func normalizeOutputName)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L26) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.BuildArtifactCopier`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L37) | Copies filtered native outputs into the engine's rebuildable dependency store. | — | — | +| [`public static void Inno.Build.Toolchains.BuildArtifactCopier.CopyArtifacts(string buildRoot, string outputDir, string config, Inno.Build.Toolchains.BuildArtifactOptions options)`](../../build/toolchains/Inno.Build.Toolchains/BuildArtifactOptions.cs#L56) | Copies all artifacts accepted by one product policy into its output directory.
`buildRoot`: The native dependency root containing the configured build directory.
`outputDir`: The destination directory receiving normalized artifacts.
`config`: The normalized native build configuration.
`options`: The immutable filtering and naming policy. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildArtifactReadLease : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L13) | Pins a verified immutable build artifact against cooperating repair and retirement operations. | — | — | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildArtifactReadLease.AcquireAsync(string directory, string ownerDirectory, string fingerprint, System.Collections.Generic.IReadOnlyList outputDirectories, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Build.Toolchains.NativeBuildContext? statistics = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L55) | Acquires reading ownership and verifies the complete artifact before exposing it to a consumer.
`ownerDirectory`: The exact publisher root retaining the persistent owner.lock gate.
`directory`: The absolute artifact directory owned by its publisher; its persistent lease is stored alongside it.
`fingerprint`: The exact input identity required by the consumer.
`outputDirectories`: The complete relative output roots declared by the publisher.
`cancellationToken`: Cancels ownership acquisition without releasing another reader's protection.
`statistics`: Optional borrowed operation receiving output-integrity read statistics; its input cache is not used. | Reading ownership which the caller must retain until all artifact access completes. | `System.ArgumentException`: The directory is not absolute or a required identity is blank.
`System.IO.InvalidDataException`: The complete artifact does not match the requested identity and output inventory.
`System.TimeoutException`: Repair or retirement retains exclusive ownership for more than thirty seconds.
`System.OperationCanceledException`: Waiting was canceled before ownership could be returned. | +| [`public void Inno.Build.Toolchains.BuildArtifactReadLease.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactReadLease.cs#L68) | Ends artifact access and allows repair or retirement; repeated disposal has no effect. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildArtifactWriteLease : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L12) | Coordinates artifact replacement or retirement with every cooperating reader and writer. | — | — | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildArtifactWriteLease.AcquireAsync(string directory, string ownerDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L42) | Waits for all readers before allowing mutation of one immutable artifact directory.
`ownerDirectory`: The exact publisher root retaining the persistent owner.lock gate.
`directory`: The absolute artifact location controlled by the publisher.
`cancellationToken`: Cancels this waiter without disturbing existing readers. | Exclusive protection; retain it through replacement and release it before acquiring a reading lease. | `System.ArgumentException`: The directory is empty or relative.
`System.TimeoutException`: Other owners retain the artifact for more than thirty seconds.
`System.OperationCanceledException`: Waiting was canceled. | +| [`public void Inno.Build.Toolchains.BuildArtifactWriteLease.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildArtifactWriteLease.cs#L58) | Ends exclusive access without removing the persistent coordination file. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildCacheArtifactDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L12) | Freezes one publisher-owned cache entry and the generations it requires. | — | — | +| [`public Inno.Build.Toolchains.BuildCacheArtifactDescriptor.BuildCacheArtifactDescriptor(string directory, string leasePath, string? targetId, long bytes, System.DateTime lastUseUtc, System.Collections.Generic.IReadOnlyList dependencies, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L48) | Freezes ownership metadata supplied by the artifact's actual publisher.
`directory`: The absolute published artifact or producer-owned workspace directory.
`leasePath`: The absolute persistent file used by this entry's actual producer and readers for Core.IO coordination.
`bytes`: The publisher-recorded sum of owned file lengths, captured under writing ownership for cache budgeting. It does not measure allocated disk blocks or count shared hardlinks as recovered volume space.
`targetId`: The explicit product target, or null for a shared host artifact that target-scoped cleanup must preserve.
`lastUseUtc`: The publisher's last recorded use, used only to order retirement.
`dependencies`: Absolute artifact directories required by this generation.
`role`: The producer-declared lifetime; workspace preference alone does not pin an immutable product. | — | `System.ArgumentException`: A path is relative or the timestamp is not UTC.
`System.ArgumentOutOfRangeException`: The measured size is negative.
`System.ArgumentNullException`: The dependency collection is absent. | +| [`public string Inno.Build.Toolchains.BuildCacheArtifactDescriptor.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L84) | Gets the absolute cache entry controlled by the declaring producer. | — | — | +| [`public string Inno.Build.Toolchains.BuildCacheArtifactDescriptor.leasePath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L89) | Gets the exact existing producer/reader coordination path; retirement does not infer another lock protocol. | — | — | +| [`public string? Inno.Build.Toolchains.BuildCacheArtifactDescriptor.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L94) | Gets the producer-declared target, or null when this artifact is shared by host operations. | — | — | +| [`public long Inno.Build.Toolchains.BuildCacheArtifactDescriptor.bytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L99) | Gets the logical file size used for budgeting, without weakening content validation. | — | — | +| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCacheArtifactDescriptor.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L104) | Gets the declared lifetime used for retirement and preferred-workspace accounting. | — | — | +| [`public System.DateTime Inno.Build.Toolchains.BuildCacheArtifactDescriptor.lastUseUtc { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L109) | Gets the UTC use timestamp, which is never used to validate cached content. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheArtifactDescriptor.dependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactDescriptor.cs#L114) | Gets the frozen dependency directories protected whenever this artifact is a live root. | — | — | +| [`public enum Inno.Build.Toolchains.BuildCacheArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L6) | Declares the producer's file lifetime independently of target identity and directory naming. | — | — | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ImmutableArtifact = 0`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L11) | A complete immutable generation remains protected by selected products and active readers. | — | — | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ReusableWorkspace = 1`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L16) | A mutable compiler workspace may retire when no producer or consumer is active. | — | — | +| [`Inno.Build.Toolchains.BuildCacheArtifactRole.ProcessWorkingDirectory = 2`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheArtifactRole.cs#L21) | An operation directory remains protected until its exact process owners have exited. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildCacheCleanupPlan`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L15) | Freezes a reviewable retirement proposal for explicitly registered cache owners in one checkout. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupPlan.candidates { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L43) | Gets the original eligible candidates shared by preview and execution. | — | — | +| [`public long Inno.Build.Toolchains.BuildCacheCleanupPlan.totalBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L48) | Gets the measured total of explicitly inventoried artifact files. | — | — | +| [`public long Inno.Build.Toolchains.BuildCacheCleanupPlan.protectedBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L53) | Gets the size protected by current roots and their transitive dependencies when planning. | — | — | +| [`public static Inno.Build.Toolchains.BuildCacheCleanupPlan Inno.Build.Toolchains.BuildCacheCleanupPlan.Create(string workspace, System.Collections.Generic.IReadOnlyList owners, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll = false, string? targetId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L95) | Plans retirement by budget pressure or an explicit request to retire every unreferenced artifact.
`workspace`: The absolute checkout boundary; no owner may manage it or its parent.
`owners`: Explicit registrations; callbacks remain borrowed until execution completes.
`policy`: The immutable size, free-space and duration budgets.
`retireAll`: Whether to propose all unreferenced generations regardless of size pressure.
`cancellationToken`: Cancels planning before any directory is removed.
`targetId`: An exact producer-declared target filter, or null to manage all registered scopes. Outside-scope generations and their dependencies remain protected. | A fixed proposal; execution rechecks ownership, roots and active readers before deletion. | `System.ArgumentException`: Owners overlap, identities repeat, or dependency metadata forms a cycle.
`System.IO.IOException`: An owner, artifact or lease escapes its boundary or traverses a link.
`System.IO.InvalidDataException`: An inventory has ambiguous ownership or undeclared dependencies.
`System.OperationCanceledException`: Planning was canceled.
`System.TimeoutException`: Inventory exceeded the complete operation budget; no artifact was removed. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildCacheCleanupPlan.ExecuteAsync(bool dryRun = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupPlan.cs#L184) | Executes the original proposal under fresh owner and artifact leases without waiting for active readers.
`dryRun`: Whether to report the proposal without acquiring mutation leases or deleting files.
`cancellationToken`: Cancels remaining work; successful earlier retirements are not rolled back. | Actual retirement, activity skips and IO failures; protected generations are never forced out. | `System.IO.InvalidDataException`: Fresh ownership metadata violates the registered boundary.
`System.OperationCanceledException`: The caller canceled; no later candidate is processed. | +| [`public sealed class Inno.Build.Toolchains.BuildCacheCleanupResult`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L11) | Reports completed retirement independently from protected, active and failed artifacts. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupResult.retired { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L30) | Gets directories actually retired; a preview always returns an empty collection. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheCleanupResult.skipped { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L35) | Gets candidates preserved because ownership, protection or dependencies changed. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.BuildCacheCleanupResult.failures { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L40) | Gets failed paths and their observable IO or permission diagnostics. | — | — | +| [`public long Inno.Build.Toolchains.BuildCacheCleanupResult.retiredBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L46) | Gets the declared file bytes of successfully retired artifacts, excluding persistent leases. This is a logical size; hardlinks, compression and other writers can affect actual volume recovery. | — | — | +| [`public bool Inno.Build.Toolchains.BuildCacheCleanupResult.timeLimitReached { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheCleanupResult.cs#L51) | Gets whether the bounded attempt ended before processing all eligible candidates. | — | — | +| [`public static class Inno.Build.Toolchains.BuildCacheOwnerCatalog`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L16) | Persists explicit publisher registrations for maintenance without loading the production distribution. | — | — | +| [`public static void Inno.Build.Toolchains.BuildCacheOwnerCatalog.Register(string engineRoot, string directory, string id, Inno.Build.Toolchains.BuildCacheArtifactRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L45) | Registers a publisher root; this never adopts unrecorded artifacts inside that root.
`engineRoot`: The absolute checkout owning the registration.
`directory`: The exact publisher root beneath the checkout.
`id`: The distribution's unique owner identity.
`role`: The publisher's actual artifact lifetime.
`cancellationToken`: Cancels before registration commits. | — | `System.IO.IOException`: A registration crosses a link or ownership boundary.
`System.IO.InvalidDataException`: An existing root has a different registration.
`System.OperationCanceledException`: Cancellation precedes commit. | +| [`public static System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildCacheOwnerCatalog.ReadOwners(string engineRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerCatalog.cs#L90) | Reads explicitly registered roots using each publisher's existing inventory and protection rules.
`engineRoot`: The absolute checkout containing registrations.
`cancellationToken`: Cancels reading before a cleanup plan exists. | Frozen owner descriptors; an unprepared checkout returns an empty list. | `System.IO.InvalidDataException`: A registration is malformed or has an ambiguous identity.
`System.IO.IOException`: A registered path crosses a link or leaves the checkout. | +| [`public sealed class Inno.Build.Toolchains.BuildCacheOwnerDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L11) | Registers a publisher's exact cache boundary and its current artifact ownership inventory. | — | — | +| [`public Inno.Build.Toolchains.BuildCacheOwnerDescriptor.BuildCacheOwnerDescriptor(string id, string directory, string coordinationPath, System.Func> readArtifacts, System.Func> readProtectedArtifacts, System.Action? retireMetadata = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L47) | Borrows publisher callbacks without discovering artifacts from directory names or file extensions.
`id`: The unique registration identity within this cleanup operation.
`directory`: The absolute cache root exclusively managed by this publisher.
`coordinationPath`: The persistent owner lease also acquired by publication and protected-root updates. It must remain within the cache root and is never deleted by cleanup.
`readArtifacts`: Returns only generations proven to belong to this owner, with their dependency metadata. The callback must observe cancellation and must not create, repair or publish artifacts.
`readProtectedArtifacts`: Returns current product and Pack roots; retirement follows their complete dependency closure. Root updates must hold coordinationPath and retain each referenced artifact's reading lease.
`retireMetadata`: Optional owner-specific metadata retirement, called only under exclusive owner coordination. | — | `System.ArgumentException`: An identity is blank or a path is relative.
`System.ArgumentNullException`: An inventory callback is absent. | +| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L73) | Gets the unique publisher identity registered for this cleanup operation. | — | — | +| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L78) | Gets the exact registered cache root; its parent directories are never managed implicitly. | — | — | +| [`public string Inno.Build.Toolchains.BuildCacheOwnerDescriptor.coordinationPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCacheOwnerDescriptor.cs#L83) | Gets the persistent lease shared with publication and protected-root changes. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildCachePolicy`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L8) | Defines bounded retirement pressure without allowing active or current generations to be removed. | — | — | +| [`public Inno.Build.Toolchains.BuildCachePolicy.BuildCachePolicy(long maximumBytes = 68719476736, long minimumFreeBytes = 21474836480, System.TimeSpan? timeBudget = null)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L25) | Creates an immutable cache budget and automatic retirement time limit.
`maximumBytes`: The managed cache budget; defaults to 64 GiB.
`minimumFreeBytes`: The free-space threshold on the checkout volume; defaults to 20 GiB.
`timeBudget`: The retirement duration, or null for the five-second default. | — | `System.ArgumentOutOfRangeException`: A budget is negative or the time limit is not positive. | +| [`public long Inno.Build.Toolchains.BuildCachePolicy.maximumBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L45) | Gets the maximum size of explicitly registered build caches. | — | — | +| [`public long Inno.Build.Toolchains.BuildCachePolicy.minimumFreeBytes { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L50) | Gets the free-space threshold below which additional retirement is requested. | — | — | +| [`public System.TimeSpan Inno.Build.Toolchains.BuildCachePolicy.timeBudget { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePolicy.cs#L56) | Gets the duration after which an attempt starts no further retirement work. An already executing filesystem operation finishes before ownership is released. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildCachePublisher`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L17) | Records exact publisher-owned generations and coordinates them with bounded cache retirement. | — | — | +| [`public Inno.Build.Toolchains.BuildCachePublisher.BuildCachePublisher(string directory, Inno.Build.Toolchains.BuildCacheArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L34) | Creates a publisher for one absolute, non-overlapping cache boundary.
`directory`: The cache root exclusively controlled by this publisher.
`role`: The lifetime declared for this publisher's completed entries. | — | `System.ArgumentException`: The directory is blank or relative. | +| [`public Inno.Build.Toolchains.BuildCacheArtifactRole Inno.Build.Toolchains.BuildCachePublisher.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L50) | Gets the declared lifecycle used by this publisher's inventory and maintenance registration. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.BuildCachePublisher.AcquireUseAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L64) | Pins publisher activity until publication, reading handoff and current-root updates finish.
`cancellationToken`: Cancels this wait without disturbing other owners. | Caller-owned shared activity; dispose after the final artifact reading lease is acquired. | `System.OperationCanceledException`: Acquisition was canceled. | +| [`public void Inno.Build.Toolchains.BuildCachePublisher.Record(string directory, string leasePath, System.Collections.Generic.IReadOnlyList dependencies, string? currentKey, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L105) | Records a completed generation and optionally selects it as a protected current root. The caller must hold this publisher's activity lease and the artifact's producer or reading lease.
`directory`: An existing artifact within this publisher's exact boundary.
`leasePath`: The persistent lease used by its producer and readers.
`dependencies`: Exact owned entries required while this generation is protected.
`currentKey`: A product/target/configuration root identity, or null for an unselected generation.
`cancellationToken`: Cancels before metadata selection.
`targetId`: The producer's product target, or null for a shared host generation. | — | `System.IO.IOException`: An owned path escapes the boundary, crosses a directory link, or contains an invalid file link.
`System.IO.DirectoryNotFoundException`: The completed generation is absent. | +| [`public Inno.Build.Toolchains.BuildCacheOwnerDescriptor Inno.Build.Toolchains.BuildCachePublisher.Describe(string id)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L143) | Registers only explicitly recorded ownership, without discovering artifacts by extension or directory age.
`id`: The unique publisher registration identity. | Borrowed inventory callbacks for this publisher's lifetime. | — | +| [`public void Inno.Build.Toolchains.BuildCachePublisher.BeginWorkingDirectory(string directory, string leasePath, string? targetId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L168) | Records a reusable workspace before its first payload write while the caller holds activity and producer leases. Existing unregistered directories are rejected, rather than adopted by cleanup authority.
`directory`: The new or previously registered workspace within this publisher root.
`leasePath`: The caller's persistent exclusive producer lease outside the workspace.
`targetId`: The explicit product target, or null for shared host work.
`cancellationToken`: Cancels registration before payload writes. | — | `System.InvalidOperationException`: The publisher does not manage reusable workspaces or an existing directory has no current registration.
`System.IO.IOException`: The workspace escapes its boundary or ownership cannot be written. | +| [`public void Inno.Build.Toolchains.BuildCachePublisher.RecordProcessUse(string directory, string leasePath, System.Collections.Generic.IReadOnlyList dependencies, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L212) | Registers a process-owned loader directory and its dependencies before returning it to a loader. Retirement preserves the directory until every recorded process has exited. Repeated registrations retain the complete dependency closure of that process-owned operation.
`directory`: The completed directory within this publisher's boundary.
`leasePath`: The persistent producer and retirement lease outside the directory.
`dependencies`: Owned generations and workspaces required throughout this process-owned operation.
`cancellationToken`: Cancels ownership acquisition before the process claim is recorded. | — | `System.IO.IOException`: The directory escapes ownership or its registration cannot be written.
`System.OperationCanceledException`: Ownership acquisition was canceled. | +| [`public void Inno.Build.Toolchains.BuildCachePublisher.RecordIndex(string path, System.Collections.Generic.IReadOnlyList dependencies)`](../../build/toolchains/Inno.Build.Toolchains/Cache/BuildCachePublisher.cs#L257) | Registers an exact cache index and the publication directories required to keep it useful.
`path`: The producer-owned index file within this publisher root.
`dependencies`: Exact publication roots; missing dependencies make the index retireable. | — | — | +| [`public sealed class Inno.Build.Toolchains.BuildHostServices`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L10) | Freezes host execution capabilities independently of a product target SDK. | — | — | +| [`public Inno.Build.Toolchains.BuildHostServices.BuildHostServices(Inno.Build.Toolchains.IToolchainWorkspaceProvider workspaces, System.StringComparer? environmentComparer = null, System.Collections.Generic.IEnumerable? inputPaths = null, string executableExtension = "")`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L27) | Captures execution policy and its implementation inputs.
`workspaces`: The host's scoped execution path provider.
`environmentComparer`: The host's environment variable name semantics.
`inputPaths`: Checkout-relative implementation files included in native recipe identity.
`executableExtension`: The host executable suffix, or an empty string for suffix-free executable names. | — | — | +| [`public Inno.Build.Toolchains.IToolchainWorkspaceProvider Inno.Build.Toolchains.BuildHostServices.workspaces { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L43) | Gets the host's scoped execution path provider. | — | — | +| [`public System.StringComparer Inno.Build.Toolchains.BuildHostServices.environmentComparer { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L47) | Gets environment variable name semantics. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.BuildHostServices.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L51) | Gets checkout-relative execution implementation inputs. | — | — | +| [`public string Inno.Build.Toolchains.BuildHostServices.executableExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/BuildHostServices.cs#L55) | Gets the host-owned executable suffix. | — | — | +| [`public interface Inno.Build.Toolchains.IBuildHostProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/IBuildHostProvider.cs#L6) | Discovers one explicitly registered host implementation. | — | — | +| [`Inno.Build.Toolchains.BuildHostDescriptor? Inno.Build.Toolchains.IBuildHostProvider.TryCapture()`](../../build/toolchains/Inno.Build.Toolchains/Host/IBuildHostProvider.cs#L14) | Captures the current execution machine when this provider supports it. | The immutable host, or null when the current machine is outside this provider's capabilities. | — | +| [`public interface Inno.Build.Toolchains.IToolchainWorkspaceProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L10) | Owns the relationship between physical build storage and host tool execution paths. | — | — | +| [`string Inno.Build.Toolchains.IToolchainWorkspaceProvider.identity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L15) | Gets the stable execution namespace included in compiler workspace identity. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.IToolchainWorkspaceProvider.OpenAsync(string physicalPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Host/IToolchainWorkspaceProvider.cs#L29) | Acquires an execution path for an absolute build-owned directory.
`physicalPath`: The retained physical directory.
`cancellationToken`: Cancellation while waiting for exclusive alias ownership. | A scoped path whose processes must finish before disposal. | — | +| [`public sealed class Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider : Inno.Build.Toolchains.IToolchainWorkspaceProvider`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L11) | Executes tools directly in their physical owner directory. | — | — | +| [`public string Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.identity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L14) | Gets the stable execution namespace included in compiler workspace identity. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.OpenAsync(string physicalPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L17) | Acquires an execution path for an absolute build-owned directory.
`physicalPath`: The retained physical directory.
`cancellationToken`: Cancellation while waiting for exclusive alias ownership. | A scoped path whose processes must finish before disposal. | — | +| [`public Inno.Build.Toolchains.PhysicalToolchainWorkspaceProvider.PhysicalToolchainWorkspaceProvider()`](../../build/toolchains/Inno.Build.Toolchains/Host/PhysicalToolchainWorkspaceProvider.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.BuildMaintenanceApplication`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L14) | Runs publisher-owned cache maintenance without backend, generator or Editor dependencies. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.BuildMaintenanceApplication.Run(string[] arguments, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L28) | Executes the cache-clean command using the checkout's explicit publisher catalog.
`arguments`: The clean command and its validated named options.
`cancellationToken`: Cancels planning or retirement without deleting protected outputs. | Zero on success, two on cancellation, or one with a diagnostic on failure. | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.BuildMaintenanceApplication.CleanAsync(string engineRoot, Inno.Build.Toolchains.BuildCachePolicy policy, bool retireAll, bool dryRun, string? targetId, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Maintenance/BuildMaintenanceApplication.cs#L117) | Uses the same owner inventory, dependency protection and retirement plan for automatic and explicit cleanup.
`engineRoot`: The absolute checkout containing registered publishers.
`policy`: The bounded size and duration policy.
`retireAll`: Whether to select every unreferenced generation instead of pressure candidates.
`dryRun`: Whether to report the plan without retiring it.
`targetId`: An optional exact producer target filter; shared dependencies remain protected.
`cancellationToken`: Cancels inventory and retirement. | A completed, bounded cleanup operation with protected owners retained. | `System.InvalidOperationException`: Explicit retirement encountered failures.
`System.TimeoutException`: Owner discovery or planning exhausts the operation budget before retirement can begin.
`System.IO.IOException`: Ownership metadata or protected input cannot be validated.
`System.OperationCanceledException`: The caller canceled before completion. | +| [`public sealed class Inno.Build.Toolchains.DotNetSdkDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L6) | Freezes the host, SDK identity and managed CLI entry selected by one managed entry project. | — | — | +| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.hostPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L21) | Gets the executable used for project-scoped SDK and workload resolution. | — | — | +| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.sdkIdentity { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L26) | Gets the exact SDK identity selected by the project's global.json resolution rules. | — | — | +| [`public string Inno.Build.Toolchains.DotNetSdkDescriptor.cliPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkDescriptor.cs#L32) | Gets the selected SDK's managed CLI entry assembly, invoked with the recorded host without resolving another SDK from a temporary execution directory. | — | — | +| [`public static class Inno.Build.Toolchains.DotNetSdkEnvironment`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkEnvironment.cs#L11) | Isolates a managed tool invocation from the invoking IDE's SDK and MSBuild process identity. | — | — | +| [`public static System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.DotNetSdkEnvironment.Create(string hostPath, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkEnvironment.cs#L40) | Freezes child-only overrides so the selected executable resolves the project's own SDK. Null SDK overrides remove inherited variables rather than borrowing a parent's loaded MSBuild.
`hostPath`: The explicitly selected managed executable, resolved to an absolute path before invocation.
`environment`: Additional selected toolchain variables to copy, or null when no native SDK environment is required. | A read-only child environment that preserves selected toolchain settings and never changes the parent. | `System.ArgumentException`: The managed executable is blank.
`System.IO.FileNotFoundException`: The selected managed executable cannot be resolved. | +| [`public static class Inno.Build.Toolchains.DotNetSdkResolver`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs#L12) | Resolves the managed SDK using the prepared entry project's directory and normal global.json rules. | — | — | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.DotNetSdkResolver.ResolveAsync(string hostPath, string projectPath, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs#L35) | Asks the selected host to resolve its SDK from the project location without guessing installed versions.
`hostPath`: The .NET executable selected by the build composition root.
`projectPath`: The prepared entry project whose directory controls SDK selection.
`cancellationToken`: Cancels SDK discovery and drains its child process. | The exact project-selected SDK, managed entry assembly and host; missing SDKs fail explicitly. | `System.IO.FileNotFoundException`: The selected host, prepared entry project or selected SDK entry assembly does not exist.
`System.InvalidOperationException`: The selected host cannot resolve a valid SDK identity and absolute base path. | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.DotNetSdkResolver.ResolveDirectoryAsync(string hostPath, string ownerDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/DotNetSdkResolver.cs#L75) | Selects the SDK from an authoring owner's directory before any temporary evaluation project is created.
`hostPath`: The managed host explicitly selected by the composition root.
`ownerDirectory`: The existing source owner whose ancestor global.json rules govern the operation.
`cancellationToken`: Cancels discovery and drains the SDK process. | The selected SDK and fixed managed CLI path, without probing reference pack directories. | `System.IO.DirectoryNotFoundException`: The source owner directory does not exist.
`System.IO.FileNotFoundException`: The host or selected SDK CLI is missing.
`System.InvalidOperationException`: The host cannot resolve the source owner's requested SDK.
`System.OperationCanceledException`: The caller cancels SDK discovery. | +| [`public sealed class Inno.Build.Toolchains.ManagedBuildExecution : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L17) | Owns a reusable, exclusively leased source workspace containing verified copies of an operation's inputs. | — | — | +| [`public string Inno.Build.Toolchains.ManagedBuildExecution.engineRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L51) | Gets the fixed source root used by compilation and source extraction until disposal. SDKs and restored packages retain their explicitly selected external locations. | — | — | +| [`public string Inno.Build.Toolchains.ManagedBuildExecution.project { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L56) | Gets the entry project within this execution's copied source root. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildExecution.VerifyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L73) | Checks that owned source copies still match the bytes accepted before compilation.
`cancellationToken`: Cancels complete source verification before the consumer publishes its output. | Completion only when every owned input remains unchanged. | `System.InvalidOperationException`: A tool changed a fixed input during execution.
`System.ObjectDisposedException`: This execution no longer owns the workspace. | +| [`public void Inno.Build.Toolchains.ManagedBuildExecution.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildExecution.cs#L93) | Releases exclusive workspace ownership after every compiler and source consumer has finished. Repeated disposal has no effect; reusable owned files remain available for the next operation. | — | `System.IO.IOException`: Completed workspace ownership metadata cannot be recorded; the execution lease is still released. | +| [`public sealed class Inno.Build.Toolchains.ManagedBuildInputSnapshot`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L15) | Freezes the selected SDK's evaluated managed source graph without compiling a product. | — | — | +| [`public string Inno.Build.Toolchains.ManagedBuildInputSnapshot.fingerprint { get; }`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L41) | Gets the content identity of the evaluated graph, compiler inputs and selected SDK. | — | — | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.CreateExecutionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L59) | Materializes the operation's declared repository inputs into an exclusively owned source workspace. Compilers and source extraction must use the returned project and root, never the mutable checkout.
`cancellationToken`: Cancels ownership acquisition or verified copying before execution ownership is returned. | Caller-owned fixed execution inputs. Dispose only after all compiler and source consumers finish. | `System.InvalidOperationException`: An input differs from its planned bytes while being copied.
`System.ArgumentException`: The project does not belong to the operation's source root. | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.CaptureAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.DotNetSdkDescriptor sdk, string project, System.Collections.Generic.IReadOnlyDictionary properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L102) | Evaluates an explicit managed graph using its frozen SDK and hashes its declared compilation inputs.
`context`: The borrowed operation that owns phase-local content reads and tool statistics.
`sdk`: The SDK already selected for this project; evaluation must use its exact CLI entry.
`project`: The existing entry project whose references define the managed source closure.
`properties`: The immutable compiler and product properties also passed to the actual managed build.
`cancellationToken`: Cancels SDK evaluation, drains its process and removes this operation's request directory. | A snapshot that can be freshly verified after waiting or executing; no Player compilation is performed. | `System.ArgumentNullException`: A required operation, SDK or property collection is null.
`System.IO.FileNotFoundException`: A required source, compiler, restored package input or SDK declaration is absent.
`System.IO.IOException`: The source entry escapes the physical checkout or a filesystem alias cannot be resolved.
`System.InvalidOperationException`: The selected SDK cannot evaluate the declared graph.
`System.OperationCanceledException`: Evaluation or hashing was canceled. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ManagedBuildInputSnapshot.VerifyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Managed/ManagedBuildInputSnapshot.cs#L140) | Re-evaluates the graph and re-reads its current bytes in a fresh verification phase.
`cancellationToken`: Cancels evaluation and verification without changing any published output. | Completion when the complete current input identity still matches this snapshot. | `System.InvalidOperationException`: The graph, SDK, source, resource, analyzer or reference inputs changed.
`System.OperationCanceledException`: Verification was canceled. | +| [`public sealed class Inno.Build.Toolchains.NativeBuildContext`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L13) | Identifies the checkout, configuration and frozen initial inputs owned by one native build operation. Create a new context for each operation; changes during its lifetime fail stability verification. | — | — | +| [`public Inno.Build.Toolchains.NativeBuildContext.NativeBuildContext(string engineRoot, string configuration)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L44) | Creates a build context without consulting the tool assembly's checkout.
`engineRoot`: The checkout containing InnoEngine.sln and the native source tree; aliases resolve to one physical owner.
`configuration`: The normalized debug or release configuration. | — | `System.ArgumentException`: The root is empty or the configuration is unsupported.
`System.IO.DirectoryNotFoundException`: The selected checkout has no repository marker.
`System.IO.IOException`: A checkout alias cannot be resolved to a physical owner. | +| [`public string Inno.Build.Toolchains.NativeBuildContext.engineRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L150) | Gets the frozen physical checkout used for every source, intermediate and output path. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildContext.configuration { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L155) | Gets the normalized debug or release configuration prepared by this operation. | — | — | +| [`public Inno.Build.Toolchains.NativeToolchainSelection? Inno.Build.Toolchains.NativeBuildContext.toolchain { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L161) | Gets the explicit frozen target compiler and SDK selection, or null before composition resolves it. Browser toolchains resolve their own workload-specific SDK independently. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildContext.GetNativeBuildRoot(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L180) | Resolves native intermediates under the owning toolchain project in this checkout.
`component`: The explicit component descriptor whose recipe project owns native intermediates. | The absolute project-local obj/native path, without creating the directory. During publication, the path also contains the operation's target and compatible workspace identity. A Windows producer receives a temporary junction to the same physical directory so SDK tools can use bounded paths. | `System.ArgumentNullException`: The component owner is null.
`System.InvalidOperationException`: The selected checkout does not contain the owning toolchain project. | +| [`public Inno.Build.Toolchains.NativeBuildStatistics Inno.Build.Toolchains.NativeBuildContext.statistics { get; }`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L198) | Gets actual hashing and native execution work accumulated by this operation and its scoped contexts. | — | — | +| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordBindingBatch()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L209) | Records one complete binding batch executed by the borrowed provider. | — | — | +| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordBindingGeneration()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L214) | Records one actual bridge or managed generator invocation. | — | — | +| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordOutputRead(long bytes)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L222) | Records one full artifact output read without treating it as an input snapshot hash.
`bytes`: The actual number of bytes consumed by output integrity verification. | — | — | +| [`public void Inno.Build.Toolchains.NativeBuildContext.RecordManagedProcess()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L227) | Records a managed SDK process owned by generator extension preparation. | — | — | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithToolchain(Inno.Build.Toolchains.NativeToolchainSelection toolchain)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L247) | Attaches a provider's immutable tool selection while preserving this operation's input ownership.
`toolchain`: The complete selection resolved for the requested target. | A scoped context sharing input snapshots and statistics with this operation. | `System.ArgumentNullException`: The selection is null. | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithConfiguration(string configuration)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L265) | Selects an explicit component configuration while borrowing this operation's frozen SDK and input state.
`configuration`: The debug or release configuration owned by the product step. | A derived context with no inherited workspace or artifact identity from another configuration. | `System.ArgumentException`: The requested configuration is unsupported. | +| [`public Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeBuildContext.RequireToolchain()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L277) | Requires an explicit selection before a component can build or start a native tool. | The already frozen tool selection without performing discovery. | `System.InvalidOperationException`: Composition has not resolved a toolchain. | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithComponentOptions(Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L295) | Scopes one component's explicit product configuration without changing the frozen SDK.
`component`: The sole source and intermediate owner receiving the configuration.
`options`: The immutable linkage, arguments and configuration input closure. | A context sharing inputs, bindings and statistics with the current operation. | `System.ArgumentNullException`: An owner or configuration is null. | +| [`public Inno.Build.Toolchains.NativeComponentBuildOptions Inno.Build.Toolchains.NativeBuildContext.RequireComponentOptions(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L316) | Requires product configuration for the exact component before its recipe or tools execute.
`component`: The component requesting its scoped configuration. | The immutable configuration; absent or foreign ownership fails explicitly. | `System.InvalidOperationException`: No options are assigned to this component owner. | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithBindings(Inno.Build.Toolchains.NativeBindingGenerationResult bindings)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L333) | Creates a scoped operation with an immutable, explicitly prepared target binding closure.
`bindings`: Borrowed completed generations indexed by their Native owners. Retain this result until all derived contexts finish. | A context sharing the existing tool selection, inputs and statistics. | — | +| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBuildContext.RequireBindings(Inno.Build.Toolchains.NativeComponentDescriptor component)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L356) | Requires the target binding generation prepared for an explicitly declared Native owner.
`component`: The component whose facade and managed binding share one generation. | The immutable completed generation descriptor. | `System.InvalidOperationException`: The owner has no prepared generation in this operation.
`System.ObjectDisposedException`: The caller released the borrowed binding result before this operation finished. | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.WithBindingGenerator(Inno.Build.Toolchains.INativeBindingGenerator generator)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L369) | Borrows a generator while preserving this operation's input and tool ownership.
`generator`: The shared generation implementation selected by composition. | A derived context; neither context owns or disposes the provider. | — | +| [`public Inno.Build.Toolchains.INativeBindingGenerator Inno.Build.Toolchains.NativeBuildContext.RequireBindingGenerator()`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L386) | Requires generation to be configured explicitly before creating staging or launching tools. | The borrowed provider without discovering or constructing an implementation. | `System.InvalidOperationException`: Composition has not supplied a binding generator. | +| [`public Inno.Build.Toolchains.NativeBuildContext Inno.Build.Toolchains.NativeBuildContext.BeginInputVerification(string phase)`](../../build/toolchains/Inno.Build.Toolchains/NativeBuildContext.cs#L401) | Starts a fresh verification phase whose reads can be shared only within this derived context.
`phase`: The operation phase recorded in statistics, such as binding-locks or binding-generation. | A context with a new input inventory and hash cache; existing scopes are unchanged. | — | +| [`public sealed record Inno.Build.Toolchains.BuildHostDescriptor : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Identifies the tool execution machine without selecting any product target. | — | — | +| [`public Inno.Build.Toolchains.BuildHostDescriptor.BuildHostDescriptor(string system, string architecture, Inno.Build.Toolchains.BuildHostServices? services = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L25) | Captures host facts supplied by a product or command composition boundary.
`system`: The execution operating system identity.
`architecture`: The execution processor architecture.
`services`: Explicit host execution services, or null for direct physical execution. | — | `System.ArgumentException`: A host fact is unassigned. | +| [`public string Inno.Build.Toolchains.BuildHostDescriptor.system { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L40) | Gets the operating system on which tools execute. | — | — | +| [`public string Inno.Build.Toolchains.BuildHostDescriptor.architecture { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L45) | Gets the processor architecture on which tools execute. | — | — | +| [`public Inno.Build.Toolchains.BuildHostServices Inno.Build.Toolchains.BuildHostDescriptor.services { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L50) | Gets frozen host execution services. | — | — | +| [`public override string Inno.Build.Toolchains.BuildHostDescriptor.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.BuildHostDescriptor.operator !=(Inno.Build.Toolchains.BuildHostDescriptor? left, Inno.Build.Toolchains.BuildHostDescriptor? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.BuildHostDescriptor.operator ==(Inno.Build.Toolchains.BuildHostDescriptor? left, Inno.Build.Toolchains.BuildHostDescriptor? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.BuildHostDescriptor.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.BuildHostDescriptor.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.BuildHostDescriptor.Equals(Inno.Build.Toolchains.BuildHostDescriptor? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/BuildHostDescriptor.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Build.Toolchains.ComponentNativeProductExecutor : Inno.Build.Toolchains.INativeProductExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L13) | Executes portable component producers in their declared dependency order. | — | — | +| [`public System.Threading.Tasks.Task Inno.Build.Toolchains.ComponentNativeProductExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.ProductNativeBuildPlan plan, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L16) | Builds or reuses the complete product with the operation's frozen tools.
`context`: The frozen operation.
`plan`: The complete dependency-ordered component plan.
`cancellationToken`: Cancels waits and unpublished work. | The owned complete output and binding closure. | — | +| [`public Inno.Build.Toolchains.ComponentNativeProductExecutor.ComponentNativeProductExecutor()`](../../build/toolchains/Inno.Build.Toolchains/Native/ComponentNativeProductExecutor.cs#L13) | Inherited or compiler-provided contract. | — | — | +| [`public interface Inno.Build.Toolchains.INativeBindingGenerator`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L10) | Prepares an entire binding closure without coupling native recipes to a generator or build host. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeBindingGenerator.ComputeInputFingerprintAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBindingGenerationRequest request, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L34) | Computes the selected generator source input identity without producing bindings or compiling a product.
`context`: The borrowed operation with frozen target tools and phase-local input reads.
`request`: The exact component, configuration target and output mode whose source closure is planned.
`cancellationToken`: Cancels configuration loading, dependency evaluation and content hashing. | The complete source preparation identity, distinct from any generated artifact fingerprint. It remains computable when reconstructible generation outputs have been retired. | `System.InvalidOperationException`: The selected tools, configuration, generator or extension inputs cannot be resolved.
`System.OperationCanceledException`: Input planning was canceled. | +| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeBindingGenerator.GenerateAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBindingGenerationRequest request, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeBindingGenerator.cs#L67) | Generates or verifies the requested closure under publication ownership.
`context`: The borrowed operation owning frozen inputs, tool selection and statistics. Configuration path variables must resolve from its selected toolchain without changing process state.
`request`: The immutable target, component closure and publication policy.
`cancellationToken`: Cancels preparation; active work must drain before returning. | An owned complete immutable map indexed by Native owner project, never a partial closure. Dispose the result only after all generated-source and native-bridge consumers finish. | `System.InvalidOperationException`: Inputs change during preparation, a required frozen SDK value is absent, or required generator capabilities are unavailable.
`System.IO.InvalidDataException`: A definition or generated output fails its declared contract.
`System.OperationCanceledException`: The request was canceled and its active work has completed cleanup. | +| [`public interface Inno.Build.Toolchains.INativeProductExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeProductExecutor.cs#L9) | Executes an explicitly selected native product graph without target discovery. | — | — | +| [`System.Threading.Tasks.Task Inno.Build.Toolchains.INativeProductExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.ProductNativeBuildPlan plan, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeProductExecutor.cs#L26) | Builds or reuses the complete product with the operation's frozen tools.
`context`: The frozen operation.
`plan`: The complete dependency-ordered component plan.
`cancellationToken`: Cancels waits and unpublished work. | The owned complete output and binding closure. | — | +| [`public interface Inno.Build.Toolchains.INativeToolchainProvider`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeToolchainProvider.cs#L9) | Resolves one platform's SDK for an explicit target on a declared tool execution host. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.Toolchains.INativeToolchainProvider.ResolveAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.BuildHostDescriptor host, string targetId, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/INativeToolchainProvider.cs#L29) | Freezes compiler tools, SDK inputs and child environment before a native operation starts.
`context`: The checkout and configuration owned by the requesting operation.
`host`: The explicitly declared execution host, independent of the target.
`targetId`: The exact requested target; unsupported combinations must fail.
`cancellationToken`: Cancels SDK discovery before any product staging. | A complete immutable tool selection for the requested host and target. | — | +| [`public enum Inno.Build.Toolchains.NativeArtifactRole : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L13) | Describes the link or deployment role explicitly assigned by a native producer. | — | — | +| [`Inno.Build.Toolchains.NativeArtifactRole.SharedLibrary = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L18) | A library loaded by the runtime. | — | — | +| [`Inno.Build.Toolchains.NativeArtifactRole.ImportLibrary = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L22) | A separate linker input referring to a shared library. | — | — | +| [`Inno.Build.Toolchains.NativeArtifactRole.StaticArchive = 2`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L26) | An archive linked into the final executable. | — | — | +| [`Inno.Build.Toolchains.NativeArtifactRole.SupportFile = 3`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L30) | A tool, header or other supporting payload. | — | — | +| [`public sealed record Inno.Build.Toolchains.NativeArtifact : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Identifies one exact output and its producer-declared role. | — | — | +| [`public Inno.Build.Toolchains.NativeArtifact.NativeArtifact(string logicalId, string file, Inno.Build.Toolchains.NativeArtifactRole role)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L53) | Captures an output independently of platform suffix conventions.
`logicalId`: The unique producer-owned output identity.
`file`: The absolute output path retained by the product's reading ownership.
`role`: The explicitly declared link or deployment role. | — | `System.ArgumentException`: The identity, path or role is invalid. | +| [`public string Inno.Build.Toolchains.NativeArtifact.logicalId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L69) | Gets the producer-owned output identity. | — | — | +| [`public string Inno.Build.Toolchains.NativeArtifact.file { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L73) | Gets the exact absolute output path. | — | — | +| [`public Inno.Build.Toolchains.NativeArtifactRole Inno.Build.Toolchains.NativeArtifact.role { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L77) | Gets the explicit link or deployment role. | — | — | +| [`public override string Inno.Build.Toolchains.NativeArtifact.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeArtifact.operator !=(Inno.Build.Toolchains.NativeArtifact? left, Inno.Build.Toolchains.NativeArtifact? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeArtifact.operator ==(Inno.Build.Toolchains.NativeArtifact? left, Inno.Build.Toolchains.NativeArtifact? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeArtifact.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeArtifact.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeArtifact.Equals(Inno.Build.Toolchains.NativeArtifact? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifact.cs#L36) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public static class Inno.Build.Toolchains.NativeArtifactPublisher`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifactPublisher.cs#L15) | Publishes complete native products by source and toolchain identity under process-shared ownership. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeArtifactPublisher.PublishAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeBuildRecipe recipe, System.Func build, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), System.Func>? describeOutputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeArtifactPublisher.cs#L51) | Reuses a validated product or builds an isolated candidate and publishes it after input stability checks.
`context`: Checkout and configuration selected by build composition.
`recipe`: The frozen component, target, input inventory and ordered compiler recipe.
`build`: Producer receiving an identity-scoped context, private output directory and cancellation token. It must await all work and validate required outputs before returning.
`cancellationToken`: Cancels ownership acquisition or candidate preparation before publication.
`describeOutputs`: The producer's exact role inventory, or null for generic support payloads. | A complete native product with an exact, read-only output closure. | `System.ArgumentException`: A component or target identifier is not a single path segment.
`System.IO.IOException`: An input, candidate or artifact cannot be read or published.
`System.InvalidOperationException`: Inputs change during preparation or the producer returns no complete output.
`System.OperationCanceledException`: Ownership acquisition or preparation is canceled; previous products remain installed. | +| [`public sealed class Inno.Build.Toolchains.NativeBindingCompilation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | Describes a selected component's native requirements independently of its source generator. | — | — | +| [`public required Inno.Build.Toolchains.NativeBindingSourceRoot[] Inno.Build.Toolchains.NativeBindingCompilation.roots { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L27) | Gets root bindings whose identities are local to this component. | — | — | +| [`public required Inno.Build.Toolchains.NativeBindingSourceFile[] Inno.Build.Toolchains.NativeBindingCompilation.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L32) | Gets all exact input files; only files marked compile are translation units. | — | — | +| [`public required Inno.Build.Toolchains.NativeBindingIncludeDirectory[] Inno.Build.Toolchains.NativeBindingCompilation.includeDirectories { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L37) | Gets include requirements in their original search order. | — | — | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.defines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L42) | Gets ordered definitions for manifest translation units; upstream target configuration remains component-owned. | — | — | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.compilerDefines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L47) | Gets parser target macro facts; these must not override the consuming compiler's builtins. | — | — | +| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cxxStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L52) | Gets the minimum C++ standard, or zero when no C++ translation unit is required. | — | — | +| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L57) | Gets the minimum C standard, or zero when no C translation unit is required. | — | — | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.requiredExports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L62) | Gets the exact native symbols that the component must provide. | — | — | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportSymbols { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L67) | Gets static table entry points, or an empty array for dynamic-only bindings. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBindingCompilation.staticExportAbi { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L72) | Gets the static table ABI digest, or an empty value when no static table exists. | — | — | +| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportNames { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L77) | Gets static table entries in address-resolution order. | — | — | +| [`public static Inno.Build.Toolchains.NativeBindingCompilation Inno.Build.Toolchains.NativeBindingCompilation.Load(string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L97) | Reads the current engine-owned compilation description from a selected native output.
`nativeDirectory`: The complete selected generation's native directory. | The validated description; missing or malformed descriptions fail explicitly. | `System.IO.IOException`: The description cannot be read.
`System.Text.Json.JsonException`: The description does not match the current contract.
`System.IO.InvalidDataException`: A root, path or content identity is invalid. | +| [`public string Inno.Build.Toolchains.NativeBindingCompilation.ResolveRoot(string engineRoot, string nativeDirectory, string rootId)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L124) | Resolves a component-local root using the current checkout and selected output.
`engineRoot`: The current checkout's absolute root.
`nativeDirectory`: The selected generation's absolute native output.
`rootId`: An ordinal root identity from this description. | The physical root; no other component's root table participates. | `System.IO.InvalidDataException`: The root identity is unknown. | +| [`public void Inno.Build.Toolchains.NativeBindingCompilation.VerifyInputs(System.Func resolveRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L152) | Verifies the exact bytes at original or remapped roots before their use.
`resolveRoot`: Resolves a root identity to the selected physical or copied directory.
`cancellationToken`: Cancels verification without publishing a result. | — | `System.IO.InvalidDataException`: A required file has a different length or digest.
`System.IO.IOException`: A required input is missing or cannot be read.
`System.OperationCanceledException`: Verification was canceled. | +| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingCompilation.GetInputs(string engineRoot, string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L179) | Enumerates exact file declarations for standalone or aggregate recipe fingerprinting.
`engineRoot`: The current checkout's absolute root.
`nativeDirectory`: The selected native artifact directory. | All generated, user and SDK inputs with their current physical locations. | — | +| [`public Inno.Build.Toolchains.NativeBindingCompilation.NativeBindingCompilation()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L14) | Describes the complete binding generation selected by one native build request. | — | — | +| [`public static void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.WriteSelection(string path, System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IReadOnlyDictionary linkage)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L38) | Writes the exact native binding identities for a subsequent managed build without reselecting generations. Identical content preserves the existing import timestamp; changed content is installed atomically.
`path`: The MSBuild selection document in an intermediate directory exclusively owned by the caller.
`generations`: Explicit checkout-relative native project paths and their complete selected generations.
`linkage`: Explicit linkage of each selected component, used by its managed native initialization boundary. | — | `System.ArgumentException`: A project name is unsafe or repeated, or component linkage is invalid or incomplete.
`System.ArgumentNullException`: The generation or linkage closure is null.
`System.IO.IOException`: The selection cannot be read or its complete replacement cannot be installed. | +| [`public static void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.ValidateSelection(string path, System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IReadOnlyDictionary linkage)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L81) | Rejects publication when the managed selection differs from the complete native generations to deploy.
`path`: The operation-owned selection consumed during managed compilation.
`generations`: The complete current native project closure and its selected generations.
`linkage`: The exact component linkage used to build the native closure. | — | `System.IO.InvalidDataException`: The selection differs in project closure, fingerprint, source location, linkage, or structure.
`System.ArgumentException`: The current generation closure has an unsafe or repeated project name.
`System.ArgumentNullException`: The current generation closure is null.
`System.IO.FileNotFoundException`: The managed selection is no longer available.
`System.Xml.XmlException`: The selection is not a valid XML document. | +| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.fingerprint { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L93) | Gets the immutable input identity assigned to this target generation. | — | — | +| [`public required bool Inno.Build.Toolchains.NativeBindingGenerationDescriptor.usesRuntimeImports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L99) | Gets whether the generated context uses runtime-resolved imports instead of a module provider. This initialization choice is independent of static or shared native linkage. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.bindingsPath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L104) | Gets the absolute managed source path selected by the component project. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.nativeDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L109) | Gets the complete native artifact and compilation-requirement directory, or empty when none is required. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.artifactDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L114) | Gets the immutable artifact root protected by reading leases, or an empty value for mutable host sources. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBindingGenerationDescriptor.artifactOwnerDirectory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L120) | Gets the publisher root coordinating immutable artifact readers and retirement, or empty for host sources. This owner remains unchanged when component sources are copied into a frozen build workspace. | — | — | +| [`public static Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBindingGenerationDescriptor.Load(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L140) | Reads and validates the result of a completed component generation request.
`path`: The request-owned descriptor file written after successful generation. | The selected generation whose managed source and optional bridge are present. | `System.IO.InvalidDataException`: The descriptor is missing required output identities or contains unavailable outputs.
`System.Text.Json.JsonException`: The descriptor cannot be read as the current contract.
`System.IO.IOException`: An immutable artifact escapes its declared publisher or traverses a linked directory. | +| [`public void Inno.Build.Toolchains.NativeBindingGenerationDescriptor.Write(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L166) | Atomically writes this completed generation to a request-owned descriptor file.
`path`: The descriptor destination; a unique path keeps concurrent requests independent. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor.NativeBindingGenerationDescriptor()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L14) | Inherited or compiler-provided contract. | — | — | +| [`public enum Inno.Build.Toolchains.NativeBindingOutputMode : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L11) | Selects the publication owner of generated sources. | — | — | +| [`Inno.Build.Toolchains.NativeBindingOutputMode.HostSource = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L16) | Replaces the component's authored host source and bridge outputs together. | — | — | +| [`Inno.Build.Toolchains.NativeBindingOutputMode.TargetArtifacts = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L20) | Publishes immutable target outputs under the component's generation fingerprint. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationRequest`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L26) | Freezes the complete binding request before ownership waits or candidate creation. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingGenerationRequest.NativeBindingGenerationRequest(System.Collections.Generic.IEnumerable components, string targetId, Inno.Build.Toolchains.NativeBindingOutputMode outputMode, bool checkOnly = false, System.Collections.Generic.IReadOnlyDictionary? expectedFingerprints = null, System.Collections.Generic.IReadOnlyDictionary? componentOptions = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L52) | Captures explicit owners and a target without discovering components or parsing a build project.
`components`: The component owners, each declaring one binding definition.
`targetId`: The target identity selecting a declared configuration; host output uses the host definition.
`outputMode`: Whether to write authored host sources or isolated target artifacts.
`checkOnly`: Whether to compare complete output without publishing a candidate.
`expectedFingerprints`: Optional expected identities indexed by Native project; mismatches reject the request.
`componentOptions`: Explicit component configurations keyed by native owner project, or null for component defaults. | — | `System.ArgumentException`: A target, owner, output mode or expected identity is invalid or duplicated. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBindingGenerationRequest.components { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L86) | Gets the immutable component closure owned by this request. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingGenerationRequest.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L90) | Gets the explicit configuration target, independent of the build execution host. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingOutputMode Inno.Build.Toolchains.NativeBindingGenerationRequest.outputMode { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L94) | Gets the selected output ownership policy. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingGenerationRequest.checkOnly { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L98) | Gets whether publication is forbidden and current output must match fresh generation. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeBindingGenerationRequest.expectedFingerprints { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L102) | Gets the expected identities which must match before any candidate is generated. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeBindingGenerationRequest.componentOptions { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationRequest.cs#L107) | Gets explicit component binding and linkage choices. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeBindingGenerationResult : System.Collections.Generic.IReadOnlyDictionary, System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L14) | Owns the reading protection of a complete binding closure until its final compiler consumer finishes. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingGenerationResult.NativeBindingGenerationResult(System.Collections.Generic.IReadOnlyDictionary generations, System.Collections.Generic.IEnumerable ownership)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L34) | Freezes a complete generation map and accepts its already acquired reading ownership.
`generations`: The complete Native project to generation mapping.
`ownership`: Protection transferred on successful construction; retained by the caller on failure. | — | `System.ArgumentNullException`: Either collection is null.
`System.ArgumentException`: A generation or protection entry is null. | +| [`public int Inno.Build.Toolchains.NativeBindingGenerationResult.Count { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L52) | Gets the number of elements in the collection. | The number of elements in the collection. | `System.ObjectDisposedException`: Reading ownership has ended. | +| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingGenerationResult.Keys { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L61) | Gets an enumerable collection that contains the keys in the read-only dictionary. | An enumerable collection that contains the keys in the read-only dictionary. | `System.ObjectDisposedException`: Reading ownership has ended. | +| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingGenerationResult.Values { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L70) | Gets an enumerable collection that contains the values in the read-only dictionary. | An enumerable collection that contains the values in the read-only dictionary. | `System.ObjectDisposedException`: Reading ownership has ended. | +| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor Inno.Build.Toolchains.NativeBindingGenerationResult.this[string key] { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L87) | Gets a borrowed descriptor whose outputs remain protected until this result is disposed.
`key`: The checkout-relative Native owner project. | The selected generation; neither its paths nor retained descriptors extend reading ownership. | `System.ObjectDisposedException`: Reading ownership has ended.
`System.Collections.Generic.KeyNotFoundException`: The project is outside this closure. | +| [`public bool Inno.Build.Toolchains.NativeBindingGenerationResult.ContainsKey(string key)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L93) | Determines whether the read-only dictionary contains an element that has the specified key.
`key`: The key to locate. | if the read-only dictionary contains an element that has the specified key; otherwise, . | `System.ArgumentNullException`: is .
`System.ObjectDisposedException`: Reading ownership has ended. | +| [`public bool Inno.Build.Toolchains.NativeBindingGenerationResult.TryGetValue(string key, out Inno.Build.Toolchains.NativeBindingGenerationDescriptor value)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L102) | Gets the value that is associated with the specified key.
`key`: The key to locate.
`value`: When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. | if the object that implements the interface contains an element that has the specified key; otherwise, . | `System.ArgumentNullException`: is .
`System.ObjectDisposedException`: Reading ownership has ended. | +| [`public System.Collections.Generic.IEnumerator> Inno.Build.Toolchains.NativeBindingGenerationResult.GetEnumerator()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L114) | Returns an enumerator that iterates through the collection. | An enumerator that can be used to iterate through the collection. | `System.ObjectDisposedException`: Reading ownership has ended. | +| [`public void Inno.Build.Toolchains.NativeBindingGenerationResult.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationResult.cs#L122) | Releases all protection in reverse acquisition order; repeated disposal has no effect. | — | `System.AggregateException`: One or more owners failed to release after every owner was attempted. | +| [`public sealed record Inno.Build.Toolchains.NativeBindingIncludeDirectory : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement.
`rootId`: The component-local input root.
`relativePath`: The root-relative directory, or empty for the root itself.
`system`: Whether this directory contains system headers.
`framework`: Whether the compiler must use framework search semantics. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingIncludeDirectory.NativeBindingIncludeDirectory(string rootId, string relativePath, bool system, bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement.
`rootId`: The component-local input root.
`relativePath`: The root-relative directory, or empty for the root itself.
`system`: Whether this directory contains system headers.
`framework`: Whether the compiler must use framework search semantics. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L19) | The component-local input root. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L20) | The root-relative directory, or empty for the root itself. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.system { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L21) | Whether this directory contains system headers. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.framework { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L22) | Whether the compiler must use framework search semantics. | — | — | +| [`public override string Inno.Build.Toolchains.NativeBindingIncludeDirectory.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator !=(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator ==(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBindingIncludeDirectory.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(Inno.Build.Toolchains.NativeBindingIncludeDirectory? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Build.Toolchains.NativeBindingIncludeDirectory.Deconstruct(out string rootId, out string relativePath, out bool system, out bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.NativeBindingPreparation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingPreparation.cs#L12) | Prepares a coherent target closure through the borrowed generator, without invoking MSBuild. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeBindingPreparation.PrepareAsync(Inno.Build.Toolchains.NativeBuildContext context, System.Collections.Generic.IReadOnlyList components, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? componentOptions = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingPreparation.cs#L35) | Requests one binding batch for all generated components of a product.
`context`: The operation owning a frozen target selection and configured generator.
`components`: The declared component closure; components without bindings are excluded.
`cancellationToken`: Cancels generation and drains active work before returning.
`componentOptions`: Exact per-owner component choices, or null for component defaults. | An owned generation closure, empty for a product without bindings. Retain it through final consumption. | `System.InvalidOperationException`: The tool selection or required binding generator is absent. | +| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceFile : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation.
`rootId`: The component-local input root.
`relativePath`: The portable root-relative filename.
`length`: The expected byte length.
`sha256`: The lowercase SHA-256 content identity.
`compile`: Whether this file is a translation unit rather than an include dependency. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingSourceFile.NativeBindingSourceFile(string rootId, string relativePath, long length, string sha256, bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation.
`rootId`: The component-local input root.
`relativePath`: The portable root-relative filename.
`length`: The expected byte length.
`sha256`: The lowercase SHA-256 content identity.
`compile`: Whether this file is a translation unit rather than an include dependency. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L22) | The component-local input root. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L23) | The portable root-relative filename. | — | — | +| [`public long Inno.Build.Toolchains.NativeBindingSourceFile.length { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L24) | The expected byte length. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.sha256 { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L25) | The lowercase SHA-256 content identity. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.compile { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L26) | Whether this file is a translation unit rather than an include dependency. | — | — | +| [`public override string Inno.Build.Toolchains.NativeBindingSourceFile.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator !=(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator ==(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBindingSourceFile.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(Inno.Build.Toolchains.NativeBindingSourceFile? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Build.Toolchains.NativeBindingSourceFile.Deconstruct(out string rootId, out string relativePath, out long length, out string sha256, out bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Inherited or compiler-provided contract. | — | — | +| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceRoot : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract.
`id`: An ordinal identity scoped to this component's selected generation.
`directory`: The checkout-relative or absolute external location; empty for generated output.
`generated`: Whether this root denotes the selected native output directory.
`frozen`: Whether source files must be copied before compilation. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingSourceRoot.NativeBindingSourceRoot(string id, string directory, bool generated, bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract.
`id`: An ordinal identity scoped to this component's selected generation.
`directory`: The checkout-relative or absolute external location; empty for generated output.
`generated`: Whether this root denotes the selected native output directory.
`frozen`: Whether source files must be copied before compilation. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.id { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L19) | An ordinal identity scoped to this component's selected generation. | — | — | +| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.directory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L20) | The checkout-relative or absolute external location; empty for generated output. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.generated { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L21) | Whether this root denotes the selected native output directory. | — | — | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.frozen { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L22) | Whether source files must be copied before compilation. | — | — | +| [`public override string Inno.Build.Toolchains.NativeBindingSourceRoot.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator !=(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator ==(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBindingSourceRoot.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(Inno.Build.Toolchains.NativeBindingSourceRoot? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Build.Toolchains.NativeBindingSourceRoot.Deconstruct(out string id, out string directory, out bool generated, out bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.NativeBuildFingerprint`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L13) | Derives recipe identities from ordered declarations, logical input names and complete source bytes. | — | — | +| [`public static string Inno.Build.Toolchains.NativeBuildFingerprint.Create(System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L39) | Hashes a complete declared input closure independently of checkout location and file timestamps.
`declarations`: Ordered target, configuration, SDK, compiler and linker arguments. Order is significant.
`inputs`: Files and directories with explicit logical identities; overlapping reading locations are hashed once.
`cancellationToken`: Cancels enumeration or complete content hashing before a fingerprint is returned. | A lowercase SHA-256 identity that changes when a declaration, input inventory or input byte changes. | `System.ArgumentNullException`: A required input collection is null.
`System.IO.IOException`: An input cannot be read or contains a directory-link cycle.
`System.OperationCanceledException`: Content hashing was canceled. | +| [`public static string Inno.Build.Toolchains.NativeBuildFingerprint.Create(Inno.Build.Toolchains.NativeBuildContext context, System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildFingerprint.cs#L68) | Hashes declarations using the current operation's initial snapshot or explicit fresh verification phase.
`context`: The owner of this phase's complete input inventory and content hashes.
`declarations`: Ordered non-file inputs; declaration order remains significant.
`inputs`: Complete inputs with stable logical identities and physical read locations.
`cancellationToken`: Cancels complete enumeration and hashing. | The SHA-256 identity; repeated physical reads within this phase are reused. | — | +| [`public sealed record Inno.Build.Toolchains.NativeBuildInput : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Separates the reproducible identity of an input from its physical reading location. | — | — | +| [`public Inno.Build.Toolchains.NativeBuildInput.NativeBuildInput(string logicalPath, string physicalPath)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L23) | Declares one file or directory consumed by a native recipe.
`logicalPath`: A nonempty, slash-separated identity relative to a declared logical root.
`physicalPath`: The absolute file or directory used only for reading and execution. | — | `System.ArgumentException`: An identity is empty, contains a control character or backslash, or the physical path is relative. | +| [`public string Inno.Build.Toolchains.NativeBuildInput.logicalPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L42) | Gets the identity included in the recipe fingerprint. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildInput.physicalPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L47) | Gets the reading location, which is not implicitly included in the fingerprint. | — | — | +| [`public static Inno.Build.Toolchains.NativeBuildInput Inno.Build.Toolchains.NativeBuildInput.FromPath(string engineRoot, string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L61) | Declares a checkout-relative input, or an external SDK location whose path affects tool selection.
`engineRoot`: The absolute checkout root assigned the logical prefix engine/.
`path`: A checkout-relative input or an absolute SDK input. | A normalized declaration preserving external SDK location identity. | — | +| [`public override string Inno.Build.Toolchains.NativeBuildInput.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildInput.operator !=(Inno.Build.Toolchains.NativeBuildInput? left, Inno.Build.Toolchains.NativeBuildInput? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildInput.operator ==(Inno.Build.Toolchains.NativeBuildInput? left, Inno.Build.Toolchains.NativeBuildInput? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBuildInput.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBuildInput.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBuildInput.Equals(Inno.Build.Toolchains.NativeBuildInput? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildInput.cs#L9) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed record Inno.Build.Toolchains.NativeBuildPhaseStatistics : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Describes actual full reads and physical read reuse within one explicitly bounded input phase. | — | — | +| [`public required string Inno.Build.Toolchains.NativeBuildPhaseStatistics.phase { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L11) | Gets the named operation phase which owns these read results. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L15) | Gets the number of complete physical file reads. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.bytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L19) | Gets the number of bytes actually read for hashing. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildPhaseStatistics.reusedReads { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L23) | Gets repeated physical hash requests satisfied from this phase's cache. | — | — | +| [`public override string Inno.Build.Toolchains.NativeBuildPhaseStatistics.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.operator !=(Inno.Build.Toolchains.NativeBuildPhaseStatistics? left, Inno.Build.Toolchains.NativeBuildPhaseStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.operator ==(Inno.Build.Toolchains.NativeBuildPhaseStatistics? left, Inno.Build.Toolchains.NativeBuildPhaseStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBuildPhaseStatistics.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBuildPhaseStatistics.Equals(Inno.Build.Toolchains.NativeBuildPhaseStatistics? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public Inno.Build.Toolchains.NativeBuildPhaseStatistics.NativeBuildPhaseStatistics()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildPhaseStatistics.cs#L6) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeBuildProduct : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L11) | Identifies one complete, integrity-checked native artifact and its exact published file closure. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildProduct.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L46) | Gets the component identity supplied by the owning toolchain. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildProduct.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L51) | Gets the target ABI selected for this build. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildProduct.fingerprint { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L56) | Gets the content fingerprint covering sources, tool executables and toolchain declarations. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildProduct.directory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L61) | Gets the immutable artifact root containing Outputs and its integrity manifest. | — | — | +| [`public Inno.Build.Toolchains.NativeBindingGenerationDescriptor? Inno.Build.Toolchains.NativeBuildProduct.bindingGeneration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L67) | Gets the exact binding generation compiled into this product, or null for a component without bindings. Managed consumers must retain this selection rather than resolve a new generation after native preparation. | — | — | +| [`public string? Inno.Build.Toolchains.NativeBuildProduct.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L72) | Gets the shared-library suffix declared by the selected SDK, or null for an SDK-independent producer. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildProduct.files { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L77) | Gets the exact absolute output file paths validated by the publisher. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildProduct.artifacts { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L82) | Gets the exact producer-declared output and link roles. | — | — | +| [`public void Inno.Build.Toolchains.NativeBuildProduct.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildProduct.cs#L88) | Releases the artifact after all deployment, compilation and linking consumers finish. File paths must not be used afterwards; repeated disposal has no effect. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeBuildRecipe`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L11) | Freezes a component's input closure, ordered tool selection and intermediate owner. | — | — | +| [`public Inno.Build.Toolchains.NativeBuildRecipe.NativeBuildRecipe(Inno.Build.Toolchains.NativeComponentDescriptor owner, string component, string targetId, System.Collections.Generic.IEnumerable inputs, System.Collections.Generic.IEnumerable declarations)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L38) | Creates a complete recipe without inferring implementation identity from an assembly MVID.
`owner`: The explicit component descriptor identifying source and intermediate owners.
`component`: The component's portable artifact path segment.
`targetId`: The ABI's portable artifact path segment.
`inputs`: The complete source, generator, executor and SDK input closure.
`declarations`: Ordered compiler, linker, include-search and non-file inputs; order is significant. | — | `System.ArgumentException`: A component or target is not a portable segment, or an input is null.
`System.ArgumentNullException`: A required owner or collection is null. | +| [`public Inno.Build.Toolchains.NativeComponentDescriptor Inno.Build.Toolchains.NativeBuildRecipe.owner { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L65) | Gets the declared intermediate owner independently of assembly location and MVID. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildRecipe.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L70) | Gets the component's artifact path segment. | — | — | +| [`public string Inno.Build.Toolchains.NativeBuildRecipe.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L75) | Gets the target ABI's artifact path segment. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildRecipe.inputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L80) | Gets the frozen declarations of physical inputs and their logical identities. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildRecipe.declarations { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L85) | Gets ordered non-file inputs; callers must explicitly sort unordered metadata before construction. | — | — | +| [`public static Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.CreateForComponent(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor owner, string component, string targetId, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable declarations, System.Collections.Generic.IEnumerable implementationPaths)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L120) | Declares a component's source, execution and SDK inputs before binding outputs need to exist. The artifact publisher attaches the exact selected generation before hashing or compiling.
`context`: The operation's checkout and previously selected host tools.
`owner`: The component project whose implementation participates in recipe identity.
`component`: The native component artifact segment.
`targetId`: The selected target ABI.
`inputPaths`: All handwritten native sources, component tools and build definitions. Selected managed bindings and generated bridges are attached by the publisher.
`declarations`: Ordered component arguments and generation identities.
`implementationPaths`: The complete explicit implementation inputs used by this component, excluding unrelated product code. | The frozen component declaration, without generating bindings or reading generated output. Unrelated build and Editor implementation assemblies do not affect it. | `System.IO.FileNotFoundException`: A required executor or component project is absent from the checkout. | +| [`public Inno.Build.Toolchains.NativeBuildRecipe Inno.Build.Toolchains.NativeBuildRecipe.WithDependency(Inno.Build.Toolchains.NativeBuildInput input, string fingerprint)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildRecipe.cs#L190) | Adds one explicitly selected published dependency while preserving the recipe's frozen execution tools.
`input`: The dependency file whose bytes participate in compilation.
`fingerprint`: The immutable dependency artifact identity selected by the product. | A new declaration with the dependency input and identity; this recipe is unchanged. | `System.ArgumentException`: The dependency identity is empty.
`System.ArgumentNullException`: The input is null. | +| [`public sealed record Inno.Build.Toolchains.NativeBuildStatistics : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Reports actual input reading and compiler process work accumulated by one build context. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.hashedFiles { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L13) | Gets the number of complete file hashes read, including required stability verification. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.hashedBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L18) | Gets the actual bytes consumed by input hashing, excluding output integrity validation. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.nativeProcesses { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L24) | Gets native execution processes started through the context's frozen tool selection. SDK discovery and managed task bootstrapping are recorded by their separate build logs. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeBuildStatistics.phases { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L29) | Gets immutable phase-specific read accounting accumulated by this operation. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.bindingBatches { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L34) | Gets complete binding batches requested by this operation. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.bindingGenerations { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L39) | Gets actual bridge or managed generation invocations, excluding valid artifact reuse. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.materializedBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L44) | Gets bytes copied to freeze mutable repository inputs. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.outputFiles { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L49) | Gets independent artifact output hashes performed for integrity verification and publication. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.outputBytes { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L54) | Gets bytes read from actual outputs, separately from source snapshot verification. | — | — | +| [`public long Inno.Build.Toolchains.NativeBuildStatistics.managedProcesses { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L59) | Gets managed SDK processes started for generator extension preparation and managed input evaluation. | — | — | +| [`public override string Inno.Build.Toolchains.NativeBuildStatistics.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildStatistics.operator !=(Inno.Build.Toolchains.NativeBuildStatistics? left, Inno.Build.Toolchains.NativeBuildStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.Toolchains.NativeBuildStatistics.operator ==(Inno.Build.Toolchains.NativeBuildStatistics? left, Inno.Build.Toolchains.NativeBuildStatistics? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.Toolchains.NativeBuildStatistics.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Build.Toolchains.NativeBuildStatistics.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Build.Toolchains.NativeBuildStatistics.Equals(Inno.Build.Toolchains.NativeBuildStatistics? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public Inno.Build.Toolchains.NativeBuildStatistics.NativeBuildStatistics()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBuildStatistics.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.NativeCMakeExecutor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L13) | Executes component-owned CMake definitions using an already frozen target SDK selection. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeCMakeExecutor.PrepareSourceAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor component, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L39) | Copies the recipe's declared checkout inputs before a cold CMake invocation.
`context`: The producer scope supplied by NativeArtifactPublisher while holding its intermediate lease.
`component`: The sole owner of the intermediate source copy.
`cancellationToken`: Cancels copying before the source candidate is installed. | The same prepared input descriptor for subsequent invocations in this producer scope. | `System.InvalidOperationException`: No recipe scope exists or an input changed during copying.
`System.OperationCanceledException`: Copying was canceled before publication.
`System.ArgumentNullException`: The context or component is null. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.NativeCMakeExecutor.BuildAsync(Inno.Build.Toolchains.NativeBuildContext context, Inno.Build.Toolchains.NativeComponentDescriptor component, string sourceDirectory, string? target, System.Collections.Generic.IReadOnlyList componentArguments, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L86) | Configures and builds an explicit component target without detecting the current platform.
`context`: The recipe-owned producer scope supplied by NativeArtifactPublisher, with frozen tools and inputs.
`component`: The unique component owner of target-scoped intermediate output.
`sourceDirectory`: The declared component-owned source directory, resolved to the operation's independent copy.
`target`: The declared CMake build target, or null for the complete selected project.
`componentArguments`: Semantic component options; SDK and generator arguments come from the selection.
`cancellationToken`: Cancels and drains the complete tool process tree. | The owned CMake build directory after successful compilation. | `System.InvalidOperationException`: Tools are unassigned or configuration or compilation fails.
`System.OperationCanceledException`: The operation was canceled.
`System.ArgumentException`: Product definitions conflict with a semantic definition owned by the component recipe. | +| [`public static string Inno.Build.Toolchains.NativeCMakeExecutor.FindOutput(Inno.Build.Toolchains.NativeBuildContext context, string buildDirectory, string filePattern)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeExecutor.cs#L171) | Selects one declared output while excluding CMake's compiler probes and other build configurations.
`context`: The selected configuration and target generator policy.
`buildDirectory`: The component-owned CMake intermediate directory.
`filePattern`: The component's exact basename or bounded filename pattern. | The sole matching output, or an explicit failure for a missing or ambiguous result. | `System.IO.FileNotFoundException`: The component did not produce exactly one expected output. | +| [`public sealed class Inno.Build.Toolchains.NativeCMakeSource`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L15) | Describes copied recipe inputs owned by one cold CMake producer. External SDK locations remain assigned to the frozen toolchain. | — | — | +| [`public string Inno.Build.Toolchains.NativeCMakeSource.root { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L52) | Gets the isolated source root; this is neither the checkout nor an output installation directory. | — | — | +| [`public string Inno.Build.Toolchains.NativeCMakeSource.ResolvePath(string path)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L69) | Resolves a declared checkout or selected binding input to its copied counterpart.
`path`: An absolute recipe input path. External SDK paths retain their original location. | The copied checkout or binding input, or the unchanged external SDK location. | `System.ArgumentException`: The path is empty or relative.
`System.InvalidOperationException`: A checkout or selected binding path was not declared by the recipe. | +| [`public string Inno.Build.Toolchains.NativeCMakeSource.ResolveDefinition(string argument)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCMakeSource.cs#L94) | Resolves declared input paths in one CMake definition without redirecting output or SDK locations.
`argument`: An ordered CMake argument; definition values may contain semicolon-separated paths. | The definition with known input paths redirected, or the original nondefinition argument. | `System.ArgumentNullException`: The argument is null. | +| [`public sealed class Inno.Build.Toolchains.NativeCompilerInputs`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L11) | Freezes SDK-resolved native compiler and header inputs without interpreting environment conventions. | — | — | +| [`public Inno.Build.Toolchains.NativeCompilerInputs.NativeCompilerInputs(string? cCompiler = null, string? cppCompiler = null, string? sysRoot = null, System.Collections.Generic.IEnumerable? systemIncludeDirectories = null, System.Collections.Generic.IEnumerable? cppSystemIncludeDirectories = null, System.Collections.Generic.IEnumerable? defines = null, System.Collections.Generic.IEnumerable? arguments = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L40) | Captures explicit parser inputs supplied by the target SDK provider.
`cCompiler`: The selected C compiler, or null when this selection does not provide one.
`cppCompiler`: The selected C++ compiler, or null when this selection does not provide one.
`sysRoot`: The SDK root, or null when the compiler does not use a sysroot.
`systemIncludeDirectories`: Ordered system header directories.
`cppSystemIncludeDirectories`: Ordered C++ standard library directories.
`defines`: Ordered target preprocessor definitions.
`arguments`: Ordered parser arguments supplied by the SDK. | — | `System.ArgumentException`: An assigned physical path is not absolute. | +| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.cCompiler { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L61) | Gets the explicit C compiler, or null when unavailable. | — | — | +| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.cppCompiler { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L65) | Gets the explicit C++ compiler, or null when unavailable. | — | — | +| [`public string? Inno.Build.Toolchains.NativeCompilerInputs.sysRoot { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L69) | Gets the target SDK root, or null when unnecessary. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.systemIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L73) | Gets ordered SDK header directories. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.cppSystemIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L77) | Gets ordered C++ standard library directories. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.defines { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L81) | Gets ordered target definitions. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeCompilerInputs.arguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeCompilerInputs.cs#L85) | Gets ordered target parser arguments. | — | — | +| [`public enum Inno.Build.Toolchains.NativeLibraryKind : int`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L11) | Specifies the linkage required by a product's native component operation. | — | — | +| [`Inno.Build.Toolchains.NativeLibraryKind.Static = 0`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L16) | Produces archives for the selected final linker. | — | — | +| [`Inno.Build.Toolchains.NativeLibraryKind.Shared = 1`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L21) | Produces libraries loaded by the deployed product. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeComponentBuildOptions`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L27) | Freezes product-owned linkage, ordered component definitions and their configuration inputs. | — | — | +| [`public Inno.Build.Toolchains.NativeComponentBuildOptions.NativeComponentBuildOptions(Inno.Build.Toolchains.NativeLibraryKind libraryKind, System.Collections.Generic.IEnumerable? cmakeArguments = null, System.Collections.Generic.IEnumerable? inputPaths = null, string? bindingLibraryName = null, System.Collections.Generic.IEnumerable? bindingIncludeDirectories = null, Inno.Build.Toolchains.NativeLinkPlan? linkPlan = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L55) | Captures component configuration without selecting an SDK or starting a build.
`libraryKind`: The explicitly requested linkage; a recipe must support it before execution starts.
`cmakeArguments`: Ordered, unique CMake definitions belonging to this component, excluding SDK and linkage selection.
`inputPaths`: Portable checkout-relative configuration files included in content hashing and stability checks.
`linkPlan`: Component-specific platform link settings, or null for the selected SDK plan.
`bindingLibraryName`: The explicit native import identity, or null to retain the component identity.
`bindingIncludeDirectories`: Additional checkout-relative binding headers owned by an integration. | — | `System.ArgumentException`: Linkage is invalid, definitions are ambiguous or override SDK selection, or an input escapes the checkout. | +| [`public Inno.Build.Toolchains.NativeLibraryKind Inno.Build.Toolchains.NativeComponentBuildOptions.libraryKind { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L91) | Gets the linkage selected by the product rather than by the execution host. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L96) | Gets the immutable ordered component definitions; order contributes to artifact identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L101) | Gets logical configuration input paths whose actual bytes participate in the recipe. | — | — | +| [`public string? Inno.Build.Toolchains.NativeComponentBuildOptions.bindingLibraryName { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L106) | Gets the explicit native import identity, or null for the component default. | — | — | +| [`public Inno.Build.Toolchains.NativeLinkPlan? Inno.Build.Toolchains.NativeComponentBuildOptions.linkPlan { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L111) | Gets component-specific platform link settings, or null for the target defaults. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeComponentBuildOptions.bindingIncludeDirectories { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentBuildOptions.cs#L116) | Gets additional checkout-relative binding header roots. | — | — | +| [`public sealed class Inno.Build.Toolchains.NativeComponentDescriptor`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L10) | Declares the unique source and build owners of a reusable native component. | — | — | +| [`public Inno.Build.Toolchains.NativeComponentDescriptor.NativeComponentDescriptor(string id, string nativeProject, string toolchainProject, Inno.Build.Toolchains.NativeStaticBuildDefinition? staticBuild = null, string? bindingDefinition = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L33) | Freezes portable checkout-relative owner locations without assembly-name or directory inference.
`id`: The portable component artifact identity.
`nativeProject`: The checkout-relative native project file.
`toolchainProject`: The checkout-relative build implementation project file.
`staticBuild`: The backend-owned static recipe, or null when this component has none.
`bindingDefinition`: The portable binding definition, or null when the component generates no bindings. | — | `System.ArgumentException`: An identity or path is empty, absolute, or escapes its checkout. | +| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L59) | Gets the portable artifact identity. | — | — | +| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.nativeProject { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L64) | Gets the explicit checkout-relative Native owner project. | — | — | +| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.toolchainProject { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L69) | Gets the explicit checkout-relative recipe owner project. | — | — | +| [`public Inno.Build.Toolchains.NativeStaticBuildDefinition? Inno.Build.Toolchains.NativeComponentDescriptor.staticBuild { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L74) | Gets the declared static build capability without requiring a platform-specific component project. | — | — | +| [`public string? Inno.Build.Toolchains.NativeComponentDescriptor.bindingDefinition { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L79) | Gets the declared binding definition, or null for a component without generated bindings. | — | — | +| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.GetNativeRoot(string engineRoot)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L90) | Resolves the declared native owner within an explicitly supplied checkout.
`engineRoot`: The absolute operation-owned checkout. | The absolute native project directory, without creating it. | — | +| [`public string Inno.Build.Toolchains.NativeComponentDescriptor.GetToolchainRoot(string engineRoot)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeComponentDescriptor.cs#L101) | Resolves the declared recipe owner within an explicitly supplied checkout.
`engineRoot`: The absolute operation-owned checkout. | The absolute toolchain project directory, without creating it. | — | +| [`public delegate System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Creates explicitly selected deployment metadata from a frozen Native closure.
`products`: Borrowed complete products whose leases remain held through metadata preparation.
`files`: Exact relative deployment paths mapped to verified source files. | Relative metadata paths mapped to immutable bytes; collisions with payloads are rejected. | — | +| [`public Inno.Build.Toolchains.NativeDeploymentMetadataFactory.NativeDeploymentMetadataFactory(object @object, nint method)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public virtual System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory.Invoke(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Creates explicitly selected deployment metadata from a frozen Native closure.
`products`: Borrowed complete products whose leases remain held through metadata preparation.
`files`: Exact relative deployment paths mapped to verified source files. | Relative metadata paths mapped to immutable bytes; collisions with payloads are rejected. | — | +| [`public virtual System.IAsyncResult Inno.Build.Toolchains.NativeDeploymentMetadataFactory.BeginInvoke(System.Collections.Generic.IReadOnlyList products, System.Collections.Generic.IReadOnlyDictionary files, System.AsyncCallback callback, object @object)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public virtual System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeDeploymentMetadataFactory.EndInvoke(System.IAsyncResult result)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeDeploymentMetadataFactory.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public static class Inno.Build.Toolchains.NativeInputMaterializer`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeInputMaterializer.cs#L13) | Materializes frozen native inputs without rewriting identical intermediate files. | — | — | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.NativeInputMaterializer.CopyAsync(Inno.Build.Toolchains.NativeBuildContext context, string source, string destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeInputMaterializer.cs#L39) | Copies a declared input atomically when its destination differs, preserving identical file timestamps.
`context`: The operation whose initial source bytes and hashing statistics are shared across components.
`source`: The absolute input file already declared by the component recipe.
`destination`: The absolute intermediate file owned exclusively by the component producer.
`cancellationToken`: Cancels copying before publication and preserves the prior intermediate file. | True when bytes were copied and published; false when the existing content was identical. | `System.InvalidOperationException`: The source changes relative to the operation's frozen input snapshot.
`System.IO.IOException`: The source, destination or atomic candidate cannot be read or published. | +| [`public sealed class Inno.Build.Toolchains.NativeLinkPlan`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L10) | Freezes platform library formats and linker configuration independently of component recipes. | — | — | +| [`public Inno.Build.Toolchains.NativeLinkPlan.NativeLinkPlan(string? sharedLibraryExtension = null, string? importLibraryExtension = null, string? staticLibraryExtension = null, System.Collections.Generic.IEnumerable? cmakeArguments = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L30) | Captures explicitly supported library formats and ordered linker configuration.
`sharedLibraryExtension`: The shared-library suffix, or null when shared linkage is unsupported.
`importLibraryExtension`: The separate import-library suffix, or null when the shared library is the link input.
`staticLibraryExtension`: The archive suffix, or null when static linkage is not contributed.
`cmakeArguments`: Ordered platform-owned CMake linker definitions. | — | `System.ArgumentException`: No format is supported, an import format lacks shared linkage, a suffix is invalid, or an argument is null. | +| [`public string? Inno.Build.Toolchains.NativeLinkPlan.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L52) | Gets the shared-library suffix, or null when shared linkage is unsupported. | — | — | +| [`public string? Inno.Build.Toolchains.NativeLinkPlan.importLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L56) | Gets the separate import suffix, or null when no separate import library is used. | — | — | +| [`public string? Inno.Build.Toolchains.NativeLinkPlan.staticLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L60) | Gets the static archive suffix, or null when static linkage is unavailable. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeLinkPlan.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L64) | Gets ordered platform linker definitions. | — | — | +| [`public string Inno.Build.Toolchains.NativeLinkPlan.GetStaticFileName(string logicalName)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L78) | Formats an explicitly declared logical archive using the selected platform format.
`logicalName`: The component-owned logical archive name. | The exact platform file name. | `System.NotSupportedException`: Static linkage has no registered archive format. | +| [`public string Inno.Build.Toolchains.NativeLinkPlan.GetFileExtension(Inno.Build.Toolchains.NativeLibraryKind kind)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeLinkPlan.cs#L95) | Resolves an explicitly supported linkage format before staging or generation begins.
`kind`: The component's requested linkage. | The selected file extension; unsupported linkage fails immediately. | `System.NotSupportedException`: The platform does not contribute this linkage format.
`System.ArgumentOutOfRangeException`: The linkage kind is not defined. | +| [`public sealed class Inno.Build.Toolchains.NativeStaticBuildDefinition`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L10) | Declares a backend-owned static-library recipe and its complete checkout input closure. | — | — | +| [`public Inno.Build.Toolchains.NativeStaticBuildDefinition.NativeStaticBuildDefinition(string cmakeFile, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable logicalOutputs, string? nativeSourceDirectory = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L33) | Freezes the component's CMake entry point, source dependencies and exact installed archive names.
`cmakeFile`: The portable checkout-relative component definition.
`inputPaths`: The portable checkout-relative source and build dependencies.
`logicalOutputs`: The exact archive file names required after installation.
`nativeSourceDirectory`: The declared checkout-relative root used by the component's native CMake fragment, or null when no such fragment is consumed. | — | `System.ArgumentException`: A path escapes its root or an archive name is invalid or duplicated.
`System.ArgumentNullException`: A required collection is null. | +| [`public string Inno.Build.Toolchains.NativeStaticBuildDefinition.cmakeFile { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L58) | Gets the component-owned CMake definition, which participates in artifact identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeStaticBuildDefinition.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L63) | Gets the immutable complete source dependency paths relative to the checkout. | — | — | +| [`public string? Inno.Build.Toolchains.NativeStaticBuildDefinition.nativeSourceDirectory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L68) | Gets the declared native fragment's source root, or null when the recipe only consumes its other inputs. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeStaticBuildDefinition.logicalOutputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeStaticBuildDefinition.cs#L73) | Gets the exact required output closure within this component's target directory. | — | — | +| [`public static class Inno.Build.Toolchains.NativeToolchainPreparation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L11) | Supplies shared environment isolation and SDK input validation without selecting a platform. | — | — | +| [`public static System.Collections.Generic.Dictionary Inno.Build.Toolchains.NativeToolchainPreparation.CreateEnvironment()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L26) | Creates a child-process environment that clears undeclared ambient compiler policy. | A caller-owned dictionary to populate with explicitly selected SDK variables. | — | +| [`public static Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeToolchainPreparation.Freeze(string targetId, Inno.Build.Toolchains.BuildHostDescriptor host, System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IReadOnlyDictionary environment, System.Collections.Generic.IEnumerable inputs, System.Collections.Generic.IEnumerable cmakeArguments, Inno.Build.Toolchains.NativeLinkPlan linkPlan, bool multiConfiguration, Inno.Build.Toolchains.NativeCompilerInputs? compilerInputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainPreparation.cs#L65) | Validates and freezes a provider's complete tool selection before product staging.
`targetId`: The explicit requested target.
`host`: The declared tool execution host.
`tools`: Selected executable paths.
`environment`: Isolated child environment.
`inputs`: Selected compiler and SDK inputs.
`cmakeArguments`: Ordered target configuration arguments.
`multiConfiguration`: Whether configuration is chosen during build.
`linkPlan`: The sole source of target library formats and linker capabilities.
`compilerInputs`: The selected SDK parser inputs, or null for a selection without parsing capability. | An immutable selection with validated physical tool and SDK inputs. | `System.IO.FileNotFoundException`: A selected SDK input or executable is absent. | +| [`public sealed class Inno.Build.Toolchains.NativeToolchainSelection`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L12) | Freezes target compiler tools and SDK inputs independently of discovery and platform policy. | — | — | +| [`public Inno.Build.Toolchains.NativeToolchainSelection.NativeToolchainSelection(string targetId, Inno.Build.Toolchains.BuildHostDescriptor host, System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IReadOnlyDictionary environment, System.Collections.Generic.IEnumerable inputPaths, System.Collections.Generic.IEnumerable cmakeArguments, Inno.Build.Toolchains.NativeLinkPlan linkPlan, bool multiConfiguration, Inno.Build.Toolchains.NativeCompilerInputs? compilerInputs = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L55) | Captures an immutable, explicit compiler and SDK selection supplied by a platform provider.
`targetId`: The exact native target identity.
`host`: The machine on which the selected tools execute.
`tools`: Absolute executable paths indexed by explicit command names.
`environment`: Frozen SDK values applied to owned tools and in-process binding configuration without changing process state.
`inputPaths`: Compiler, SDK and tool files participating in product identity.
`cmakeArguments`: Ordered configuration arguments pinning compiler, generator and SDK.
`multiConfiguration`: Whether configuration is selected during build rather than configure.
`linkPlan`: The sole source of target library formats and linker capabilities.
`compilerInputs`: Explicit SDK parser inputs, or null when no native parser is provided. | — | `System.ArgumentException`: The selection is incomplete or has unassigned target facts.
`System.ArgumentNullException`: A required descriptor or collection is null.
`System.IO.FileNotFoundException`: A selected executable or SDK input is unavailable before preparation begins. | +| [`public string Inno.Build.Toolchains.NativeToolchainSelection.targetId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L115) | Gets the target identity, without inferring it from the execution host. | — | — | +| [`public Inno.Build.Toolchains.BuildHostDescriptor Inno.Build.Toolchains.NativeToolchainSelection.host { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L120) | Gets the declared tool execution host. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.inputPaths { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L125) | Gets the immutable SDK and compiler input closure. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.declarations { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L130) | Gets ordered tool and environment facts included in the recipe fingerprint. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.NativeToolchainSelection.cmakeArguments { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L135) | Gets configuration arguments selecting the compiler, generator and SDK. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.NativeToolchainSelection.environment { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L140) | Gets frozen SDK values supplied to owned tools and in-process binding configuration. | — | — | +| [`public string Inno.Build.Toolchains.NativeToolchainSelection.sharedLibraryExtension { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L148) | Gets the target's shared-library suffix. | — | `System.NotSupportedException`: The target does not support shared libraries. | +| [`public bool Inno.Build.Toolchains.NativeToolchainSelection.multiConfiguration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L153) | Gets whether the selected generator supports build-time configuration selection. | — | — | +| [`public Inno.Build.Toolchains.NativeCompilerInputs Inno.Build.Toolchains.NativeToolchainSelection.compilerInputs { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L158) | Gets the target provider's explicit parser inputs. | — | — | +| [`public Inno.Build.Toolchains.NativeLinkPlan Inno.Build.Toolchains.NativeToolchainSelection.linkPlan { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L163) | Gets the platform-owned linker capabilities. | — | — | +| [`public Inno.Build.Toolchains.NativeToolchainSelection Inno.Build.Toolchains.NativeToolchainSelection.WithTools(System.Collections.Generic.IReadOnlyDictionary tools, System.Collections.Generic.IEnumerable inputs)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L183) | Adds integration-owned tools without changing the platform SDK or replacing existing commands.
`tools`: Explicit absolute executables required by the selected backend integration.
`inputs`: Integration implementation and configuration files participating in tool selection. | A new immutable selection containing both the platform and integration inputs. | `System.ArgumentException`: A contribution conflicts with an already selected command.
`System.IO.FileNotFoundException`: A contributed tool or input does not exist. | +| [`public string Inno.Build.Toolchains.NativeToolchainSelection.ResolveExecutable(string name)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L212) | Resolves an executable from the frozen selection rather than a mutable process PATH.
`name`: The explicit command registered by the platform provider. | The selected executable; an absent command fails immediately. | `System.InvalidOperationException`: The provider did not register the requested command. | +| [`public bool Inno.Build.Toolchains.NativeToolchainSelection.TryResolveExecutable(string name, out string executable)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeToolchainSelection.cs#L231) | Checks an optional tool capability without discovering a command from the process environment.
`name`: The declared tool identity to check.
`executable`: The frozen absolute path when present, or an empty string when absent. | True only when this selection explicitly supplies the requested tool. | `System.ArgumentException`: The tool identity is empty. | +| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildPlan`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L14) | Executes a frozen product closure with one target toolchain and no implicit backend selection. | — | — | +| [`public Inno.Build.Toolchains.ProductNativeBuildPlan.ProductNativeBuildPlan(string productId, System.Collections.Generic.IReadOnlyList steps, Inno.Build.Toolchains.NativeDeploymentMetadataFactory? deploymentMetadata = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L36) | Validates the complete ordered component graph before tools or staging can start.
`productId`: The product identity whose native closure is selected.
`steps`: Unique, dependency-ordered component operations.
`deploymentMetadata`: Optional product-owned metadata preparation, without backend discovery. | — | `System.ArgumentException`: The product, component identity or dependency ordering is invalid.
`System.ArgumentNullException`: The step collection is null. | +| [`public string Inno.Build.Toolchains.ProductNativeBuildPlan.productId { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L60) | Gets the explicit product identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildPlan.steps { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L65) | Gets the frozen dependency-ordered component closure. | — | — | +| [`public Inno.Build.Toolchains.NativeDeploymentMetadataFactory? Inno.Build.Toolchains.ProductNativeBuildPlan.deploymentMetadata { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L70) | Gets the product's explicit metadata preparation, or null when no metadata is required. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildPlan.CreateRecipes(Inno.Build.Toolchains.NativeBuildContext context)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L91) | Resolves the complete selected source recipe closure before binding generation or native compilation.
`context`: The borrowed operation with a frozen target SDK; each step supplies its own component options. | Immutable recipes in declaration order, including each component's selected tools and parameters. Actual generated bytes and dependency artifacts are added by the producer before publication. | `System.ArgumentNullException`: The context is null.
`System.InvalidOperationException`: Tools are unassigned or a declaration returns a foreign owner, component or target.
`System.NotSupportedException`: A static component does not support the selected target. | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Build.Toolchains.ProductNativeBuildPlan.CreateDeploymentFiles(System.Collections.Generic.IReadOnlyList products)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildPlan.cs#L133) | Resolves exact deployment paths from the declared layout rather than component-name conventions.
`products`: The complete result of this plan, in declaration order. | Owned immutable relative-output to source-file mappings for atomic deployment. | `System.ArgumentException`: Products differ from the plan or deployment paths collide or escape their root. | +| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildResult : System.Collections.Generic.IReadOnlyList, System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L12) | Owns a complete ordered Native product closure and releases its reading leases together. | — | — | +| [`public Inno.Build.Toolchains.ProductNativeBuildResult.ProductNativeBuildResult(System.Collections.Generic.IEnumerable products, Inno.Build.Toolchains.NativeBindingGenerationResult bindings, string? aggregateDirectory = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L30) | Takes ownership of a complete native and binding closure.
`products`: Owned products in dependency order.
`bindings`: The owned matching binding batch.
`aggregateDirectory`: A complete aggregate install directory, or null for individual products. | — | — | +| [`public int Inno.Build.Toolchains.ProductNativeBuildResult.Count { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L43) | Gets the number of products in the completed build plan. | — | — | +| [`public string? Inno.Build.Toolchains.ProductNativeBuildResult.aggregateDirectory { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L48) | Gets the complete aggregate install directory, or null for individually published products. | — | — | +| [`public Inno.Build.Toolchains.NativeBuildProduct Inno.Build.Toolchains.ProductNativeBuildResult.this[int index] { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L63) | Borrows a product whose lease is owned by this result until disposal.
`index`: The zero-based position in the plan's declaration order. | The borrowed product; the caller must finish access before disposing the result. | `System.ObjectDisposedException`: The result no longer owns the products. | +| [`public System.Collections.Generic.IEnumerator Inno.Build.Toolchains.ProductNativeBuildResult.GetEnumerator()`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L81) | Enumerates borrowed products in plan order without transferring their ownership. | An enumerator which must finish before this result is disposed. | `System.ObjectDisposedException`: The result has already released its leases. | +| [`public void Inno.Build.Toolchains.ProductNativeBuildResult.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildResult.cs#L94) | Releases products in reverse dependency order after all consumers have finished. Repeated disposal has no effect. | — | `System.AggregateException`: One or more reading owners failed to release after every owner was attempted. | +| [`public sealed class Inno.Build.Toolchains.ProductNativeBuildStep`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L12) | Binds one product component to an owned build operation and explicit deployment layout. | — | — | +| [`public Inno.Build.Toolchains.ProductNativeBuildStep.ProductNativeBuildStep(string id, Inno.Build.Toolchains.NativeComponentDescriptor component, Inno.Build.Toolchains.NativeComponentBuildOptions options, System.Collections.Generic.IReadOnlyList dependencies, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? build, System.Func? createRecipe, System.Func deploymentPath, string? configuration = null, System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? bindDependencies = null)`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L52) | Freezes a component step without embedding any backend list in the shared executor.
`id`: The expected published component identity.
`component`: The unique source and recipe owners.
`options`: Explicit product linkage, ordered component parameters and their input closure.
`dependencies`: Earlier component identities required by this operation.
`build`: The awaited standalone producer, or null when the component is executed by a static aggregate.
`createRecipe`: Declares the standalone producer's source inputs without creating generated outputs, or null for a component whose complete source definition is owned by a static aggregate.
`deploymentPath`: Maps a relative output and target to a deployment path, or null for build-only inputs.
`configuration`: An explicit component configuration, or null to use the product configuration.
`bindDependencies`: Optional backend-owned transformation adding the exact published dependency inputs to its source recipe. It must not generate outputs or execute tools. | — | `System.ArgumentException`: The identity or dependency set is invalid.
`System.ArgumentNullException`: A required owner, producer or path policy is null. | +| [`public string Inno.Build.Toolchains.ProductNativeBuildStep.id { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L92) | Gets the exact expected component identity. | — | — | +| [`public Inno.Build.Toolchains.NativeComponentDescriptor Inno.Build.Toolchains.ProductNativeBuildStep.component { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L97) | Gets the explicit source and recipe owners. | — | — | +| [`public Inno.Build.Toolchains.NativeComponentBuildOptions Inno.Build.Toolchains.ProductNativeBuildStep.options { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L102) | Gets product-owned configuration independently of the selected SDK. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.Toolchains.ProductNativeBuildStep.dependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L107) | Gets dependencies that must precede this step. | — | — | +| [`public System.Func, System.Threading.CancellationToken, System.Threading.Tasks.Task>? Inno.Build.Toolchains.ProductNativeBuildStep.build { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L112) | Gets the standalone producer, or null for a component owned by a static aggregate executor. | — | — | +| [`public System.Func? Inno.Build.Toolchains.ProductNativeBuildStep.createRecipe { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L118) | Gets the producer's source declaration, or null when a static aggregate owns the component recipe. The declaration must not generate bindings, run a compiler or create staging. | — | — | +| [`public System.Func Inno.Build.Toolchains.ProductNativeBuildStep.deploymentPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L123) | Gets the explicit output layout policy; null results exclude build-only files. | — | — | +| [`public string? Inno.Build.Toolchains.ProductNativeBuildStep.configuration { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L128) | Gets the step's explicit configuration, or null to borrow the containing product configuration. | — | — | +| [`public System.Func, Inno.Build.Toolchains.NativeBuildRecipe>? Inno.Build.Toolchains.ProductNativeBuildStep.bindDependencies { get; }`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeBuildStep.cs#L134) | Gets the backend-owned dependency transformation, or null for an independent source recipe. The supplied products are borrowed only during recipe construction. | — | — | +| [`public static class Inno.Build.Toolchains.ProductNativeDeployment`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L15) | Materializes an explicitly built host-native closure into one application's deployment directory. | — | — | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ProductNativeDeployment.InstallAsync(System.Collections.Generic.IReadOnlyList products, Inno.Build.Toolchains.ProductNativeBuildPlan plan, string applicationDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L46) | Validates the application's native tree and atomically replaces it when its exact contents differ.
`products`: Validated runtime and optional authoring products returned by the declared product plan. All products must use the same target; each component can appear once.
`cancellationToken`: Cancels ownership acquisition or staging before the atomic installation starts.
`plan`: The explicit component closure and deployment mapping used to produce these products.
`applicationDirectory`: The managed output directory receiving the complete native tree. | Completion after the complete native tree has been validated under exclusive publication ownership. Identical deployments retain their files, including libraries loaded by a running application. | `System.ArgumentException`: The product closure is empty, contains duplicate components or mixes targets.
`System.IO.IOException`: A product cannot be validated, copied or installed; the prior native tree remains available.
`System.OperationCanceledException`: The operation was canceled before installation; the previous native tree is unchanged. | +| [`public static System.Threading.Tasks.ValueTask Inno.Build.Toolchains.ProductNativeDeployment.StageAsync(System.Collections.Generic.IReadOnlyList products, Inno.Build.Toolchains.ProductNativeBuildPlan plan, string applicationDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/ProductNativeDeployment.cs#L87) | Writes a new native tree into an unpublished candidate exclusively owned by the caller.
`products`: The pinned, validated products to copy into the candidate.
`plan`: The explicit component layout and neutral deployment metadata.
`applicationDirectory`: An existing caller-owned candidate with no native subtree.
`cancellationToken`: Cancels copying or final input verification before the caller publishes. | Completion after copying and verifying payload, without creating transaction metadata in the candidate. | `System.ArgumentException`: The product closure or candidate path is invalid.
`System.IO.DirectoryNotFoundException`: The caller has not created its owned candidate.
`System.IO.IOException`: The native tree already exists, or an input cannot be verified or copied.
`System.OperationCanceledException`: Cancellation prevents completing the unpublished candidate. | +| [`public static class Inno.Build.Toolchains.ToolchainEnvironment`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L15) | Provides deterministic host-process and workspace operations shared by native dependency toolchains. | — | — | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary environment)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L50) | Executes a declared absolute tool with an explicitly resolved SDK environment and records the operation cost.
`context`: The operation owning execution statistics and frozen recipe inputs.
`fileName`: The absolute executable declared by the recipe; no host-native SDK is inferred.
`arguments`: Individual arguments passed without shell interpretation.
`workingDirectory`: The owned source or intermediate directory.
`cancellationToken`: Cancels execution and drains the complete process tree.
`environment`: The frozen environment supplied by the selected SDK resolver. | Completion after successful exit and output delivery. | `System.ArgumentException`: The executable is not an absolute declared tool path.
`System.InvalidOperationException`: The process fails to start or exits unsuccessfully.
`System.OperationCanceledException`: Execution was canceled. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L93) | Runs a host-native process with the context's frozen compiler and SDK selection.
`context`: A context resolved by NativeToolchainSelection before artifact fingerprinting.
`fileName`: A selected build command or an absolute executable declared as a component input.
`arguments`: Individual arguments passed without shell interpretation.
`workingDirectory`: The source snapshot or intermediate directory owned by the operation.
`cancellationToken`: Cancels and retires the complete process tree. | Completion after successful exit and complete output delivery. | `System.InvalidOperationException`: Host tools were not resolved, a command is unavailable or a process fails.
`System.OperationCanceledException`: The operation was canceled. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(Inno.Build.Toolchains.NativeBuildContext context, string fileName, string arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L134) | Runs a quoted host command with a frozen compiler and SDK environment.
`context`: The context resolved before artifact fingerprinting.
`fileName`: A selected command or an absolute executable declared in the component inputs.
`arguments`: Arguments quoted for the selected executable's command-line parser.
`workingDirectory`: The operation's source snapshot or intermediate directory.
`cancellationToken`: Cancels and retires the complete process tree. | Completion after the command exits successfully and its streams drain. | `System.InvalidOperationException`: Tools are unresolved or execution fails.
`System.OperationCanceledException`: The operation was canceled. | +| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.ResolveExecutable(string name)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L164) | Resolves one explicitly selected executable before it becomes a build input.
`name`: An executable path or a command name available on the current process PATH. | The absolute executable path used for process execution and artifact identity. | `System.ArgumentException`: The name is empty.
`System.IO.FileNotFoundException`: The executable is unavailable at the supplied path or on PATH. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L216) | Runs a child build with structured arguments, cancellation and hidden windows.
`fileName`: The executable to launch.
`arguments`: The individual arguments, passed without shell interpretation.
`workingDirectory`: The repository or staging directory owned by the operation.
`cancellationToken`: Cancels the child process and its complete process tree.
`environment`: Optional process-local toolchain variables; the parent environment is unchanged. Null values remove inherited variables, while empty values remain explicitly empty. | Completion after both output streams have drained and the process succeeds. | `System.InvalidOperationException`: The process fails to start or exits unsuccessfully.
`System.OperationCanceledException`: The operation was canceled.
`System.IO.IOException`: Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment, System.IO.TextWriter standardOutput, System.IO.TextWriter standardError)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L261) | Runs a child build with caller-owned output destinations and the shared process retirement protocol.
`fileName`: The executable selected by the build composition.
`arguments`: Structured arguments passed without shell interpretation.
`workingDirectory`: The project directory controlling SDK and workload resolution.
`cancellationToken`: Cancels the complete process tree and drains both output streams.
`environment`: Optional child-only toolchain variables. Null values remove inherited variables; empty values remain explicitly empty.
`standardOutput`: The caller-owned standard output destination; the runner does not dispose it.
`standardError`: The caller-owned standard error destination; the runner does not dispose it. | Completion after successful exit and complete output delivery; failures propagate. | `System.InvalidOperationException`: The executable cannot start or exits unsuccessfully.
`System.IO.IOException`: An output destination fails; the owned process tree is terminated before failure returns.
`System.OperationCanceledException`: The operation was canceled after retiring its owned process tree. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.CaptureOutputAsync(string fileName, System.Collections.Generic.IReadOnlyList arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken, System.Collections.Generic.IReadOnlyDictionary? environment = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L309) | Captures a tool's standard output while forwarding errors and preserving the common process lifecycle.
`fileName`: The executable to launch.
`arguments`: Structured arguments passed without shell interpretation.
`workingDirectory`: The directory used for project and SDK resolution.
`cancellationToken`: Cancels the child process tree and waits for its complete exit.
`environment`: Optional child-only environment variables. Null values remove inherited variables; empty values remain explicitly empty. | The complete standard output after successful exit; failures and cancellation propagate. | `System.InvalidOperationException`: The tool fails to start or exits unsuccessfully.
`System.OperationCanceledException`: The operation was canceled.
`System.IO.IOException`: Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | +| [`public static void Inno.Build.Toolchains.ToolchainEnvironment.ValidateConfiguration(string configuration)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L330) | Validates the configuration shared by all native component builds.
`configuration`: The debug or release token supplied by the build workflow. | — | `System.ArgumentException`: The configuration is unsupported. | +| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.FindRepoRoot()`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L345) | Resolves the repository containing the currently executing toolchain assembly. | The absolute path of the repository root. | `System.InvalidOperationException`: Thrown when no repository marker exists above the toolchain assembly directory. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.RunAsync(string fileName, string arguments, string workingDirectory, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L390) | Runs a child build using a complete argument string, without a shell.
`fileName`: The executable resolved by the host operating system.
`arguments`: Arguments quoted for the selected executable's command-line parser.
`workingDirectory`: The directory owned by the operation.
`cancellationToken`: Cancels the complete process tree and waits for output to finish draining. | Completion after successful exit; failures and cancellation propagate. | `System.InvalidOperationException`: The process fails to start or exits unsuccessfully.
`System.OperationCanceledException`: The operation was canceled.
`System.IO.IOException`: Redirected output cannot be read or forwarded; the process tree is terminated before failure returns. | +| [`public static bool Inno.Build.Toolchains.ToolchainEnvironment.ContainsAny(string value, params string[] needles)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L417) | Determines whether a value contains at least one token without regard to casing.
`value`: The value searched for candidate tokens.
`needles`: The candidate tokens tested against the value. | when at least one token occurs in the value; otherwise . | — | +| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.NormalizeOutputName(string fileName, string config)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L444) | Normalizes a native artifact name and appends its build configuration.
`fileName`: The source artifact file name.
`config`: The normalized configuration appended to the artifact stem. | The deterministic output file name. | — | +| [`public static string Inno.Build.Toolchains.ToolchainEnvironment.TrimConfigSuffix(string baseName)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L463) | Removes a trailing native debug or release token from an artifact stem.
`baseName`: The artifact stem to normalize. | The normalized artifact stem without a configuration suffix. | — | +| [`public static void Inno.Build.Toolchains.ToolchainEnvironment.DeleteDirectory(string path)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L486) | Deletes one explicitly resolved toolchain output directory when it exists.
`path`: The exact directory selected by a toolchain command. | — | `System.ArgumentException`: Thrown when is empty or does not resolve to an absolute path. | +| [`public static System.Threading.Tasks.Task Inno.Build.Toolchains.ToolchainEnvironment.CaptureOutputAsync(System.Diagnostics.ProcessStartInfo start, System.Threading.CancellationToken cancellationToken)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainEnvironment.cs#L512) | Captures platform tool discovery through the same cancellable process lifecycle as builds.
`start`: The prepared discovery command, including any platform-specific argument quoting.
`cancellationToken`: Cancels discovery and waits for its process tree to exit. | Complete standard output after successful discovery; errors and cancellation propagate. | — | +| [`public static class Inno.Build.Toolchains.ToolchainLayout`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L6) | Defines stable repository-relative names shared by native dependency toolchains. | — | — | +| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_REPOSITORY_MARKER_FILE = "InnoEngine.sln"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L11) | Identifies the repository marker used while resolving a toolchain workspace. | — | — | +| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_EXTERNAL_DIRECTORY_NAME = "extern"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L16) | Identifies the directory containing checked-out native dependency sources. | — | — | +| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_DEBUG_CONFIGURATION = "debug"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L21) | Identifies the normalized debug configuration token. | — | — | +| [`public const string Inno.Build.Toolchains.ToolchainLayout.C_RELEASE_CONFIGURATION = "release"`](../../build/toolchains/Inno.Build.Toolchains/ToolchainLayout.cs#L26) | Identifies the normalized release configuration token. | — | — | +| [`public sealed class Inno.Build.Toolchains.ToolchainWorkingDirectory : System.IDisposable`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L10) | Retains a provider-owned tool execution path until all dependent processes finish. | — | — | +| [`public Inno.Build.Toolchains.ToolchainWorkingDirectory.ToolchainWorkingDirectory(string toolPath, System.IDisposable? ownership = null)`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L26) | Captures an absolute execution path and its optional release protocol.
`toolPath`: The execution directory selected by the host provider.
`ownership`: The alias and lease owner, or null for direct physical execution. | — | `System.ArgumentException`: The execution path is not absolute. | +| [`public string Inno.Build.Toolchains.ToolchainWorkingDirectory.toolPath { get; }`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L39) | Gets the execution path, valid until disposal. | — | — | +| [`public void Inno.Build.Toolchains.ToolchainWorkingDirectory.Dispose()`](../../build/toolchains/Inno.Build.Toolchains/ToolchainWorkingDirectory.cs#L44) | Releases provider resources after all dependent processes have stopped. | — | — | -### 输入路径别名与创作归属 - -同一校验阶段按最终物理路径去重哈希,但保留每次读取的原路径和 checkout 相对逻辑身份。macOS 的 `/var` 别名、目录链接与 Windows junction 不会把所属源码误记成外部 SDK 输入;固定编译副本仍按所属源码布局准备。独立锁等待、编译及提交后重新校验,不跨阶段复用内容结论。 - -## 原生生成编译契约 API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| 当前签名 | 语义 | -| --- | --- | -| [`public sealed class Inno.Build.Toolchains.NativeBindingCompilation`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | Describes a selected component's native requirements independently of its source generator. | -| [`public required Inno.Build.Toolchains.NativeBindingSourceRoot[] Inno.Build.Toolchains.NativeBindingCompilation.roots { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L27) | Gets root bindings whose identities are local to this component. | -| [`public required Inno.Build.Toolchains.NativeBindingSourceFile[] Inno.Build.Toolchains.NativeBindingCompilation.files { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L32) | Gets all exact input files; only files marked compile are translation units. | -| [`public required Inno.Build.Toolchains.NativeBindingIncludeDirectory[] Inno.Build.Toolchains.NativeBindingCompilation.includeDirectories { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L37) | Gets include requirements in their original search order. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.defines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L42) | Gets ordered definitions for manifest translation units; upstream target configuration remains component-owned. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.compilerDefines { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L47) | Gets parser target macro facts; these must not override the consuming compiler's builtins. | -| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cxxStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L52) | Gets the minimum C++ standard, or zero when no C++ translation unit is required. | -| [`public required int Inno.Build.Toolchains.NativeBindingCompilation.cStandard { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L57) | Gets the minimum C standard, or zero when no C translation unit is required. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.requiredExports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L62) | Gets the exact native symbols that the component must provide. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportSymbols { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L67) | Gets static table entry points, or an empty array for dynamic-only bindings. | -| [`public required string Inno.Build.Toolchains.NativeBindingCompilation.staticExportAbi { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L72) | Gets the static table ABI digest, or an empty value when no static table exists. | -| [`public required string[] Inno.Build.Toolchains.NativeBindingCompilation.staticExportNames { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L77) | Gets static table entries in address-resolution order. | -| [`public static Inno.Build.Toolchains.NativeBindingCompilation Inno.Build.Toolchains.NativeBindingCompilation.Load(string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L97) | Reads the current engine-owned compilation description from a selected native output. | -| [`public string Inno.Build.Toolchains.NativeBindingCompilation.ResolveRoot(string engineRoot, string nativeDirectory, string rootId)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L124) | Resolves a component-local root using the current checkout and selected output. | -| [`public void Inno.Build.Toolchains.NativeBindingCompilation.VerifyInputs(System.Func resolveRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L152) | Verifies the exact bytes at original or remapped roots before their use. | -| [`public System.Collections.Generic.IEnumerable Inno.Build.Toolchains.NativeBindingCompilation.GetInputs(string engineRoot, string nativeDirectory)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L179) | Enumerates exact file declarations for standalone or aggregate recipe fingerprinting. | -| [`public Inno.Build.Toolchains.NativeBindingCompilation.NativeBindingCompilation()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingCompilation.cs#L16) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public sealed record Inno.Build.Toolchains.NativeBindingIncludeDirectory : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement. | -| [`public Inno.Build.Toolchains.NativeBindingIncludeDirectory.NativeBindingIncludeDirectory(string rootId, string relativePath, bool system, bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Preserves the order and search semantics of one native include requirement. | -| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L19) | The component-local input root. | -| [`public string Inno.Build.Toolchains.NativeBindingIncludeDirectory.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L20) | The root-relative directory, or empty for the root itself. | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.system { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L21) | Whether this directory contains system headers. | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.framework { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L22) | Whether the compiler must use framework search semantics. | -| [`public override string Inno.Build.Toolchains.NativeBindingIncludeDirectory.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Returns a string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator !=(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.operator ==(Inno.Build.Toolchains.NativeBindingIncludeDirectory? left, Inno.Build.Toolchains.NativeBindingIncludeDirectory? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public override int Inno.Build.Toolchains.NativeBindingIncludeDirectory.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Serves as the default hash function. | -| [`public override bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Determines whether the specified object is equal to the current object. | -| [`public bool Inno.Build.Toolchains.NativeBindingIncludeDirectory.Equals(Inno.Build.Toolchains.NativeBindingIncludeDirectory? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | Indicates whether the current object is equal to another object of the same type. | -| [`public void Inno.Build.Toolchains.NativeBindingIncludeDirectory.Deconstruct(out string rootId, out string relativePath, out bool system, out bool framework)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingIncludeDirectory.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceFile : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation. | -| [`public Inno.Build.Toolchains.NativeBindingSourceFile.NativeBindingSourceFile(string rootId, string relativePath, long length, string sha256, bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Identifies exact generated or external bytes needed by a native compilation. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.rootId { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L22) | The component-local input root. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.relativePath { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L23) | The portable root-relative filename. | -| [`public long Inno.Build.Toolchains.NativeBindingSourceFile.length { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L24) | The expected byte length. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceFile.sha256 { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L25) | The lowercase SHA-256 content identity. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.compile { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L26) | Whether this file is a translation unit rather than an include dependency. | -| [`public override string Inno.Build.Toolchains.NativeBindingSourceFile.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Returns a string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator !=(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceFile.operator ==(Inno.Build.Toolchains.NativeBindingSourceFile? left, Inno.Build.Toolchains.NativeBindingSourceFile? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public override int Inno.Build.Toolchains.NativeBindingSourceFile.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Serves as the default hash function. | -| [`public override bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Determines whether the specified object is equal to the current object. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceFile.Equals(Inno.Build.Toolchains.NativeBindingSourceFile? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | Indicates whether the current object is equal to another object of the same type. | -| [`public void Inno.Build.Toolchains.NativeBindingSourceFile.Deconstruct(out string rootId, out string relativePath, out long length, out string sha256, out bool compile)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceFile.cs#L21) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public sealed record Inno.Build.Toolchains.NativeBindingSourceRoot : System.IEquatable`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract. | -| [`public Inno.Build.Toolchains.NativeBindingSourceRoot.NativeBindingSourceRoot(string id, string directory, bool generated, bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Identifies one component-local root without exposing a generator-specific contract. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.id { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L19) | An ordinal identity scoped to this component's selected generation. | -| [`public string Inno.Build.Toolchains.NativeBindingSourceRoot.directory { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L20) | The checkout-relative or absolute external location; empty for generated output. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.generated { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L21) | Whether this root denotes the selected native output directory. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.frozen { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L22) | Whether source files must be copied before compilation. | -| [`public override string Inno.Build.Toolchains.NativeBindingSourceRoot.ToString()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Returns a string that represents the current object. | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator !=(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public static bool Inno.Build.Toolchains.NativeBindingSourceRoot.operator ==(Inno.Build.Toolchains.NativeBindingSourceRoot? left, Inno.Build.Toolchains.NativeBindingSourceRoot? right)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | -| [`public override int Inno.Build.Toolchains.NativeBindingSourceRoot.GetHashCode()`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Serves as the default hash function. | -| [`public override bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(object? obj)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Determines whether the specified object is equal to the current object. | -| [`public bool Inno.Build.Toolchains.NativeBindingSourceRoot.Equals(Inno.Build.Toolchains.NativeBindingSourceRoot? other)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | Indicates whether the current object is equal to another object of the same type. | -| [`public void Inno.Build.Toolchains.NativeBindingSourceRoot.Deconstruct(out string id, out string directory, out bool generated, out bool frozen)`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingSourceRoot.cs#L18) | 该 record 的值比较、复制或格式化成员;不持有生成结果的 lease。 | - - +## 项目依赖 -| 声明 | 行为与约束 | +| 工程 | 边界 | | --- | --- | -| [`public required bool Inno.Build.Toolchains.NativeBindingGenerationDescriptor.usesRuntimeImports { get; init; }`](../../build/toolchains/Inno.Build.Toolchains/Native/NativeBindingGenerationDescriptor.cs#L99) | Gets whether the generated context uses runtime-resolved imports instead of a module provider. This initialization choice is independent of static or shared native linkage. | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Core.IO` | 公开签名依赖 | diff --git a/docs/build/Inno.Build.md b/docs/build/Inno.Build.md index 0c3e59a1c..d7069ab9b 100644 --- a/docs/build/Inno.Build.md +++ b/docs/build/Inno.Build.md @@ -1,5 +1,12 @@ # Inno.Build +## 脚本 SDK 接入 + +`SdkScriptCompilationEnvironment` 是 Build 到脚本编译器的适配边界:Host 提供实际 dotnet host 与 Project 根目录,每次捕获选定 SDK 的引用和符号,再返回中立的 `ScriptCompilationInputs`。它不把 MSBuild、Roslyn 或平台类型暴露给编译器,不维护 reference pack 版本列表。 + +SDK 从创作源 owner 的目录选定。求值工程与还原中间态由 `Library/Scripting/SdkInputs` 下的 Core IO process owner 管理,操作排空后释放。取消不会留下可用发布,错误保留 SDK 输出。该适配器不修改 SDK、NuGet 全局缓存或 Project 的 global.json,也不生成依赖锁清单。 + + [Build 索引](README.md) · [Wiki 首页](../README.md) · [Runtime](../runtime/Inno.Runtime.md) ## 职责与边界 @@ -101,40 +108,17 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 ## 当前源码公开 API 清单 -当前声明由源码语义清单核对,包含 public 和可供外部派生实现使用的 protected 成员。构建契约不作为游戏脚本导出;线程、失败与所有权要求见前文及每项 XML。 - -### `Inno.Build.BuildDiagnosticSeverity` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`Inno.Build.BuildDiagnosticSeverity.Error = 2`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L23) | Reports a condition that prevented a valid build. | -| [`Inno.Build.BuildDiagnosticSeverity.Information = 0`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L13) | Provides non-failing build context. | -| [`Inno.Build.BuildDiagnosticSeverity.Warning = 1`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L18) | Reports a condition that did not prevent output creation. | -| [`public enum Inno.Build.BuildDiagnosticSeverity : int`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L8) | Identifies the severity of one structured build diagnostic. | - -### `Inno.Build.BuildDiagnostic` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildDiagnostic.BuildDiagnostic(Inno.Build.BuildDiagnosticSeverity severity, string code, string message)`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L46) | Creates an immutable build diagnostic.
`severity`:The impact of the diagnostic on build success.
`code`:The stable diagnostic code.
`message`:The human-readable explanation.
T:System.ArgumentException:Thrown when code or message is empty. | -| [`public string Inno.Build.BuildDiagnostic.code { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L66) | Gets the stable diagnostic code. | -| [`public string Inno.Build.BuildDiagnostic.message { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L71) | Gets the human-readable explanation. | -| [`public Inno.Build.BuildDiagnosticSeverity Inno.Build.BuildDiagnostic.severity { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L61) | Gets the impact of the diagnostic on build success. | -| [`public sealed class Inno.Build.BuildDiagnostic`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L29) | Describes one stable machine-readable build problem or informational event. | - -### `Inno.Build.BuildPipeline` - @@ -143,20 +127,6 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildPipeline.BuildPipeline(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Plugins.Authoring.PluginEnvironment plugins, Inno.Core.Settings.ProjectSettingsStore settings, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Reload.GenerationCoordinator generations, Inno.Scripting.Compiler.ScriptCompiler compiler, string supportPackRoot, System.Collections.Generic.IEnumerable gameTargets, Inno.Build.Managed.ManagedDeploymentCatalog managedDeployments, Inno.Build.IPlayerSupportPackProvisioner? supportPackProvisioner = null)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L68) | Creates a build pipeline from installed Player Support Packs and platform packagers.
`supportPackRoot`:The directory containing one child directory per .
`assets`:The active authoring asset pipeline captured by builds.
`generations`:The host-wide admission gate that remains pinned until the build snapshot is released.
`plugins`:The active Plugin environment captured by builds.
`settings`:The current project settings store captured by builds.
`serialization`:The serialization registry used to pin and encode the build snapshot.
`compiler`:The project compiler used to produce a fresh runtime-only assembly generation for each game build.
`gameTargets`:The complete set of replaceable platform package implementations available to this host.
`managedDeployments`:Explicit managed publishers selected independently of the platform packagers.
`supportPackProvisioner`:Optional build-time provider that checks current source and SDK inputs during explicit pack preparation.
T:System.ArgumentException:Thrown when the Support Pack root is empty, no target is provided, or a target identity is duplicated. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.BuildGameAsync(Inno.Build.GameBuildRequest request, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L253) | Builds and installs one complete source-free game deployment with rollback protection.
约束:Prepare current packs with EnsurePlayerSupportPackAsync before invoking this method on the authoring owner's thread. The synchronous snapshot phase never waits for asynchronous provisioning. Coordinate readers and writers of the output directory during its two installation moves. Post-commit backup, staging and progress observer failures are warnings on the successful result.
`request`:The exact profile, destination, and activated runtime compilation generation.
`progress`:Optional observer for monotonic stage progress.
`cancellationToken`:The token that cancels work before directory installation begins.
返回:The durable output identity and deployment metrics.
T:System.OperationCanceledException:Thrown when cancellation is requested before commit.
T:System.IO.DirectoryNotFoundException:The target Support Pack is missing; prepare it before starting the owner-thread build snapshot.
T:System.IO.InvalidDataException:The target Support Pack is present but invalid.
T:System.IO.IOException:Output installation fails before a complete candidate is committed.
T:System.AggregateException:Output installation and restoration both fail; the previous tree remains at the reported backup path. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.BuildPluginAsync(Inno.Build.PluginBuildRequest request, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L287) | Builds and atomically commits one deterministic project Plugin package.
`request`:The package identity, destination, and dependency embedding policy.
`progress`:Optional observer for monotonic stage progress.
`cancellationToken`:The token that cancels work before atomic commit.
返回:The durable package identity and source-content metrics.
T:System.OperationCanceledException:Thrown when cancellation is requested before commit. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.EnsurePlayerSupportPackAsync(Inno.Build.BuildTargetId target, Inno.Build.Managed.ManagedDeploymentId? deployment, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L203) | Validates the platform and managed deployment before asynchronously preparing its Support Pack.
`target`:The registered platform and architecture target to prepare.
`deployment`:The selected managed publisher, or null to use the platform's default deployment. Unsupported pairs fail before any provisioner or external tool starts.
`cancellationToken`:Cancellation before the Pack installation commits.
返回:Reading ownership of the verified Pack, to be disposed after starting its consuming build. No authoring asset database operations run during preparation.
T:System.NotSupportedException:No game target is registered for the requested identity.
T:System.InvalidOperationException:The managed publisher is absent or cannot publish the selected platform's runtime identifier.
T:System.IO.DirectoryNotFoundException:The Pack is absent and this host has no provisioner.
T:System.IO.InvalidDataException:An installed or newly published Pack fails deployment validation. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildPipeline.GetManagedDeployments(Inno.Build.BuildTargetId target)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L144) | Gets the managed deployment choices supported by a registered publication platform.
`target`:The registered publication target whose managed runtime requirements are queried.
返回:Immutable deployment identities in stable order; an unknown target throws explicitly.
T:System.ArgumentException:The publication target is not registered in this build composition. | -| [`public bool Inno.Build.BuildPipeline.TryGetGameTargetDisplayName(Inno.Build.BuildTargetId target, out string displayName)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L161) | Tries to get the adapter-owned display name for a registered game target.
`target`:The stable target identity to query.
`displayName`:Receives the registered user-facing name, or an empty value when no target is registered.
返回:when the target is registered; otherwise, . | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildPipeline.availableGameTargets { get; }`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L130) | Gets an immutable view of every registered build target in stable identity order. | -| [`public sealed class Inno.Build.BuildPipeline`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L21) | Orchestrates deterministic Plugin and game builds over isolated staging and atomic commits. | - -### `Inno.Build.BuildProfile` - @@ -169,40 +139,11 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildProfile.BuildProfile()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L15) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public Inno.Build.BuildProfile Inno.Build.BuildProfile.Copy()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L91) | Copies product and deployment settings before an asynchronous build retains the request.
返回:A newly owned profile with the same current-format scalar settings and stable selections. | -| [`public void Inno.Build.BuildProfile.Validate()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L109) | Validates product identity, startup content, target, and window dimensions.
T:System.IO.InvalidDataException:Thrown when the profile cannot produce a portable Player deployment. | -| [`public string Inno.Build.BuildProfile.applicationId { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L28) | Gets or sets the stable lowercase identity used for persistent application data. | -| [`public Inno.Build.Managed.ManagedDeploymentId? Inno.Build.BuildProfile.managedDeployment { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L61) | Gets or sets an explicit managed publisher; null uses the platform composition's default. | -| [`public string Inno.Build.BuildProfile.persistentDataPath { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L41) | Gets or sets the writable data folder relative to the operating system's local application data directory. An empty value uses . | -| [`public string Inno.Build.BuildProfile.productName { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L34) | Gets or sets the player-facing product and output name. | -| [`public string Inno.Build.BuildProfile.startupScene { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L47) | Gets or sets the mount-qualified startup scene path. | -| [`public Inno.Build.BuildTargetId Inno.Build.BuildProfile.target { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L52) | Gets or sets the platform target. | -| [`public int Inno.Build.BuildProfile.windowHeight { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L80) | Gets or sets the initial logical window height. | -| [`public int Inno.Build.BuildProfile.windowWidth { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L74) | Gets or sets the initial logical window width. | -| [`public sealed class Inno.Build.BuildProfile : Inno.Core.Serialization.ISerializable`](../../build/pipeline/Inno.Build/BuildProfile.cs#L15) | Defines reusable product and startup settings for deterministic game builds. | - -### `Inno.Build.BuildProfileStore` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildProfileStore.BuildProfileStore(string path, Inno.Core.Serialization.SerializationRegistry serialization)`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L36) | Creates a store for one current-format build profile document.
`path`:The project-owned path of the build profile document.
`serialization`:The serialization registry whose active generation owns the generated converter.
T:System.ArgumentException:Thrown when is empty.
T:System.ArgumentNullException:Thrown when is . | -| [`public Inno.Build.BuildProfile Inno.Build.BuildProfileStore.Load()`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L63) | Loads and validates the current build profile document.
返回:A newly owned build profile that callers may edit independently.
T:System.IO.FileNotFoundException:Thrown when the project does not contain a build profile document.
T:System.IO.InvalidDataException:Thrown when the document is corrupt or contains an invalid profile. | -| [`public void Inno.Build.BuildProfileStore.Save(Inno.Build.BuildProfile profile)`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L99) | Validates and atomically saves the supplied build profile.
`profile`:The complete current profile to persist.
T:System.ArgumentNullException:Thrown when is .
T:System.IO.InvalidDataException:Thrown when the profile cannot produce a supported Player deployment.
T:System.IO.IOException:Thrown when the candidate document cannot be written or atomically installed. | -| [`public bool Inno.Build.BuildProfileStore.exists { get; }`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L49) | Gets whether the current project contains a build profile document. | -| [`public sealed class Inno.Build.BuildProfileStore`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L16) | Persists one project's current game build profile through the engine serialization contract.
约束:The store writes a complete candidate beside the destination and replaces the active document only after serialization and validation succeed. | - -### `Inno.Build.BuildProgress` - @@ -214,23 +155,6 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildProgress.BuildProgress(string stage, double fraction, string message)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L28) | Creates a validated progress update.
`stage`:The stable build stage name.
`fraction`:The completed fraction in the inclusive range from zero to one.
`message`:A concise human-readable status message.
T:System.ArgumentException:Thrown when stage or message is empty.
T:System.ArgumentOutOfRangeException:Thrown when fraction is outside the supported range. | -| [`public bool Inno.Build.BuildProgress.Equals(Inno.Build.BuildProgress other)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.BuildProgress.Equals(object obj)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`:The object to compare with the current instance.
返回:if and this instance are the same type and represent the same value; otherwise, . | -| [`public override int Inno.Build.BuildProgress.GetHashCode()`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | -| [`public override string Inno.Build.BuildProgress.ToString()`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | -| [`public static bool Inno.Build.BuildProgress.operator ==(Inno.Build.BuildProgress left, Inno.Build.BuildProgress right)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.BuildProgress.operator !=(Inno.Build.BuildProgress left, Inno.Build.BuildProgress right)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public double Inno.Build.BuildProgress.fraction { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L52) | Gets the completed fraction in the inclusive range from zero to one. | -| [`public string Inno.Build.BuildProgress.message { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L57) | Gets a concise human-readable status message. | -| [`public string Inno.Build.BuildProgress.stage { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L47) | Gets the stable build stage name. | -| [`public readonly record struct Inno.Build.BuildProgress : System.IEquatable`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Reports monotonic progress for one asynchronous build operation. | - -### `Inno.Build.BuildResult` - @@ -241,22 +165,6 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public int Inno.Build.BuildResult.artifactBundleCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L61) | Gets the number of content-addressed artifact bundles represented by the output. | -| [`public int Inno.Build.BuildResult.assetCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L56) | Gets the number of deployed runtime assets or packaged source assets. | -| [`public string? Inno.Build.BuildResult.contentHash { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L51) | Gets the deterministic content identity, or when the build failed. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildResult.diagnostics { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L76) | Gets structured diagnostics emitted during the build. | -| [`public int Inno.Build.BuildResult.embeddedPluginCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L71) | Gets the number of embedded dependency Plugin packages. | -| [`public string? Inno.Build.BuildResult.outputPath { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L41) | Gets the atomically committed output path, or when the build failed. | -| [`public int Inno.Build.BuildResult.runtimeAssemblyCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L66) | Gets the number of deployed runtime assemblies. | -| [`public bool Inno.Build.BuildResult.succeeded { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L36) | Gets whether the build committed a complete output. | -| [`public Inno.Build.BuildTargetId? Inno.Build.BuildResult.target { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L46) | Gets the game target, or for a Plugin package. | -| [`public sealed class Inno.Build.BuildResult`](../../build/pipeline/Inno.Build/BuildResult.cs#L9) | Reports the immutable outcome, diagnostics, and optional durable output of one build. | - -### `Inno.Build.BuildSettings` - @@ -273,28 +181,6 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildSettings.BuildSettings()`](../../build/pipeline/Inno.Build/BuildSettings.cs#L14) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public Inno.Build.BuildSettings Inno.Build.BuildSettings.Copy()`](../../build/pipeline/Inno.Build/BuildSettings.cs#L165) | Creates an isolated copy suitable for a temporary export draft.
返回:A newly owned settings object. | -| [`public static Inno.Build.BuildSettings Inno.Build.BuildSettings.CreateDefault(string projectName, string startupScene, Inno.Build.BuildTargetId target)`](../../build/pipeline/Inno.Build/BuildSettings.cs#L113) | Creates canonical defaults for a new project.
`projectName`:The project directory name used for initial labels and output paths.
`startupScene`:The initial mount-qualified startup Scene, or an empty value when unavailable.
`target`:The host-appropriate initial build target.
返回:A newly owned build settings object.
T:System.ArgumentException:Thrown when is empty or is invalid. | -| [`public Inno.Build.BuildProfile Inno.Build.BuildSettings.CreateGameProfile(Inno.Core.Settings.ProjectId projectId)`](../../build/pipeline/Inno.Build/BuildSettings.cs#L146) | Creates an isolated game profile from the current defaults.
`projectId`:The authoritative project identity copied into the game profile.
返回:A newly owned profile that may be changed for one build without mutating these settings. | -| [`public Inno.Build.Managed.ManagedDeploymentId? Inno.Build.BuildSettings.gameManagedDeployment { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L86) | Gets or sets the default managed publisher; null follows the selected platform's default. | -| [`public string Inno.Build.BuildSettings.gameOutputDirectory { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L57) | Gets or sets the default game output directory, relative to the project root or absolute. | -| [`public string Inno.Build.BuildSettings.gamePersistentDataPath { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L45) | Gets or sets the default writable data folder below the operating system's local application data directory. An empty value uses the project ID. | -| [`public string Inno.Build.BuildSettings.gameProductName { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L38) | Gets or sets the default player-facing product name. | -| [`public string Inno.Build.BuildSettings.gameStartupScene { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L51) | Gets or sets the default mount-qualified startup Scene path. | -| [`public Inno.Build.BuildTargetId Inno.Build.BuildSettings.gameTarget { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L74) | Gets or sets the default game build target. | -| [`public int Inno.Build.BuildSettings.gameWindowHeight { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L69) | Gets or sets the default initial logical window height. | -| [`public int Inno.Build.BuildSettings.gameWindowWidth { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L63) | Gets or sets the default initial logical window width. | -| [`public bool Inno.Build.BuildSettings.includePluginDependencies { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L32) | Gets or sets whether Plugin dependency packages are embedded by default. | -| [`public string Inno.Build.BuildSettings.pluginDisplayName { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L20) | Gets or sets the default user-facing Plugin name. | -| [`public string Inno.Build.BuildSettings.pluginOutputPath { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L26) | Gets or sets the default Plugin package destination, relative to the project root or absolute. | -| [`public sealed class Inno.Build.BuildSettings : Inno.Core.Serialization.ISerializable`](../../build/pipeline/Inno.Build/BuildSettings.cs#L14) | Defines project-owned defaults copied into each temporary game or Plugin export request. | - -### `Inno.Build.BuildSettingsStore` - @@ -304,21 +190,6 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildSettingsStore.BuildSettingsStore(string path, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Build.BuildSettings defaultSettings)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L37) | Creates a store for one project's current build settings document.
`path`:The project-owned Settings.Build.inno path.
`serialization`:The active engine serialization registry.
`defaultSettings`:Canonical project defaults used while no document exists.
T:System.ArgumentException:Thrown when is empty.
T:System.ArgumentNullException:Thrown when or is null. | -| [`public byte[] Inno.Build.BuildSettingsStore.CaptureDocument()`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L122) | Captures the current effective build defaults as a native document.
返回:A newly owned current-format document payload. | -| [`public Inno.Build.BuildSettings Inno.Build.BuildSettingsStore.Load()`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L87) | Loads the saved settings or returns an isolated copy of the canonical defaults.
返回:A newly owned settings object.
T:System.IO.InvalidDataException:Thrown when the saved document is malformed. | -| [`public void Inno.Build.BuildSettingsStore.RestoreDocument(System.ReadOnlySpan document)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L140) | Validates and atomically restores a native build settings document.
`document`:The complete document payload.
T:System.IO.InvalidDataException:Thrown when the document is malformed.
T:System.IO.IOException:Thrown when the document cannot be committed. | -| [`public void Inno.Build.BuildSettingsStore.Save(Inno.Build.BuildSettings settings)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L108) | Atomically replaces the project-owned build defaults.
`settings`:The complete defaults to persist.
T:System.ArgumentNullException:Thrown when is null.
T:System.IO.InvalidDataException:Thrown when the settings document is malformed.
T:System.IO.IOException:Thrown when the candidate cannot be committed. | -| [`public void Inno.Build.BuildSettingsStore.ValidateDocument(System.ReadOnlySpan document)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L156) | Validates a native build settings document without changing the active file.
`document`:The document payload to inspect.
T:System.IO.InvalidDataException:Thrown when the document is malformed. | -| [`public Inno.Build.BuildSettings Inno.Build.BuildSettingsStore.defaultSettings { get; }`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L69) | Gets an isolated copy of the canonical project defaults. | -| [`public bool Inno.Build.BuildSettingsStore.exists { get; }`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L57) | Gets whether a project-owned build settings document exists. | -| [`public sealed class Inno.Build.BuildSettingsStore`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L13) | Persists project-owned export defaults independently from temporary export requests. | - -### `Inno.Build.BuildTargetId` - @@ -328,113 +199,34 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.BuildTargetId.BuildTargetId(string value)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L19) | Creates a stable target identity.
`value`:A lowercase portable identifier composed of letters, numbers, and hyphens.
T:System.ArgumentException:Thrown when is not a portable target identity. | -| [`public bool Inno.Build.BuildTargetId.Equals(Inno.Build.BuildTargetId other)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`:An object to compare with this object.
返回:if the current object is equal to the parameter; otherwise, . | -| [`public override bool Inno.Build.BuildTargetId.Equals(object obj)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`:The object to compare with the current instance.
返回:if and this instance are the same type and represent the same value; otherwise, . | -| [`public override int Inno.Build.BuildTargetId.GetHashCode()`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | -| [`public override string Inno.Build.BuildTargetId.ToString()`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L41) | Formats the portable target identity.
返回:The exact stable target identity. | -| [`public static bool Inno.Build.BuildTargetId.operator ==(Inno.Build.BuildTargetId left, Inno.Build.BuildTargetId right)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public static bool Inno.Build.BuildTargetId.operator !=(Inno.Build.BuildTargetId left, Inno.Build.BuildTargetId right)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public string Inno.Build.BuildTargetId.value { get; }`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L33) | Gets the portable target identity. | -| [`public readonly record struct Inno.Build.BuildTargetId : System.IEquatable`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Identifies one stable platform and architecture combination supported by the game build pipeline. | - -### `Inno.Build.GameBuildRequest` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.GameBuildRequest.GameBuildRequest()`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L9) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public void Inno.Build.GameBuildRequest.Validate()`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L27) | Validates destination and product inputs without mutating output.
T:System.IO.InvalidDataException:Thrown when the request is incomplete. | -| [`public required string Inno.Build.GameBuildRequest.outputDirectory { get; init; }`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L19) | Gets or initializes the parent directory that receives the atomically committed platform output. | -| [`public required Inno.Build.BuildProfile Inno.Build.GameBuildRequest.profile { get; init; }`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L14) | Gets or initializes the reusable product profile. | -| [`public sealed class Inno.Build.GameBuildRequest`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L9) | Requests one game build from the current validated authoring generation. | - -### `Inno.Build.GameBuildContentContext` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public string Inno.Build.GameBuildContentContext.outputDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L29) | Gets the empty staging directory that receives source-free target artifacts. | -| [`public Inno.Build.BuildProfile Inno.Build.GameBuildContentContext.profile { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L24) | Gets the validated product and target profile. | -| [`public Inno.Core.Serialization.SerializationGeneration Inno.Build.GameBuildContentContext.serialization { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L34) | Gets the immutable converter generation pinned by the surrounding build transaction. | -| [`public sealed class Inno.Build.GameBuildContentContext`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L9) | Supplies isolated staging and profile data for target-specific offline artifact generation. | - -### `Inno.Build.GameBuildPackageContext` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public string Inno.Build.GameBuildPackageContext.contentDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L37) | Gets the source-free packaged content directory to deploy. | -| [`public Inno.Build.Managed.ManagedDeploymentResult Inno.Build.GameBuildPackageContext.managedDeployment { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L42) | Gets the verified managed publication produced independently of platform packaging. | -| [`public string Inno.Build.GameBuildPackageContext.outputDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L47) | Gets the empty staging parent where the target must create exactly one output. | -| [`public Inno.Build.BuildProfile Inno.Build.GameBuildPackageContext.profile { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L27) | Gets the validated product profile. | -| [`public string Inno.Build.GameBuildPackageContext.supportPackDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L32) | Gets the verified read-only Support Pack directory for this target. | -| [`public sealed class Inno.Build.GameBuildPackageContext`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L8) | Supplies an isolated staging layout to a replaceable platform packager. | - -### `Inno.Build.GameBuildTargetBinding` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.GameBuildTargetBinding.GameBuildTargetBinding(Inno.Build.IGameBuildTarget packager, Inno.Build.IGameContentCompiler compiler)`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L25) | Validates a complete binding without starting tools or taking ownership of services.
`packager`:The borrowed platform packaging implementation.
`compiler`:The borrowed content implementation for the same target and authoring generation.
T:System.ArgumentNullException:Either implementation is null.
T:System.ArgumentException:A target is unassigned or the two implementation identities disagree. | -| [`public Inno.Build.IGameContentCompiler Inno.Build.GameBuildTargetBinding.compiler { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L45) | Gets the content implementation borrowing this pipeline's authoring generation. | -| [`public Inno.Build.IGameBuildTarget Inno.Build.GameBuildTargetBinding.packager { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L40) | Gets the platform implementation responsible for layout and Support Pack validation. | -| [`public sealed class Inno.Build.GameBuildTargetBinding`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L8) | Binds independent packaging and content implementations for one publication target. | - -### `Inno.Build.IGameBuildTarget` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.IGameBuildTarget.PackageAsync(Inno.Build.GameBuildPackageContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L47) | Composes one platform output from a verified Support Pack and source-free content directory.
`context`:The isolated package context owned by the current build.
`cancellationToken`:The token that cancels work before the build is committed.
返回:The single platform output path created beneath .
T:System.OperationCanceledException:Thrown when packaging is canceled. | -| [`Inno.Build.Managed.ManagedDeploymentId Inno.Build.IGameBuildTarget.defaultManagedDeployment { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L20) | Gets the managed publisher selected when the profile does not specify an override. | -| [`string Inno.Build.IGameBuildTarget.displayName { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L30) | Gets the user-facing target name presented by authoring hosts. | -| [`Inno.Build.BuildTargetId Inno.Build.IGameBuildTarget.id { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L15) | Gets the stable target identity implemented by this packager. | -| [`string Inno.Build.IGameBuildTarget.runtimeIdentifier { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L25) | Gets the managed toolchain target identifier required by this platform composition. | -| [`public interface Inno.Build.IGameBuildTarget : Inno.Build.IPlayerSupportPackValidator`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L10) | Defines the replaceable platform packaging boundary for one game build target. | - -### `Inno.Build.IGameContentCompiler` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.IGameContentCompiler.CompileAsync(Inno.Build.GameBuildContentContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L31) | Writes target content into the current operation's isolated staging directory.
`context`:The borrowed authoring generation and operation-owned content destination.
`cancellationToken`:Cancels compilation before publication; associated work must finish before returning.
返回:An operation completing after every required artifact is staged, or faulting on failure.
T:System.OperationCanceledException:Cancellation was requested before compilation completed. | -| [`Inno.Build.BuildTargetId Inno.Build.IGameContentCompiler.target { get; }`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L14) | Gets the exact publication target whose content this compiler produces. | -| [`public interface Inno.Build.IGameContentCompiler`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L9) | Compiles source-free runtime content independently of application packaging. | - -### `Inno.Build.PlatformTargetDescriptor` - @@ -443,68 +235,18 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.PlatformTargetDescriptor.PlatformTargetDescriptor(Inno.Build.BuildTargetId id, string platform, string architecture, string abi, string runtimeIdentifier, bool supportsEditor)`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L34) | Creates an explicit platform, processor, ABI and managed-runtime mapping.
`id`:The stable publication target identity.
`platform`:The platform package owning system integration.
`architecture`:The target processor architecture, such as x64, arm64 or wasm32.
`abi`:The native ABI selected by this platform implementation.
`runtimeIdentifier`:The exact managed toolchain runtime identifier.
`supportsEditor`:Whether this implemented target supports the shared Editor product.
T:System.ArgumentException:An identity or required target fact is unassigned. | -| [`public string Inno.Build.PlatformTargetDescriptor.abi { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L73) | Gets the explicitly selected native ABI. | -| [`public string Inno.Build.PlatformTargetDescriptor.architecture { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L68) | Gets the explicitly declared processor architecture. | -| [`public Inno.Build.BuildTargetId Inno.Build.PlatformTargetDescriptor.id { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L58) | Gets the stable publication target identity. | -| [`public string Inno.Build.PlatformTargetDescriptor.platform { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L63) | Gets the owning platform package. | -| [`public string Inno.Build.PlatformTargetDescriptor.runtimeIdentifier { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L78) | Gets the managed runtime mapping without deriving it from the target identity. | -| [`public bool Inno.Build.PlatformTargetDescriptor.supportsEditor { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L83) | Gets whether this target provides an implemented Editor host. | -| [`public sealed class Inno.Build.PlatformTargetDescriptor`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L8) | Declares immutable product target facts independently of the machine executing build tools. | - -### `Inno.Build.IPlayerSupportPackProvisioner` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`System.Threading.Tasks.ValueTask Inno.Build.IPlayerSupportPackProvisioner.ProvisionAsync(Inno.Build.BuildTargetId target, string supportPackRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackProvisioner.cs#L26) | Checks current source inputs and selects a complete immutable pack before the build resumes.
`target`:The platform and architecture target whose current inputs are required.
`supportPackRoot`:The catalog directory where the verified pack must be installed.
`cancellationToken`:Cancellation before the installation commits.
返回:An operation that completes after the pack has been installed. | -| [`public interface Inno.Build.IPlayerSupportPackProvisioner`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackProvisioner.cs#L9) | Prepares current Player Support Pack inputs without coupling game builds to an SDK or platform toolchain. | - -### `Inno.Build.IPlayerSupportPackValidator` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`void Inno.Build.IPlayerSupportPackValidator.Validate(string directory)`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackValidator.cs#L17) | Rejects an incomplete or incompatible platform closure.
`directory`:The prepared or installed Support Pack directory.
T:System.IO.InvalidDataException:A required platform input is absent or incompatible. | -| [`public interface Inno.Build.IPlayerSupportPackValidator`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackValidator.cs#L6) | Validates platform-owned runtime or link inputs without coupling the pack catalog to target names. | - -### `Inno.Build.PlayerSupportPackCatalog` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.PlayerSupportPackCatalog.PlayerSupportPackCatalog(string root)`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L49) | Creates a catalog rooted at the directory containing target-specific Player Support Packs.
`root`:The directory whose target children contain immutable fingerprint directories and a current index.
T:System.ArgumentException:Thrown when is empty. | -| [`public Inno.Build.PlayerSupportPackLease Inno.Build.PlayerSupportPackCatalog.Acquire(Inno.Build.BuildTargetId target, Inno.Build.IPlayerSupportPackValidator validator, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L144) | Resolves one installed Support Pack after validating its deployment-only closure.
`target`:The platform and architecture identity of the required Support Pack.
返回:Reading ownership of the complete pack; dispose it after its final consumer finishes.
T:System.IO.DirectoryNotFoundException:Thrown when no Support Pack is installed for .
T:System.IO.InvalidDataException:Thrown when the pack is empty, contains build-time payload, or lacks its Player executable or native runtimes.
`validator`:The target-owned validator for runtime or linker inputs.
`cancellationToken`:Cancels ownership acquisition or complete content validation before returning a lease.
T:System.OperationCanceledException:Acquisition or validation was canceled; no reading ownership is transferred.
T:System.TimeoutException:Exclusive repair or retirement retains the artifact for more than thirty seconds. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.PlayerSupportPackCatalog.PublishAsync(Inno.Build.BuildTargetId target, string stagingDirectory, Inno.Build.IPlayerSupportPackValidator validator, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), System.Func? verifyInputs = null)`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L198) | Validates and publishes an immutable pack, then atomically selects it for new readers.
`target`:The platform and architecture owning the staged inputs.
`stagingDirectory`:A complete, exclusively owned staging tree on the same filesystem as this catalog. Successful publication consumes this directory unless identical inputs already exist.
`validator`:The target validator for runtime and linker inputs.
`cancellationToken`:Cancels validation or lease acquisition before selecting the completed pack.
`verifyInputs`:Optional final source-input gate after output validation and any publication wait. Omit only when the caller exclusively owns a fully prepared tree with no external preparation inputs.
返回:Reading ownership of the selected immutable pack; earlier readers retain their own protection.
T:System.IO.InvalidDataException:The candidate is incomplete or contains forbidden inputs.
T:System.IO.IOException:The completed directory or its atomic current index cannot be installed.
T:System.OperationCanceledException:Cancellation occurred before the current index was committed. | -| [`public System.Threading.Tasks.ValueTask Inno.Build.PlayerSupportPackCatalog.SelectAsync(Inno.Build.BuildTargetId target, string fingerprint, Inno.Build.IPlayerSupportPackValidator validator, System.Func verifyInputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L85) | Validates one immutable content identity and atomically selects it for subsequent readers.
`target`:The explicit target whose prepared Pack is requested.
`fingerprint`:The exact lowercase SHA-256 content identity recorded by the publisher.
`validator`:The borrowed platform closure validator.
`verifyInputs`:The final awaited source-input gate after output validation and before selection.
`cancellationToken`:Cancels validation or selection before replacing the current index.
返回:Reading ownership of the complete requested Pack; dispose it after every consumer finishes.
T:System.ArgumentException:The content identity is invalid or the target path escapes the catalog.
T:System.IO.InvalidDataException:The requested directory or its complete file set and hashes are invalid.
T:System.OperationCanceledException:Validation or selection was canceled. | -| [`public sealed class Inno.Build.PlayerSupportPackCatalog`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L17) | Resolves and validates installed Player Support Packs without loading authoring services. | - -### `Inno.Build.PlayerSupportPackLease` - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public void Inno.Build.PlayerSupportPackLease.Dispose()`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L42) | Releases reading rights after all compilation, copying and linking have completed. Repeated disposal has no effect. | -| [`public string Inno.Build.PlayerSupportPackLease.directory { get; }`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L29) | Gets the verified absolute directory while this lease owns reading rights.
T:System.ObjectDisposedException:The consumer has already released its reading rights. | -| [`public sealed class Inno.Build.PlayerSupportPackLease : System.IDisposable`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L10) | Keeps verified Player compilation and linker inputs alive until their consumer finishes. | - -### `Inno.Build.PluginBuildRequest` - @@ -513,46 +255,180 @@ ContentPackWriter 与 ContentPackReader 共用 ContentKey/Index 协议。冻结 + + + -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public Inno.Build.PluginBuildRequest.PluginBuildRequest()`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L11) | 由 C# 生成,遵循所属类型的值语义与本页的生命周期约束。 | -| [`public void Inno.Build.PluginBuildRequest.Validate()`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L45) | Validates package identity and destination syntax.
T:System.IO.InvalidDataException:Thrown when the request cannot identify a portable Plugin package. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Build.PluginBuildRequest.dependencies { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L37) | Gets or initializes the installed plugins this package actually requires. Their transitive dependencies are included in the package manifest. | -| [`public required string Inno.Build.PluginBuildRequest.displayName { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L21) | Gets or initializes the user-facing Plugin name. | -| [`public bool Inno.Build.PluginBuildRequest.includeDependencies { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L31) | Gets or initializes whether the complete transitive dependency closure is embedded. | -| [`public required string Inno.Build.PluginBuildRequest.outputPath { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L26) | Gets or initializes the destination .iplugin package path. | -| [`public required string Inno.Build.PluginBuildRequest.pluginId { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L16) | Gets or initializes the globally stable lowercase Plugin identity. | -| [`public sealed class Inno.Build.PluginBuildRequest`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L11) | Requests one deterministic project-to-Plugin package without a companion authoring asset. | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public enum Inno.Build.BuildDiagnosticSeverity : int`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L8) | Identifies the severity of one structured build diagnostic. | — | — | +| [`Inno.Build.BuildDiagnosticSeverity.Information = 0`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L13) | Provides non-failing build context. | — | — | +| [`Inno.Build.BuildDiagnosticSeverity.Warning = 1`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L18) | Reports a condition that did not prevent output creation. | — | — | +| [`Inno.Build.BuildDiagnosticSeverity.Error = 2`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L23) | Reports a condition that prevented a valid build. | — | — | +| [`public sealed class Inno.Build.BuildDiagnostic`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L29) | Describes one stable machine-readable build problem or informational event. | — | — | +| [`public Inno.Build.BuildDiagnostic.BuildDiagnostic(Inno.Build.BuildDiagnosticSeverity severity, string code, string message)`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L46) | Creates an immutable build diagnostic.
`severity`: The impact of the diagnostic on build success.
`code`: The stable diagnostic code.
`message`: The human-readable explanation. | — | `System.ArgumentException`: Thrown when code or message is empty. | +| [`public Inno.Build.BuildDiagnosticSeverity Inno.Build.BuildDiagnostic.severity { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L61) | Gets the impact of the diagnostic on build success. | — | — | +| [`public string Inno.Build.BuildDiagnostic.code { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L66) | Gets the stable diagnostic code. | — | — | +| [`public string Inno.Build.BuildDiagnostic.message { get; }`](../../build/pipeline/Inno.Build/BuildDiagnostic.cs#L71) | Gets the human-readable explanation. | — | — | +| [`public sealed class Inno.Build.BuildPipeline`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L21) | Orchestrates deterministic Plugin and game builds over isolated staging and atomic commits. | — | — | +| [`public Inno.Build.BuildPipeline.BuildPipeline(Inno.Assets.Pipeline.AssetPipeline assets, Inno.Plugins.Authoring.PluginEnvironment plugins, Inno.Core.Settings.ProjectSettingsStore settings, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Extensibility.Reload.GenerationCoordinator generations, Inno.Scripting.Compiler.ScriptCompiler compiler, string supportPackRoot, System.Collections.Generic.IEnumerable gameTargets, Inno.Build.Managed.ManagedDeploymentCatalog managedDeployments, Inno.Build.IPlayerSupportPackProvisioner? supportPackProvisioner = null)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L68) | Creates a build pipeline from installed Player Support Packs and platform packagers.
`supportPackRoot`: The directory containing one child directory per .
`assets`: The active authoring asset pipeline captured by builds.
`generations`: The host-wide admission gate that remains pinned until the build snapshot is released.
`plugins`: The active Plugin environment captured by builds.
`settings`: The current project settings store captured by builds.
`serialization`: The serialization registry used to pin and encode the build snapshot.
`compiler`: The project compiler used to produce a fresh runtime-only assembly generation for each game build.
`gameTargets`: The complete set of replaceable platform package implementations available to this host.
`managedDeployments`: Explicit managed publishers selected independently of the platform packagers.
`supportPackProvisioner`: Optional build-time provider that checks current source and SDK inputs during explicit pack preparation. | — | `System.ArgumentException`: Thrown when the Support Pack root is empty, no target is provided, or a target identity is duplicated. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildPipeline.availableGameTargets { get; }`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L130) | Gets an immutable view of every registered build target in stable identity order. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildPipeline.GetManagedDeployments(Inno.Build.BuildTargetId target)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L144) | Gets the managed deployment choices supported by a registered publication platform.
`target`: The registered publication target whose managed runtime requirements are queried. | Immutable deployment identities in stable order; an unknown target throws explicitly. | `System.ArgumentException`: The publication target is not registered in this build composition. | +| [`public bool Inno.Build.BuildPipeline.TryGetGameTargetDisplayName(Inno.Build.BuildTargetId target, out string displayName)`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L161) | Tries to get the adapter-owned display name for a registered game target.
`target`: The stable target identity to query.
`displayName`: Receives the registered user-facing name, or an empty value when no target is registered. | when the target is registered; otherwise, . | — | +| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.EnsurePlayerSupportPackAsync(Inno.Build.BuildTargetId target, Inno.Build.Managed.ManagedDeploymentId? deployment, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L203) | Validates the platform and managed deployment before asynchronously preparing its Support Pack.
`target`: The registered platform and architecture target to prepare.
`deployment`: The selected managed publisher, or null to use the platform's default deployment. Unsupported pairs fail before any provisioner or external tool starts.
`cancellationToken`: Cancellation before the Pack installation commits. | Reading ownership of the verified Pack, to be disposed after starting its consuming build. No authoring asset database operations run during preparation. | `System.NotSupportedException`: No game target is registered for the requested identity.
`System.InvalidOperationException`: The managed publisher is absent or cannot publish the selected platform's runtime identifier.
`System.IO.DirectoryNotFoundException`: The Pack is absent and this host has no provisioner.
`System.IO.InvalidDataException`: An installed or newly published Pack fails deployment validation. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.BuildGameAsync(Inno.Build.GameBuildRequest request, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L253) | Builds and installs one complete source-free game deployment with rollback protection.
`request`: The exact profile, destination, and activated runtime compilation generation.
`progress`: Optional observer for monotonic stage progress.
`cancellationToken`: The token that cancels work before directory installation begins. | The durable output identity and deployment metrics. | `System.OperationCanceledException`: Thrown when cancellation is requested before commit.
`System.IO.DirectoryNotFoundException`: The target Support Pack is missing; prepare it before starting the owner-thread build snapshot.
`System.IO.InvalidDataException`: The target Support Pack is present but invalid.
`System.IO.IOException`: Output installation fails before a complete candidate is committed.
`System.AggregateException`: Output installation and restoration both fail; the previous tree remains at the reported backup path. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.BuildPipeline.BuildPluginAsync(Inno.Build.PluginBuildRequest request, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/BuildPipeline.cs#L287) | Builds and atomically commits one deterministic project Plugin package.
`request`: The package identity, destination, and dependency embedding policy.
`progress`: Optional observer for monotonic stage progress.
`cancellationToken`: The token that cancels work before atomic commit. | The durable package identity and source-content metrics. | `System.OperationCanceledException`: Thrown when cancellation is requested before commit. | +| [`public sealed class Inno.Build.BuildProfile : Inno.Core.Serialization.ISerializable`](../../build/pipeline/Inno.Build/BuildProfile.cs#L15) | Defines reusable product and startup settings for deterministic game builds. | — | — | +| [`public string Inno.Build.BuildProfile.applicationId { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L28) | Gets or sets the stable lowercase identity used for persistent application data. | — | — | +| [`public string Inno.Build.BuildProfile.productName { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L34) | Gets or sets the player-facing product and output name. | — | — | +| [`public string Inno.Build.BuildProfile.persistentDataPath { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L41) | Gets or sets the writable data folder relative to the operating system's local application data directory. An empty value uses . | — | — | +| [`public string Inno.Build.BuildProfile.startupScene { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L47) | Gets or sets the mount-qualified startup scene path. | — | — | +| [`public Inno.Build.BuildTargetId Inno.Build.BuildProfile.target { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L52) | Gets or sets the platform target. | — | — | +| [`public Inno.Build.Managed.ManagedDeploymentId? Inno.Build.BuildProfile.managedDeployment { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L61) | Gets or sets an explicit managed publisher; null uses the platform composition's default. | — | — | +| [`public int Inno.Build.BuildProfile.windowWidth { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L74) | Gets or sets the initial logical window width. | — | — | +| [`public int Inno.Build.BuildProfile.windowHeight { get; set; }`](../../build/pipeline/Inno.Build/BuildProfile.cs#L80) | Gets or sets the initial logical window height. | — | — | +| [`public Inno.Build.BuildProfile Inno.Build.BuildProfile.Copy()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L91) | Copies product and deployment settings before an asynchronous build retains the request. | A newly owned profile with the same current-format scalar settings and stable selections. | — | +| [`public void Inno.Build.BuildProfile.Validate()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L109) | Validates product identity, startup content, target, and window dimensions. | — | `System.IO.InvalidDataException`: Thrown when the profile cannot produce a portable Player deployment. | +| [`public Inno.Build.BuildProfile.BuildProfile()`](../../build/pipeline/Inno.Build/BuildProfile.cs#L15) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.BuildProfileStore`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L16) | Persists one project's current game build profile through the engine serialization contract. | — | — | +| [`public Inno.Build.BuildProfileStore.BuildProfileStore(string path, Inno.Core.Serialization.SerializationRegistry serialization)`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L36) | Creates a store for one current-format build profile document.
`path`: The project-owned path of the build profile document.
`serialization`: The serialization registry whose active generation owns the generated converter. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Build.BuildProfileStore.exists { get; }`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L49) | Gets whether the current project contains a build profile document. | — | — | +| [`public Inno.Build.BuildProfile Inno.Build.BuildProfileStore.Load()`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L63) | Loads and validates the current build profile document. | A newly owned build profile that callers may edit independently. | `System.IO.FileNotFoundException`: Thrown when the project does not contain a build profile document.
`System.IO.InvalidDataException`: Thrown when the document is corrupt or contains an invalid profile. | +| [`public void Inno.Build.BuildProfileStore.Save(Inno.Build.BuildProfile profile)`](../../build/pipeline/Inno.Build/BuildProfileStore.cs#L99) | Validates and atomically saves the supplied build profile.
`profile`: The complete current profile to persist. | — | `System.ArgumentNullException`: Thrown when is .
`System.IO.InvalidDataException`: Thrown when the profile cannot produce a supported Player deployment.
`System.IO.IOException`: Thrown when the candidate document cannot be written or atomically installed. | +| [`public readonly record struct Inno.Build.BuildProgress : System.IEquatable`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Reports monotonic progress for one asynchronous build operation. | — | — | +| [`public Inno.Build.BuildProgress.BuildProgress(string stage, double fraction, string message)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L28) | Creates a validated progress update.
`stage`: The stable build stage name.
`fraction`: The completed fraction in the inclusive range from zero to one.
`message`: A concise human-readable status message. | — | `System.ArgumentException`: Thrown when stage or message is empty.
`System.ArgumentOutOfRangeException`: Thrown when fraction is outside the supported range. | +| [`public string Inno.Build.BuildProgress.stage { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L47) | Gets the stable build stage name. | — | — | +| [`public double Inno.Build.BuildProgress.fraction { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L52) | Gets the completed fraction in the inclusive range from zero to one. | — | — | +| [`public string Inno.Build.BuildProgress.message { get; }`](../../build/pipeline/Inno.Build/BuildProgress.cs#L57) | Gets a concise human-readable status message. | — | — | +| [`public override string Inno.Build.BuildProgress.ToString()`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Build.BuildProgress.operator !=(Inno.Build.BuildProgress left, Inno.Build.BuildProgress right)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.BuildProgress.operator ==(Inno.Build.BuildProgress left, Inno.Build.BuildProgress right)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.BuildProgress.GetHashCode()`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Build.BuildProgress.Equals(object obj)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Build.BuildProgress.Equals(Inno.Build.BuildProgress other)`](../../build/pipeline/Inno.Build/BuildProgress.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Build.BuildResult`](../../build/pipeline/Inno.Build/BuildResult.cs#L9) | Reports the immutable outcome, diagnostics, and optional durable output of one build. | — | — | +| [`public bool Inno.Build.BuildResult.succeeded { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L36) | Gets whether the build committed a complete output. | — | — | +| [`public string? Inno.Build.BuildResult.outputPath { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L41) | Gets the atomically committed output path, or when the build failed. | — | — | +| [`public Inno.Build.BuildTargetId? Inno.Build.BuildResult.target { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L46) | Gets the game target, or for a Plugin package. | — | — | +| [`public string? Inno.Build.BuildResult.contentHash { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L51) | Gets the deterministic content identity, or when the build failed. | — | — | +| [`public int Inno.Build.BuildResult.assetCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L56) | Gets the number of deployed runtime assets or packaged source assets. | — | — | +| [`public int Inno.Build.BuildResult.artifactBundleCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L61) | Gets the number of content-addressed artifact bundles represented by the output. | — | — | +| [`public int Inno.Build.BuildResult.runtimeAssemblyCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L66) | Gets the number of deployed runtime assemblies. | — | — | +| [`public int Inno.Build.BuildResult.embeddedPluginCount { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L71) | Gets the number of embedded dependency Plugin packages. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.BuildResult.diagnostics { get; }`](../../build/pipeline/Inno.Build/BuildResult.cs#L76) | Gets structured diagnostics emitted during the build. | — | — | +| [`public sealed class Inno.Build.BuildSettings : Inno.Core.Serialization.ISerializable`](../../build/pipeline/Inno.Build/BuildSettings.cs#L14) | Defines project-owned defaults copied into each temporary game or Plugin export request. | — | — | +| [`public string Inno.Build.BuildSettings.pluginDisplayName { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L20) | Gets or sets the default user-facing Plugin name. | — | — | +| [`public string Inno.Build.BuildSettings.pluginOutputPath { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L26) | Gets or sets the default Plugin package destination, relative to the project root or absolute. | — | — | +| [`public bool Inno.Build.BuildSettings.includePluginDependencies { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L32) | Gets or sets whether Plugin dependency packages are embedded by default. | — | — | +| [`public string Inno.Build.BuildSettings.gameProductName { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L38) | Gets or sets the default player-facing product name. | — | — | +| [`public string Inno.Build.BuildSettings.gamePersistentDataPath { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L45) | Gets or sets the default writable data folder below the operating system's local application data directory. An empty value uses the project ID. | — | — | +| [`public string Inno.Build.BuildSettings.gameStartupScene { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L51) | Gets or sets the default mount-qualified startup Scene path. | — | — | +| [`public string Inno.Build.BuildSettings.gameOutputDirectory { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L57) | Gets or sets the default game output directory, relative to the project root or absolute. | — | — | +| [`public int Inno.Build.BuildSettings.gameWindowWidth { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L63) | Gets or sets the default initial logical window width. | — | — | +| [`public int Inno.Build.BuildSettings.gameWindowHeight { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L69) | Gets or sets the default initial logical window height. | — | — | +| [`public Inno.Build.BuildTargetId Inno.Build.BuildSettings.gameTarget { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L74) | Gets or sets the default game build target. | — | — | +| [`public Inno.Build.Managed.ManagedDeploymentId? Inno.Build.BuildSettings.gameManagedDeployment { get; set; }`](../../build/pipeline/Inno.Build/BuildSettings.cs#L86) | Gets or sets the default managed publisher; null follows the selected platform's default. | — | — | +| [`public static Inno.Build.BuildSettings Inno.Build.BuildSettings.CreateDefault(string projectName, string startupScene, Inno.Build.BuildTargetId target)`](../../build/pipeline/Inno.Build/BuildSettings.cs#L113) | Creates canonical defaults for a new project.
`projectName`: The project directory name used for initial labels and output paths.
`startupScene`: The initial mount-qualified startup Scene, or an empty value when unavailable.
`target`: The host-appropriate initial build target. | A newly owned build settings object. | `System.ArgumentException`: Thrown when is empty or is invalid. | +| [`public Inno.Build.BuildProfile Inno.Build.BuildSettings.CreateGameProfile(Inno.Core.Settings.ProjectId projectId)`](../../build/pipeline/Inno.Build/BuildSettings.cs#L146) | Creates an isolated game profile from the current defaults.
`projectId`: The authoritative project identity copied into the game profile. | A newly owned profile that may be changed for one build without mutating these settings. | — | +| [`public Inno.Build.BuildSettings Inno.Build.BuildSettings.Copy()`](../../build/pipeline/Inno.Build/BuildSettings.cs#L165) | Creates an isolated copy suitable for a temporary export draft. | A newly owned settings object. | — | +| [`public Inno.Build.BuildSettings.BuildSettings()`](../../build/pipeline/Inno.Build/BuildSettings.cs#L14) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.BuildSettingsStore`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L13) | Persists project-owned export defaults independently from temporary export requests. | — | — | +| [`public Inno.Build.BuildSettingsStore.BuildSettingsStore(string path, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Build.BuildSettings defaultSettings)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L37) | Creates a store for one project's current build settings document.
`path`: The project-owned Settings.Build.inno path.
`serialization`: The active engine serialization registry.
`defaultSettings`: Canonical project defaults used while no document exists. | — | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when or is null. | +| [`public bool Inno.Build.BuildSettingsStore.exists { get; }`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L57) | Gets whether a project-owned build settings document exists. | — | — | +| [`public Inno.Build.BuildSettings Inno.Build.BuildSettingsStore.defaultSettings { get; }`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L69) | Gets an isolated copy of the canonical project defaults. | — | — | +| [`public Inno.Build.BuildSettings Inno.Build.BuildSettingsStore.Load()`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L87) | Loads the saved settings or returns an isolated copy of the canonical defaults. | A newly owned settings object. | `System.IO.InvalidDataException`: Thrown when the saved document is malformed. | +| [`public void Inno.Build.BuildSettingsStore.Save(Inno.Build.BuildSettings settings)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L108) | Atomically replaces the project-owned build defaults.
`settings`: The complete defaults to persist. | — | `System.ArgumentNullException`: Thrown when is null.
`System.IO.InvalidDataException`: Thrown when the settings document is malformed.
`System.IO.IOException`: Thrown when the candidate cannot be committed. | +| [`public byte[] Inno.Build.BuildSettingsStore.CaptureDocument()`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L122) | Captures the current effective build defaults as a native document. | A newly owned current-format document payload. | — | +| [`public void Inno.Build.BuildSettingsStore.RestoreDocument(System.ReadOnlySpan document)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L140) | Validates and atomically restores a native build settings document.
`document`: The complete document payload. | — | `System.IO.InvalidDataException`: Thrown when the document is malformed.
`System.IO.IOException`: Thrown when the document cannot be committed. | +| [`public void Inno.Build.BuildSettingsStore.ValidateDocument(System.ReadOnlySpan document)`](../../build/pipeline/Inno.Build/BuildSettingsStore.cs#L156) | Validates a native build settings document without changing the active file.
`document`: The document payload to inspect. | — | `System.IO.InvalidDataException`: Thrown when the document is malformed. | +| [`public readonly record struct Inno.Build.BuildTargetId : System.IEquatable`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Identifies one stable platform and architecture combination supported by the game build pipeline. | — | — | +| [`public Inno.Build.BuildTargetId.BuildTargetId(string value)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L19) | Creates a stable target identity.
`value`: A lowercase portable identifier composed of letters, numbers, and hyphens. | — | `System.ArgumentException`: Thrown when is not a portable target identity. | +| [`public string Inno.Build.BuildTargetId.value { get; }`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L33) | Gets the portable target identity. | — | — | +| [`public override string Inno.Build.BuildTargetId.ToString()`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L41) | Formats the portable target identity. | The exact stable target identity. | — | +| [`public static bool Inno.Build.BuildTargetId.operator !=(Inno.Build.BuildTargetId left, Inno.Build.BuildTargetId right)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Build.BuildTargetId.operator ==(Inno.Build.BuildTargetId left, Inno.Build.BuildTargetId right)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Build.BuildTargetId.GetHashCode()`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Build.BuildTargetId.Equals(object obj)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Build.BuildTargetId.Equals(Inno.Build.BuildTargetId other)`](../../build/pipeline/Inno.Build/BuildTargetId.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Build.GameBuildRequest`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L9) | Requests one game build from the current validated authoring generation. | — | — | +| [`public required Inno.Build.BuildProfile Inno.Build.GameBuildRequest.profile { get; init; }`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L14) | Gets or initializes the reusable product profile. | — | — | +| [`public required string Inno.Build.GameBuildRequest.outputDirectory { get; init; }`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L19) | Gets or initializes the parent directory that receives the atomically committed platform output. | — | — | +| [`public void Inno.Build.GameBuildRequest.Validate()`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L27) | Validates destination and product inputs without mutating output. | — | `System.IO.InvalidDataException`: Thrown when the request is incomplete. | +| [`public Inno.Build.GameBuildRequest.GameBuildRequest()`](../../build/pipeline/Inno.Build/GameBuildRequest.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.GameBuildContentContext`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L9) | Supplies isolated staging and profile data for target-specific offline artifact generation. | — | — | +| [`public Inno.Build.BuildProfile Inno.Build.GameBuildContentContext.profile { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L24) | Gets the validated product and target profile. | — | — | +| [`public string Inno.Build.GameBuildContentContext.outputDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L29) | Gets the empty staging directory that receives source-free target artifacts. | — | — | +| [`public Inno.Core.Serialization.SerializationGeneration Inno.Build.GameBuildContentContext.serialization { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildContentContext.cs#L34) | Gets the immutable converter generation pinned by the surrounding build transaction. | — | — | +| [`public sealed class Inno.Build.GameBuildPackageContext`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L8) | Supplies an isolated staging layout to a replaceable platform packager. | — | — | +| [`public Inno.Build.BuildProfile Inno.Build.GameBuildPackageContext.profile { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L27) | Gets the validated product profile. | — | — | +| [`public string Inno.Build.GameBuildPackageContext.supportPackDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L32) | Gets the verified read-only Support Pack directory for this target. | — | — | +| [`public string Inno.Build.GameBuildPackageContext.contentDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L37) | Gets the source-free packaged content directory to deploy. | — | — | +| [`public Inno.Build.Managed.ManagedDeploymentResult Inno.Build.GameBuildPackageContext.managedDeployment { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L42) | Gets the verified managed publication produced independently of platform packaging. | — | — | +| [`public string Inno.Build.GameBuildPackageContext.outputDirectory { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildPackageContext.cs#L47) | Gets the empty staging parent where the target must create exactly one output. | — | — | +| [`public sealed class Inno.Build.GameBuildTargetBinding`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L8) | Binds independent packaging and content implementations for one publication target. | — | — | +| [`public Inno.Build.GameBuildTargetBinding.GameBuildTargetBinding(Inno.Build.IGameBuildTarget packager, Inno.Build.IGameContentCompiler compiler)`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L25) | Validates a complete binding without starting tools or taking ownership of services.
`packager`: The borrowed platform packaging implementation.
`compiler`: The borrowed content implementation for the same target and authoring generation. | — | `System.ArgumentNullException`: Either implementation is null.
`System.ArgumentException`: A target is unassigned or the two implementation identities disagree. | +| [`public Inno.Build.IGameBuildTarget Inno.Build.GameBuildTargetBinding.packager { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L40) | Gets the platform implementation responsible for layout and Support Pack validation. | — | — | +| [`public Inno.Build.IGameContentCompiler Inno.Build.GameBuildTargetBinding.compiler { get; }`](../../build/pipeline/Inno.Build/Game/GameBuildTargetBinding.cs#L45) | Gets the content implementation borrowing this pipeline's authoring generation. | — | — | +| [`public interface Inno.Build.IGameBuildTarget : Inno.Build.IPlayerSupportPackValidator`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L10) | Defines the replaceable platform packaging boundary for one game build target. | — | — | +| [`Inno.Build.BuildTargetId Inno.Build.IGameBuildTarget.id { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L15) | Gets the stable target identity implemented by this packager. | — | — | +| [`Inno.Build.Managed.ManagedDeploymentId Inno.Build.IGameBuildTarget.defaultManagedDeployment { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L20) | Gets the managed publisher selected when the profile does not specify an override. | — | — | +| [`string Inno.Build.IGameBuildTarget.runtimeIdentifier { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L25) | Gets the managed toolchain target identifier required by this platform composition. | — | — | +| [`string Inno.Build.IGameBuildTarget.displayName { get; }`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L30) | Gets the user-facing target name presented by authoring hosts. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.IGameBuildTarget.PackageAsync(Inno.Build.GameBuildPackageContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Game/IGameBuildTarget.cs#L47) | Composes one platform output from a verified Support Pack and source-free content directory.
`context`: The isolated package context owned by the current build.
`cancellationToken`: The token that cancels work before the build is committed. | The single platform output path created beneath . | `System.OperationCanceledException`: Thrown when packaging is canceled. | +| [`public interface Inno.Build.IGameContentCompiler`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L9) | Compiles source-free runtime content independently of application packaging. | — | — | +| [`Inno.Build.BuildTargetId Inno.Build.IGameContentCompiler.target { get; }`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L14) | Gets the exact publication target whose content this compiler produces. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.IGameContentCompiler.CompileAsync(Inno.Build.GameBuildContentContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Game/IGameContentCompiler.cs#L31) | Writes target content into the current operation's isolated staging directory.
`context`: The borrowed authoring generation and operation-owned content destination.
`cancellationToken`: Cancels compilation before publication; associated work must finish before returning. | An operation completing after every required artifact is staged, or faulting on failure. | `System.OperationCanceledException`: Cancellation was requested before compilation completed. | +| [`public sealed class Inno.Build.PlatformTargetDescriptor`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L8) | Declares immutable product target facts independently of the machine executing build tools. | — | — | +| [`public Inno.Build.PlatformTargetDescriptor.PlatformTargetDescriptor(Inno.Build.BuildTargetId id, string platform, string architecture, string abi, string runtimeIdentifier, bool supportsEditor)`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L34) | Creates an explicit platform, processor, ABI and managed-runtime mapping.
`id`: The stable publication target identity.
`platform`: The platform package owning system integration.
`architecture`: The target processor architecture, such as x64, arm64 or wasm32.
`abi`: The native ABI selected by this platform implementation.
`runtimeIdentifier`: The exact managed toolchain runtime identifier.
`supportsEditor`: Whether this implemented target supports the shared Editor product. | — | `System.ArgumentException`: An identity or required target fact is unassigned. | +| [`public Inno.Build.BuildTargetId Inno.Build.PlatformTargetDescriptor.id { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L58) | Gets the stable publication target identity. | — | — | +| [`public string Inno.Build.PlatformTargetDescriptor.platform { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L63) | Gets the owning platform package. | — | — | +| [`public string Inno.Build.PlatformTargetDescriptor.architecture { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L68) | Gets the explicitly declared processor architecture. | — | — | +| [`public string Inno.Build.PlatformTargetDescriptor.abi { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L73) | Gets the explicitly selected native ABI. | — | — | +| [`public string Inno.Build.PlatformTargetDescriptor.runtimeIdentifier { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L78) | Gets the managed runtime mapping without deriving it from the target identity. | — | — | +| [`public bool Inno.Build.PlatformTargetDescriptor.supportsEditor { get; }`](../../build/pipeline/Inno.Build/PlatformTargetDescriptor.cs#L83) | Gets whether this target provides an implemented Editor host. | — | — | +| [`public interface Inno.Build.IPlayerSupportPackProvisioner`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackProvisioner.cs#L9) | Prepares current Player Support Pack inputs without coupling game builds to an SDK or platform toolchain. | — | — | +| [`System.Threading.Tasks.ValueTask Inno.Build.IPlayerSupportPackProvisioner.ProvisionAsync(Inno.Build.BuildTargetId target, string supportPackRoot, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackProvisioner.cs#L26) | Checks current source inputs and selects a complete immutable pack before the build resumes.
`target`: The platform and architecture target whose current inputs are required.
`supportPackRoot`: The catalog directory where the verified pack must be installed.
`cancellationToken`: Cancellation before the installation commits. | An operation that completes after the pack has been installed. | — | +| [`public interface Inno.Build.IPlayerSupportPackValidator`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackValidator.cs#L6) | Validates platform-owned runtime or link inputs without coupling the pack catalog to target names. | — | — | +| [`void Inno.Build.IPlayerSupportPackValidator.Validate(string directory)`](../../build/pipeline/Inno.Build/Player/IPlayerSupportPackValidator.cs#L17) | Rejects an incomplete or incompatible platform closure.
`directory`: The prepared or installed Support Pack directory. | — | `System.IO.InvalidDataException`: A required platform input is absent or incompatible. | +| [`public sealed class Inno.Build.PlayerSupportPackCatalog`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L17) | Resolves and validates installed Player Support Packs without loading authoring services. | — | — | +| [`public Inno.Build.PlayerSupportPackCatalog.PlayerSupportPackCatalog(string root)`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L49) | Creates a catalog rooted at the directory containing target-specific Player Support Packs.
`root`: The directory whose target children contain immutable fingerprint directories and a current index. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.PlayerSupportPackCatalog.SelectAsync(Inno.Build.BuildTargetId target, string fingerprint, Inno.Build.IPlayerSupportPackValidator validator, System.Func verifyInputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L85) | Validates one immutable content identity and atomically selects it for subsequent readers.
`target`: The explicit target whose prepared Pack is requested.
`fingerprint`: The exact lowercase SHA-256 content identity recorded by the publisher.
`validator`: The borrowed platform closure validator.
`verifyInputs`: The final awaited source-input gate after output validation and before selection.
`cancellationToken`: Cancels validation or selection before replacing the current index. | Reading ownership of the complete requested Pack; dispose it after every consumer finishes. | `System.ArgumentException`: The content identity is invalid or the target path escapes the catalog.
`System.IO.InvalidDataException`: The requested directory or its complete file set and hashes are invalid.
`System.OperationCanceledException`: Validation or selection was canceled. | +| [`public Inno.Build.PlayerSupportPackLease Inno.Build.PlayerSupportPackCatalog.Acquire(Inno.Build.BuildTargetId target, Inno.Build.IPlayerSupportPackValidator validator, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L144) | Resolves one installed Support Pack after validating its deployment-only closure.
`target`: The platform and architecture identity of the required Support Pack.
`validator`: The target-owned validator for runtime or linker inputs.
`cancellationToken`: Cancels ownership acquisition or complete content validation before returning a lease. | Reading ownership of the complete pack; dispose it after its final consumer finishes. | `System.IO.DirectoryNotFoundException`: Thrown when no Support Pack is installed for .
`System.IO.InvalidDataException`: Thrown when the pack is empty, contains build-time payload, or lacks its Player executable or native runtimes.
`System.OperationCanceledException`: Acquisition or validation was canceled; no reading ownership is transferred.
`System.TimeoutException`: Exclusive repair or retirement retains the artifact for more than thirty seconds. | +| [`public System.Threading.Tasks.ValueTask Inno.Build.PlayerSupportPackCatalog.PublishAsync(Inno.Build.BuildTargetId target, string stagingDirectory, Inno.Build.IPlayerSupportPackValidator validator, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), System.Func? verifyInputs = null)`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackCatalog.cs#L198) | Validates and publishes an immutable pack, then atomically selects it for new readers.
`target`: The platform and architecture owning the staged inputs.
`stagingDirectory`: A complete, exclusively owned staging tree on the same filesystem as this catalog. Successful publication consumes this directory unless identical inputs already exist.
`validator`: The target validator for runtime and linker inputs.
`cancellationToken`: Cancels validation or lease acquisition before selecting the completed pack.
`verifyInputs`: Optional final source-input gate after output validation and any publication wait. Omit only when the caller exclusively owns a fully prepared tree with no external preparation inputs. | Reading ownership of the selected immutable pack; earlier readers retain their own protection. | `System.IO.InvalidDataException`: The candidate is incomplete or contains forbidden inputs.
`System.IO.IOException`: The completed directory or its atomic current index cannot be installed.
`System.OperationCanceledException`: Cancellation occurred before the current index was committed. | +| [`public sealed class Inno.Build.PlayerSupportPackLease : System.IDisposable`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L10) | Keeps verified Player compilation and linker inputs alive until their consumer finishes. | — | — | +| [`public string Inno.Build.PlayerSupportPackLease.directory { get; }`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L29) | Gets the verified absolute directory while this lease owns reading rights. | — | `System.ObjectDisposedException`: The consumer has already released its reading rights. | +| [`public void Inno.Build.PlayerSupportPackLease.Dispose()`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L42) | Releases reading rights after all compilation, copying and linking have completed. Repeated disposal has no effect. | — | — | +| [`public sealed class Inno.Build.PluginBuildRequest`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L11) | Requests one deterministic project-to-Plugin package without a companion authoring asset. | — | — | +| [`public required string Inno.Build.PluginBuildRequest.pluginId { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L16) | Gets or initializes the globally stable lowercase Plugin identity. | — | — | +| [`public required string Inno.Build.PluginBuildRequest.displayName { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L21) | Gets or initializes the user-facing Plugin name. | — | — | +| [`public required string Inno.Build.PluginBuildRequest.outputPath { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L26) | Gets or initializes the destination .iplugin package path. | — | — | +| [`public bool Inno.Build.PluginBuildRequest.includeDependencies { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L31) | Gets or initializes whether the complete transitive dependency closure is embedded. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Build.PluginBuildRequest.dependencies { get; init; }`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L37) | Gets or initializes the installed plugins this package actually requires. Their transitive dependencies are included in the package manifest. | — | — | +| [`public void Inno.Build.PluginBuildRequest.Validate()`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L45) | Validates package identity and destination syntax. | — | `System.IO.InvalidDataException`: Thrown when the request cannot identify a portable Plugin package. | +| [`public Inno.Build.PluginBuildRequest.PluginBuildRequest()`](../../build/pipeline/Inno.Build/PluginBuildRequest.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Build.SdkScriptCompilationEnvironment : Inno.Scripting.Compiler.IScriptCompilationEnvironmentProvider`](../../build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs#L17) | Evaluates the authoring project's selected SDK and maps its framework contract into scripting inputs. | — | — | +| [`public Inno.Build.SdkScriptCompilationEnvironment.SdkScriptCompilationEnvironment(string dotnetHost, string projectRoot)`](../../build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs#L34) | Creates a host-owned input provider without selecting installed reference packs independently.
`dotnetHost`: The .NET host executable selected by the composition root.
`projectRoot`: The authoring project whose ancestor SDK configuration governs compilation and IDE use. | — | `System.ArgumentException`: A host or project location is empty. | +| [`public Inno.Scripting.Compiler.ScriptCompilationInputs Inno.Build.SdkScriptCompilationEnvironment.Capture(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../build/pipeline/Inno.Build/Scripting/SdkScriptCompilationEnvironment.cs#L45) | Captures current SDK inputs without choosing a framework inside the scripting compiler.
`cancellationToken`: Cancels input preparation before any script artifact is published. | Complete immutable inputs; missing or inconsistent SDK inputs must fail explicitly. | — | ## 项目依赖 -- [Inno.Content](../assets/Inno.Content.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization.Generators](../core/Inno.Core.Serialization.Generators.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Plugins.Authoring](../plugins/Inno.Plugins.Authoring.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.IO](../core/Inno.Core.IO.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Runtime](../runtime/Inno.Runtime.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scene.Assets](../scene/Inno.Scene.Assets.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Compiler](../scripting/Inno.Scripting.Compiler.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Plugins](../plugins/Inno.Plugins.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Build.Managed](Inno.Build.Managed.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 - -### `Inno.Build.PlayerSupportPackLease` - - - - - -| 声明 | 行为与结果 | 所有权/线程 | 失败或限制 | -| --- | --- | --- | --- | -| [`public void Inno.Build.PlayerSupportPackLease.Dispose()`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L42) | Releases reading rights after all compilation, copying and linking have completed. Repeated disposal has no effect. | 当前实例唯一拥有本次读取权;可重复 Dispose。 | 释放后不能再取得 directory;不接管调用者进程。 | -| [`public string Inno.Build.PlayerSupportPackLease.directory { get; }`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L29) | Gets the verified absolute directory while this lease owns reading rights. | 当前实例唯一拥有本次读取权;可重复 Dispose。 | 释放后不能再取得 directory;不接管调用者进程。 | -| [`Inno.Build.PlayerSupportPackLease`](../../build/pipeline/Inno.Build/Player/PlayerSupportPackLease.cs#L10) | Keeps verified Player compilation and linker inputs alive until their consumer finishes. | 当前实例唯一拥有本次读取权;可重复 Dispose。 | 释放后不能再取得 directory;不接管调用者进程。 | +| 工程 | 边界 | +| --- | --- | +| `Inno.Build.Toolchains` | 实现依赖;不传递 compile surface | +| `Inno.Content` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Reload` | 公开签名依赖 | +| `Inno.Core.Serialization.Generators` | 编译器 Analyzer;不进入运行时 | +| `Inno.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Plugins.Authoring` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Types` | 实现依赖;不传递 compile surface | +| `Inno.Core.IO` | 实现依赖;不传递 compile surface | +| `Inno.Core.Settings` | 实现依赖;不传递 compile surface | +| `Inno.Runtime` | 实现依赖;不传递 compile surface | +| `Inno.Scene.Assets` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Compiler` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Plugins` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Build.Managed` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Assets.ImGui.md b/docs/editor/Inno.Editor.Assets.ImGui.md index 8a918bd24..f1ecc0e5b 100644 --- a/docs/editor/Inno.Editor.Assets.ImGui.md +++ b/docs/editor/Inno.Editor.Assets.ImGui.md @@ -10,10 +10,14 @@ Asset 浏览呈现与图标发现。依赖 Assets 编辑 feature、Inspection、 产品通过 `EditorProduct.props` 显式部署此程序集。由当前 TypeRegistry/Editor Registry 发现声明并在候选成功后切换;不扫描目录、不建立独立注册表或全局状态。 +标准产品组合 Workbench 的默认外观设置贡献。嵌入 Host 也必须组合所需的图标设置贡献;缺少 `Editor/Appearance/Icons/Folder` 等必需设置时明确失败,不从另一个 owner 隐式补齐配置。 + ## API 与扩展点 AssetBrowserState、AssetBrowserRoot、AssetIconAttribute 和 AssetIconRegistry 提供当前浏览位置、来源、图标声明与查询。AssetBrowserModule 组合图标及无界面的 AssetEditingModule;BasicAssetInspectionDrawer 只做显示。 +`AssetBrowserStyle(EditorStyleMetrics)` 在一次绘制前冻结当前用户 Zoom,不保留 Session,也不再次乘上 DPI。默认值使用 100% Zoom。它包含 Browser 图标、网格、面包屑及列比例;通用 pane/splitter/toolbar 指标仍由共享 metrics 提供。尺度在帧边界更新后,调用方重新取得值快照。 + ## 工作流 使用逻辑 namespace `InnoEditor.Assets` 声明 AssetIconAttribute;修改源内容时调用 Assets feature,不能直接写安装包。 @@ -60,6 +64,33 @@ AssetBrowserState、AssetBrowserRoot、AssetIconAttribute 和 AssetIconRegistry + + + + + + + + + + + + + + + + + + + + + + + + + + + | API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | @@ -95,13 +126,40 @@ AssetBrowserState、AssetBrowserRoot、AssetIconAttribute 和 AssetIconRegistry | [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.Select(Inno.Editor.Core.EditorContext context, string? relativePath)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L158) | Selects an asset path through the editor-wide selection state, or clears Asset selection.
`context`: The shared editor context whose selection should be updated.
`relativePath`: The source-relative path to select, or an empty value to clear selection. | — | `System.ArgumentNullException`: Thrown when is . | | [`public string Inno.Editor.Assets.ImGui.AssetBrowserState.GetDirectory(Inno.Editor.Assets.ImGui.AssetBrowserRoot targetRoot)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L184) | Gets the last visited isolated directory for a navigation root.
`targetRoot`: The root whose independent navigation position is requested. | The source-relative directory, or an empty string for the root. | — | | [`public void Inno.Editor.Assets.ImGui.AssetBrowserState.SetLocation(Inno.Editor.Assets.ImGui.AssetBrowserRoot targetRoot, string directory)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Navigation/AssetBrowserState.cs#L207) | Changes the displayed source root and its isolated navigation directory.
`targetRoot`: The navigation root that owns the requested directory.
`directory`: The isolated source-relative directory; empty selects the root. | — | `System.ArgumentException`: The directory belongs to a different source root. | +| [`public readonly struct Inno.Editor.Assets.ImGui.AssetBrowserStyle`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L11) | Freezes Browser-specific layout values from the shared UI scale without retaining a session. The default value uses the standard 100 percent scale. | — | — | +| [`public Inno.Editor.Assets.ImGui.AssetBrowserStyle.AssetBrowserStyle(Inno.Editor.ImGui.EditorStyleMetrics metrics)`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L24) | Captures the current shared scale for one Browser presentation pass.
`metrics`: The existing session metrics; later mutations do not change this value. | — | `System.ArgumentNullException`: The metrics are null. | +| [`public System.Numerics.Vector2 Inno.Editor.Assets.ImGui.AssetBrowserStyle.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L33) | Gets asset browser window padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Assets.ImGui.AssetBrowserStyle.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L38) | Gets asset browser cell padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Assets.ImGui.AssetBrowserStyle.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L43) | Gets asset browser item spacing. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.frameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L48) | Gets asset browser frame rounding. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.breadcrumbHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L53) | Gets asset breadcrumb bar height. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridMinimumCellSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L58) | Gets minimum asset grid cell size. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L63) | Gets asset grid cell padding. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridDefaultScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L68) | Gets default asset grid scale. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridMinimumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L73) | Gets minimum asset grid scale. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridMaximumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L78) | Gets maximum asset grid scale. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L83) | Gets spacing between asset list rows. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listNameSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L88) | Gets the default normalized position of the asset list name/type separator. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listTypeSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L93) | Gets the default normalized position of the asset list type/source separator. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listMinimumColumnRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L98) | Gets the minimum normalized width reserved for each asset list column. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listSeparatorHitWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L103) | Gets the horizontal hit width of an asset list column separator. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.listContentHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L108) | Gets the horizontal inset between an asset list separator and column content. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridLabelHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L113) | Gets horizontal padding for asset grid labels. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridLabelBottomPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L118) | Gets bottom padding for asset grid labels. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridLabelLineSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L123) | Gets the additional vertical spacing between asset grid label lines. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridFixedCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L128) | Gets fixed padding added to calculated asset grid cells. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridScaleBias { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L133) | Gets the scale bias added to asset grid icons. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridIconTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L138) | Gets the top inset reserved above an asset grid icon. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridIconHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L143) | Gets the horizontal inset that constrains an asset grid icon inside its card. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.gridIconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L148) | Gets the vertical spacing between an asset grid icon and its label. | — | — | +| [`public float Inno.Editor.Assets.ImGui.AssetBrowserStyle.breadcrumbSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs#L153) | Gets spacing around breadcrumb separators. | — | — | ## 项目依赖 | 工程 | 边界 | | --- | --- | | `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | -| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 公开签名依赖 | | `Inno.Assets.Pipeline` | 公开签名依赖 | | `Inno.Core.Serialization` | 公开签名依赖 | | `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.Core.md b/docs/editor/Inno.Editor.Core.md index 8bdaf36ed..910ec39a1 100644 --- a/docs/editor/Inno.Editor.Core.md +++ b/docs/editor/Inno.Editor.Core.md @@ -570,9 +570,9 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 | [`public int Inno.Editor.Core.EditorStatistic.groupOrder { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L170) | Gets ascending group presentation order. | — | — | | [`public int Inno.Editor.Core.EditorStatistic.order { get; }`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L175) | Gets ascending metric order within the group. | — | — | | [`public sealed class Inno.Editor.Core.EditorStatistics`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | Exchanges frame-scoped, reload-safe statistics between independent editor features and viewers. | — | — | -| [`public void Inno.Editor.Core.EditorStatistics.Publish(Inno.Editor.Core.EditorStatistic statistic)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L200) | Publishes or replaces one statistic for the current editor frame.
`statistic`: Immutable contribution containing no runtime provider references. | — | `System.ArgumentNullException`: Thrown when is . | -| [`public void Inno.Editor.Core.EditorStatistics.Publish(System.Collections.Generic.IEnumerable statistics)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L216) | Publishes or replaces several statistics for the current editor frame.
`statistics`: Contributions to publish in enumeration order. | — | `System.ArgumentNullException`: Thrown when or one of its values is null. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorStatistics.GetSnapshot()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L230) | Gets a deterministic snapshot combining current contributions with the previous completed frame for providers that draw after their consumer. | A detached, sorted snapshot containing at most one value for each stable statistic ID. | — | +| [`public void Inno.Editor.Core.EditorStatistics.Publish(Inno.Editor.Core.EditorStatistic statistic)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L202) | Publishes or replaces one statistic for the current editor frame.
`statistic`: Immutable contribution containing no runtime provider references. | — | `System.ArgumentNullException`: Thrown when is . | +| [`public void Inno.Editor.Core.EditorStatistics.Publish(System.Collections.Generic.IEnumerable statistics)`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L223) | Publishes or replaces several statistics for the current editor frame.
`statistics`: Contributions to publish in enumeration order. | — | `System.ArgumentNullException`: Thrown when or one of its values is null. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Core.EditorStatistics.GetSnapshot()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L238) | Gets a deterministic snapshot combining current contributions with the previous completed frame for providers that draw after their consumer. | An immutable, sorted snapshot containing at most one value for each stable statistic ID. Unchanged content reuses the same snapshot; previously returned snapshots remain valid. | — | | [`public Inno.Editor.Core.EditorStatistics.EditorStatistics()`](../../src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorStatistics.cs#L185) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 diff --git a/docs/editor/Inno.Editor.Graph.ImGui.md b/docs/editor/Inno.Editor.Graph.ImGui.md index 434ce42d8..cd4b8b80e 100644 --- a/docs/editor/Inno.Editor.Graph.ImGui.md +++ b/docs/editor/Inno.Editor.Graph.ImGui.md @@ -12,12 +12,18 @@ ## API 与扩展点 -GraphCanvasView 组合当前 GraphCanvasState、画布原点和尺寸,提供 ToScreen、ToGraph、DrawGrid、DrawConnection 和 HitConnection。它不修改 graph,也不拥有 History、选择或资源。 +`GraphCanvasView` 在构造时复制当前 pan、zoom、画布原点和尺寸;同一帧的投影不会因后续导航修改而漂移。它提供节点、端口、连线、命中与框选的通用呈现,不解释 Shader 或其他图领域语义。 + +`GraphCanvasLayout` 保留由稳定图 ID、位置、尺寸、文字与端口组成的中立快照。`SetNodes` 原子校验候选,按值复用未变化的节点;平移、缩放和拖动预览不要求重建端口几何。长期状态不能包含领域实例、插件 Type 或绘制 callback。 + +`GraphCanvasPointerController` 只管理选择与当前手势的中立预览。`Step` 接收已经完成焦点、遮挡和导航路由的输入,返回移动或连线意图;调用方负责校验、原子领域修改及既有 History。取消不修改文档,完成移动只有一个逻辑 Undo。Shader 消费方在保存、关闭、Play 或 reload 前通过现有 Core Events 操作准备通知完成尚未提交的预览;失败保留可重试输入。 ## 工作流 每次绘制按当前 viewport 创建值对象;同一连接曲线用于绘制与命中测试。屏幕坐标只在当前帧有效。 +`DrawNode` 的自定义内容只在节点与画布相交时借用执行,返回前恢复本次 clip;异常继续传播。调用方应在该次回调中配对自己的样式和其他作用域。没有原生 UI 帧时仍可调用纯几何、命中及指针契约;实际绘制要求有效的 owner thread、Session 和 ImGui Context。 + ## 错误、生命周期与热重载 控件只在当前 EditorUiSession 的 owner thread、作用域与原生上下文中使用。跨帧只保留稳定 ID 和中立状态;Missing、只读和未赋值状态保持区别。手势失败保留 History 所有权和诊断;扩展在所属 generation 退休,不保留插件 Type、delegate 或实例。 @@ -28,23 +34,139 @@ GraphCanvasView 组合当前 GraphCanvasState、画布原点和尺寸,提供 T 此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | +| [`public sealed class Inno.Editor.Graph.ImGui.GraphCanvasLayout`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs#L12) | Owns a document's neutral node display snapshot, independently of viewport transforms and domain instances. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.ImGui.GraphCanvasLayout.nodes { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs#L20) | Gets nodes in draw order; reverse order determines overlapping-node hit priority. | — | — | +| [`public int Inno.Editor.Graph.ImGui.GraphCanvasLayout.SetNodes(System.Collections.Generic.IEnumerable nodes)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs#L37) | Atomically replaces a complete display snapshot, reusing unchanged node geometry by value.
`nodes`: The current neutral node descriptions in draw order. | The number of inserted, changed or removed nodes. Ordering changes do not rebuild geometry. | `System.ArgumentNullException`: The collection or one of its nodes is null.
`System.ArgumentException`: A node identity occurs more than once; the previous snapshot remains active. | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasLayout.TryGetNode(Inno.Core.Graphs.GraphNodeId id, out Inno.Editor.Graph.ImGui.GraphCanvasNode? node)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs#L74) | Resolves a node in this display snapshot without consulting domain or live-object registries.
`id`: The graph node identity to resolve.
`node`: The current neutral node, or null when it is absent. | True when the current snapshot contains the node. | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasLayout.GraphCanvasLayout()`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Editor.Graph.ImGui.GraphCanvasNode`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L12) | Freezes one node's geometric and textual presentation without owning its domain record. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasNode.GraphCanvasNode(Inno.Core.Graphs.GraphNodeId id, Inno.Core.Graphs.GraphPosition position, System.Numerics.Vector2 size, float headerHeight, string title, System.Collections.Generic.IEnumerable ports)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L44) | Creates immutable display data that can safely outlive the extension that described it.
`id`: The existing graph node identity.
`position`: The node's document position in graph coordinates.
`size`: Finite positive width and height in graph coordinates.
`headerHeight`: The header extent, within the node height.
`title`: A display title already interpreted by its domain owner.
`ports`: Immutable neutral endpoint descriptions; the collection is copied. | — | `System.ArgumentNullException`: The title or port collection is null.
`System.ArgumentException`: A node or port identity is invalid, duplicated or owned by a different node.
`System.ArgumentOutOfRangeException`: The node's geometry is nonfinite or has an invalid extent. | +| [`public Inno.Core.Graphs.GraphNodeId Inno.Editor.Graph.ImGui.GraphCanvasNode.id { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L76) | Gets the stable graph identity represented by this display snapshot. | — | — | +| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasNode.position { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L81) | Gets the document position, before transient gesture previews. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasNode.size { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L86) | Gets the unscaled node extent. | — | — | +| [`public float Inno.Editor.Graph.ImGui.GraphCanvasNode.headerHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L91) | Gets the unscaled header extent used for node movement hit testing. | — | — | +| [`public string Inno.Editor.Graph.ImGui.GraphCanvasNode.title { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L96) | Gets the domain-supplied display title. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.ImGui.GraphCanvasNode.ports { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs#L101) | Gets copied neutral port descriptions; no callback or domain object is retained. | — | — | +| [`public sealed class Inno.Editor.Graph.ImGui.GraphCanvasPointerController`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L14) | Coordinates neutral selection, movement previews, box selection and connection intents for one canvas. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasPointerController.GraphCanvasPointerController(Inno.Editor.Graph.GraphCanvasState state)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L34) | Creates a pointer owner over existing document navigation and selection state.
`state`: The existing canvas state, which remains the selection and pending-connection authority. | — | `System.ArgumentNullException`: The state is null. | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Editor.Graph.ImGui.GraphCanvasPointerController.previewPositions { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L44) | Gets frame-local movement previews; callers must not persist this changing view. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerController.isMoving { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L49) | Gets whether a node movement preview is active. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerController.isBoxSelecting { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L54) | Gets whether a selection box is active. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasPointerController.boxStart { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L59) | Gets the initial screen-space corner of the current selection box. | — | — | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasPointerController.Cancel()`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L64) | Cancels transient presentation state without applying or undoing document changes. | — | — | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasPointerController.BeginMove(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Collections.Generic.IEnumerable nodes, System.Numerics.Vector2 pointer)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L85) | Starts movement for explicit node IDs, supporting domain-owned group-header composition.
`layout`: The neutral snapshot supplying initial positions.
`nodes`: Selected stable graph identities; absent nodes remain selected but are not moved.
`pointer`: The screen-space gesture start. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasPointerResult Inno.Editor.Graph.ImGui.GraphCanvasPointerController.Step(Inno.Editor.Graph.ImGui.GraphCanvasView view, Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, Inno.Editor.Graph.ImGui.GraphCanvasPointerInput input)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs#L121) | Consumes one routed frame and returns data-editing intents without invoking domain code.
`view`: The current canvas projection.
`layout`: The immutable neutral node snapshot used by drawing and hit testing.
`input`: Current pointer input after the caller's focus, navigation and overlay routing. | Completed neutral intents. The feature owns validation, atomic mutation and History recording. | `System.ArgumentOutOfRangeException`: The pointer position is nonfinite. | +| [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasPointerInput`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L8) | Carries one frame's already-routed pointer input without depending on a native backend. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.position { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L13) | Gets the screen-space pointer position. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.isHovered { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L18) | Gets whether this canvas owns the pointer for starting an interaction. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.isPressed { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L23) | Gets whether the primary pointer button was pressed this frame. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.isDown { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L28) | Gets whether the primary pointer button remains held. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.isReleased { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L33) | Gets whether the primary pointer button was released this frame. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.shift { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L38) | Gets whether selection extension is requested. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.cancel { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L43) | Gets whether the current preview must be canceled without modifying document data. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPointerInput.readOnly { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs#L48) | Gets whether selection is allowed while data-editing intents are prohibited. | — | — | +| [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasPointerResult`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L9) | Reports neutral interaction intents for the feature controller to validate and apply through History. | — | — | +| [`public Inno.Core.Graphs.GraphNodeId? Inno.Editor.Graph.ImGui.GraphCanvasPointerResult.nodePressed { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L28) | Gets the pressed node header, or null when no header was pressed. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary? Inno.Editor.Graph.ImGui.GraphCanvasPointerResult.movedNodes { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L33) | Gets completed movement positions, or null when no data edit was requested. | — | — | +| [`public Inno.Core.Graphs.GraphEndpoint? Inno.Editor.Graph.ImGui.GraphCanvasPointerResult.output { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L38) | Gets a requested connection's output endpoint; its domain type still requires validation. | — | — | +| [`public Inno.Core.Graphs.GraphEndpoint? Inno.Editor.Graph.ImGui.GraphCanvasPointerResult.input { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L43) | Gets a requested connection's input endpoint, paired with the output endpoint. | — | — | +| [`public Inno.Core.Graphs.GraphEndpoint? Inno.Editor.Graph.ImGui.GraphCanvasPointerResult.releasedOutput { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs#L48) | Gets an output released over empty canvas, allowing the feature to offer a creation workflow. | — | — | +| [`public readonly record struct Inno.Editor.Graph.ImGui.GraphCanvasPort : System.IEquatable`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Describes a neutral port's node-relative geometry and display state without retaining extensions. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasPort.GraphCanvasPort(Inno.Core.Graphs.GraphEndpoint endpoint, Inno.Core.Graphs.GraphPortDirection direction, System.Numerics.Vector2 position, string label, bool isOptional = false, bool isMissing = false, (System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum)? labelBounds = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L45) | Creates a port presentation whose position is measured in unscaled node coordinates.
`endpoint`: The existing document endpoint represented by this port.
`direction`: The connection direction, independent of any domain-specific type system.
`position`: Node-relative graph coordinates of the port center.
`label`: The current display label, without domain objects or callbacks.
`isOptional`: Whether to distinguish an optional input visually.
`isMissing`: Whether retained document structure currently lacks its domain implementation.
`labelBounds`: Optional node-relative label bounds used by drawing and row hit testing; null limits hits to the socket. | — | `System.ArgumentException`: An endpoint identity is empty.
`System.ArgumentNullException`: The label is null.
`System.ArgumentOutOfRangeException`: The position or direction is invalid. | +| [`public Inno.Core.Graphs.GraphEndpoint Inno.Editor.Graph.ImGui.GraphCanvasPort.endpoint { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L77) | Gets the document endpoint; it does not identify a live engine object. | — | — | +| [`public Inno.Core.Graphs.GraphPortDirection Inno.Editor.Graph.ImGui.GraphCanvasPort.direction { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L82) | Gets the neutral connection direction. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasPort.position { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L87) | Gets the node-relative center in graph coordinates. | — | — | +| [`public string Inno.Editor.Graph.ImGui.GraphCanvasPort.label { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L92) | Gets the current display label. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPort.isOptional { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L97) | Gets whether the input is visually optional. | — | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPort.isMissing { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L102) | Gets whether this retained endpoint currently lacks an implementation. | — | — | +| [`public (System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum)? Inno.Editor.Graph.ImGui.GraphCanvasPort.labelBounds { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L107) | Gets optional node-relative label geometry shared by rendering and row hit testing. | — | — | +| [`public override string Inno.Editor.Graph.ImGui.GraphCanvasPort.ToString()`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasPort.operator !=(Inno.Editor.Graph.ImGui.GraphCanvasPort left, Inno.Editor.Graph.ImGui.GraphCanvasPort right)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasPort.operator ==(Inno.Editor.Graph.ImGui.GraphCanvasPort left, Inno.Editor.Graph.ImGui.GraphCanvasPort right)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Editor.Graph.ImGui.GraphCanvasPort.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Editor.Graph.ImGui.GraphCanvasPort.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPort.Equals(Inno.Editor.Graph.ImGui.GraphCanvasPort other)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | | [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasView`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L11) | Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. | — | — | -| [`public Inno.Editor.Graph.ImGui.GraphCanvasView.GraphCanvasView(Inno.Editor.Graph.GraphCanvasState state, System.Numerics.Vector2 origin, System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L31) | Creates a frame-local graph presenter over navigation owned by its document.
`state`: The existing neutral navigation and selection state.
`origin`: The screen-space canvas origin.
`size`: The finite nonnegative canvas extent. | — | `System.ArgumentOutOfRangeException`: The canvas geometry is invalid. | -| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasView.ToScreen(Inno.Core.Graphs.GraphPosition point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L55) | Projects a graph-space location into the current screen rectangle.
`point`: The domain-independent graph coordinate. | The corresponding screen coordinate. | — | -| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasView.ToGraph(System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L65) | Converts a screen location back into graph coordinates.
`point`: The screen coordinate to project. | The graph coordinate under the pointer. | — | -| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawGrid(uint color, float spacing = 32)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L78) | Draws a bounded graph-space grid through the current UI drawing context.
`color`: The packed presentation color.
`spacing`: Positive graph-space grid spacing. | — | `System.ArgumentOutOfRangeException`: Spacing is nonpositive or nonfinite. | -| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawConnection(System.Numerics.Vector2 output, System.Numerics.Vector2 input, uint color, float thickness = 2)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L106) | Draws a horizontal-tangent connection using the same curve used for hit testing.
`output`: The output endpoint in screen space.
`input`: The input endpoint in screen space.
`color`: The packed connection color.
`thickness`: The positive screen-space stroke width. | — | — | -| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasView.HitConnection(System.Numerics.Vector2 point, System.Numerics.Vector2 output, System.Numerics.Vector2 input, float radius = 6)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L137) | Checks pointer distance against the displayed cubic connection without any UI or document access.
`point`: The screen-space pointer location.
`output`: The displayed output endpoint.
`input`: The displayed input endpoint.
`radius`: Nonnegative screen-space acceptance radius. | True when the pointer is within the curve's hit area, including its endpoints. | `System.ArgumentOutOfRangeException`: Radius is negative or nonfinite. | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasView.GraphCanvasView(Inno.Editor.Graph.GraphCanvasState state, System.Numerics.Vector2 origin, System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L33) | Creates a frame-local graph presenter over navigation owned by its document.
`state`: The existing neutral navigation and selection state.
`origin`: The screen-space canvas origin.
`size`: The finite nonnegative canvas extent. | — | `System.ArgumentOutOfRangeException`: The canvas geometry is invalid. | +| [`public float Inno.Editor.Graph.ImGui.GraphCanvasView.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L55) | Gets the graph-to-screen zoom captured for this frame, independently of later navigation changes. | — | `System.InvalidOperationException`: The view was not initialized through its constructor. | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasView.ToScreen(Inno.Core.Graphs.GraphPosition point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L66) | Projects a graph-space location into the current screen rectangle.
`point`: The domain-independent graph coordinate. | The corresponding screen coordinate. | — | +| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasView.ToGraph(System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L76) | Converts a screen location back into graph coordinates.
`point`: The screen coordinate to project. | The graph coordinate under the pointer. | — | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawGrid(uint color, float spacing = 32)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L89) | Draws a bounded graph-space grid through the current UI drawing context.
`color`: The packed presentation color.
`spacing`: Positive graph-space grid spacing. | — | `System.ArgumentOutOfRangeException`: Spacing is nonpositive or nonfinite. | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawConnection(System.Numerics.Vector2 output, System.Numerics.Vector2 input, uint color, float thickness = 2)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L120) | Draws a horizontal-tangent connection using the same curve used for hit testing.
`output`: The output endpoint in screen space.
`input`: The input endpoint in screen space.
`color`: The packed connection color.
`thickness`: The positive screen-space stroke width. | — | `System.ArgumentOutOfRangeException`: An endpoint or derived control point is nonfinite, or the stroke width is not positive and finite. | +| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasView.HitConnection(System.Numerics.Vector2 point, System.Numerics.Vector2 output, System.Numerics.Vector2 input, float radius = 6)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L177) | Checks pointer distance against the displayed cubic connection without any UI or document access.
`point`: The screen-space pointer location.
`output`: The displayed output endpoint.
`input`: The displayed input endpoint.
`radius`: Nonnegative screen-space acceptance radius. | True when the pointer is within the curve's hit area, including its endpoints. | `System.ArgumentOutOfRangeException`: Radius is negative or nonfinite. | +| [`public (System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum) Inno.Editor.Graph.ImGui.GraphCanvasView.NodeBounds(Inno.Editor.Graph.ImGui.GraphCanvasNode node, Inno.Core.Graphs.GraphPosition? previewPosition = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L27) | Projects neutral node geometry, optionally substituting a transient drag position.
`node`: The immutable display node.
`previewPosition`: An uncommitted gesture position, or null to use the document position. | The screen-space node bounds. | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasNode? Inno.Editor.Graph.ImGui.GraphCanvasView.HitNode(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L48) | Finds the frontmost node beneath a pointer using the same geometry as drawing.
`layout`: The current document presentation snapshot.
`point`: The screen-space pointer. | The frontmost neutral node, or null over empty canvas. | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasPort? Inno.Editor.Graph.ImGui.GraphCanvasView.HitPort(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 point, float radius = 8)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L81) | Finds a port by its displayed socket or label row, without interpreting its domain type.
`layout`: The current document presentation snapshot.
`point`: The screen-space pointer.
`radius`: The nonnegative minimum socket hit radius in screen pixels. | The hit neutral port, including Missing state, or null over non-port content. | `System.ArgumentOutOfRangeException`: The radius is negative or nonfinite. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.ImGui.GraphCanvasView.BoxNodes(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 first, System.Numerics.Vector2 second)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L124) | Finds nodes intersecting a screen-space selection rectangle in document draw order.
`layout`: The current neutral display snapshot.
`first`: One corner of the selection box.
`second`: The opposite corner, in any drag direction. | Stable graph node IDs; no live domain objects are returned. | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasView.DrawNode(Inno.Editor.Graph.ImGui.GraphCanvasNode node, uint headerColor, bool selected, System.Action? drawContent = null, Inno.Core.Graphs.GraphPosition? previewPosition = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L164) | Draws a visible neutral node and its ports, borrowing optional content only for this call.
`node`: The immutable geometric and textual node input.
`headerColor`: The domain-selected packed header color; the presenter does not classify node types.
`selected`: Whether to display the current selection outline.
`drawContent`: Optional frame-local custom drawing. Exceptions propagate after restoring the node clip.
`previewPosition`: An uncommitted movement position, or null to draw the document position. | True when the node intersects the canvas and effective window clip and was submitted; false when culled. | — | ## 项目依赖 diff --git a/docs/editor/Inno.Editor.ImGui.md b/docs/editor/Inno.Editor.ImGui.md index 420257e10..4061427b5 100644 --- a/docs/editor/Inno.Editor.ImGui.md +++ b/docs/editor/Inno.Editor.ImGui.md @@ -63,13 +63,19 @@ Inno.Editor.ImGui/ └─ ... ``` -Palette 与 Style Metrics 并列位于 `Styling`,runtime host 与三个表现桥统一位于 `Runtime`,不再人为拆分只有三个文件的 Renderers 层。它们仍使用项目 namespace `Inno.Editor.ImGui`。所有 `ImGuiWidget.*.cs` 位于 `Widgets`,namespace 统一为 `Inno.Editor.ImGui.ImGuiWidget`;实现统一组成 `static partial ImGuiWidget`,可复用入口全部是 static 方法。Options、Result、presentation 与私有状态收口在对应的 `ImGuiWidget..cs` 中,不创建独立 Widget helper 文件。 +Palette 与 Style Metrics 并列位于 `Styling`,runtime host 与三个表现桥统一位于 `Runtime`,不再人为拆分只有三个文件的 Renderers 层。它们仍使用项目 namespace `Inno.Editor.ImGui`。所有 `ImGuiWidget.*.cs` 位于 `Widgets`,namespace 统一为 `Inno.Editor.ImGui.ImGuiWidget`;实现统一组成 Session 所有的 `sealed partial ImGuiWidget`,可复用入口由当前 `EditorUiSession.widgets` 提供。Options、Result、presentation 与私有状态收口在对应的 `ImGuiWidget..cs` 中,不创建独立 Widget helper 文件。 ## Palette 与 Style -所有主题颜色集中在 `EditorPalette`:原生 ImGui col、Inspector、Hierarchy、Asset Browser、Logging、轴颜色与 drag target 都不在 Panel 中声明。换主题只需替换这一个 palette surface。 +`EditorPalette` 只提供通用内容、选择、错误和控件表面的语义色。Browser 的图标/网格/列布局由 `Inno.Editor.Assets.ImGui.AssetBrowserStyle` 拥有;Shader 节点分类及其颜色由 Shader 呈现 owner 解释。通用层不维护资产或 Shader 类型名单。 -所有跨 Panel 的像素布局、padding、spacing、rounding、列比例和最小尺寸集中在 `EditorUiSession.current.widgets.style`(`EditorStyleMetrics`)。Panel 可以读取语义名,例如 `panelTabFramePadding`、`sectionHeaderPadding`、`inspectorSectionPadding`、`inspectorSectionRounding`、`inspectorCollapsedSectionCapLength`、`propertyMetadataSpacing`、`assetListNameSeparatorPosition`、`inspectorCardSpacing`、`hierarchyItemSpacing`、`hierarchyRenameMinimumWidth` 与 `settingsFieldPadding`,不应新增散落的固定像素。Inspector fieldset 的 outline 同样来自 `EditorPalette.inspectorSectionBorder`。 +基础与通用控件的语义色集中在 `EditorPalette`,包括原生 ImGui col、选择、禁用、错误、轴颜色和 drag target。领域分类及专属预览颜色由对应 presentation owner 解释;它们复用基础色,不把 Shader 类型名单或资产布局放回共享层。 + +`widgets.NavigateList(interactions, itemCount, focusedIndex, pageSize)` 返回当前可见顺序上的 `ListNavigationResult`,不保存对象或替领域修改 Selection。调用方把结果交给同一个 Core Identity/Interactions owner:方向、Home/End、Page Up/Down 移动,Shift 扩展范围,产品主修饰键允许只移动焦点或用 Space 切换选择,Enter 请求激活,Escape 请求取消。输入框、其他活动控件或 Popup 占有输入时不产生底层列表操作。树的行 ID 只在现有 Session/Window 状态中参与按键归属;叠加按钮仍由原生控件处理,不变成第二套对象身份或选择服务。`ImGui.FocusNextItem()` 在有效绘制 scope 内请求下一控件的键盘焦点;`TreeNodeOptions.requestFocus` 用于当前可见行的焦点恢复。 + +`widgets.SearchInput` 使用稳定 ID,Escape 先清空非空查询,再次按下退出输入。`widgets.Trend` 只在当前绘制调用内借用有限数值 Span,不复制缓冲或保留 provider;空 Span 绘制空图,非有限样本或非法环形偏移明确失败。采样窗口、频率及 buffer 生命周期由使用它的 Panel 拥有。 + +基础控件的像素布局、padding、spacing、rounding、列比例和最小尺寸集中在 `EditorUiSession.current.widgets.style`(`EditorStyleMetrics`)。Panel 可以读取语义名,例如 `panelTabFramePadding`、`sectionHeaderPadding`、`inspectorSectionPadding`、`inspectorSectionRounding`、`inspectorCollapsedSectionCapLength`、`propertyMetadataSpacing`、`inspectorCardSpacing`、`hierarchyItemSpacing`、`hierarchyRenameMinimumWidth` 与 `settingsFieldPadding`,不应新增散落的固定像素。Inspector fieldset 的 outline 同样来自 `EditorPalette.inspectorSectionBorder`。 `EditorUiSession.current.widgets.SetupStyle()` 把 layout metrics 和 `EditorPalette` 应用到原生 ImGui style;运行期间 zoom 改变时,runtime 只在倍率发生变化后重新应用一次 native style。普通窗口绘制时,`ResizeGrip`、`ResizeGripHovered` 与 `ResizeGripActive` 使用透明色,因此可缩放窗口仍保留边缘/角落命中能力,但不会显示右下角三角形。Dear ImGui 在更新 Dock tree splitter 时会把 separator hover/active 临时映射到 resize-grip hover/active;`ImGuiEditorRuntime` 只在 `DockSpaceOverViewport` 调用范围内恢复这两个 accent color,保证 Panel 间连接线的 hover/drag feedback 可见,同时不恢复窗口三角形。 @@ -288,6 +294,7 @@ static void DrawHelp() + @@ -326,6 +333,7 @@ static void DrawHelp() + @@ -344,6 +352,7 @@ static void DrawHelp() + @@ -1033,6 +1042,7 @@ static void DrawHelp() + @@ -1055,9 +1065,6 @@ static void DrawHelp() - - - @@ -1094,66 +1101,42 @@ static void DrawHelp() - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - + + @@ -1180,77 +1163,44 @@ static void DrawHelp() - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - + + + - - + + - - - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -1264,7 +1214,6 @@ static void DrawHelp() - @@ -1333,6 +1282,7 @@ static void DrawHelp() + @@ -1393,7 +1343,8 @@ static void DrawHelp() - + + @@ -1416,7 +1367,11 @@ static void DrawHelp() + + + + @@ -1469,9 +1424,21 @@ static void DrawHelp() + + + + + + + + + + + + @@ -1480,6 +1447,7 @@ static void DrawHelp() + @@ -1547,11 +1515,12 @@ static void DrawHelp() | [`public static Inno.Editor.ImGui.EditorUiInput Inno.Editor.ImGui.ImGui.GetInput()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L28) | Copies the input facts for this frame. | A neutral value snapshot; no pointer or callback is retained. | — | | [`public static System.Numerics.Vector4 Inno.Editor.ImGui.ImGui.GetStyleColorValue(Inno.Editor.ImGui.ImGuiCol color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L43) | Copies an unmodulated style color for compound presentation.
`color`: The semantic style color slot. | The copied linear vector of UI color components. | — | | [`public static (uint id, System.Numerics.Vector2 position) Inno.Editor.ImGui.ImGui.GetViewport()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L55) | Gets the logical viewport identity and copied position for the current window. | The viewport ID and its screen-space origin. | — | -| [`public static (float minimumX, float maximumX, float rowMinimum, float rowMaximum) Inno.Editor.ImGui.ImGui.GetTableBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L68) | Gets the table and current row bounds needed by row overlays. | The table's horizontal bounds and current row's vertical bounds. | — | -| [`public static (float minimumX, float maximumX, System.Numerics.Vector2 workMaximum, System.Numerics.Vector2 padding) Inno.Editor.ImGui.ImGui.GetWindowBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L83) | Gets copied inner and work bounds for the current window. | Screen-space horizontal bounds and work maximum. | — | -| [`public static Inno.Editor.ImGui.EditorDrawList Inno.Editor.ImGui.ImGui.GetDrawList()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L96) | Borrows drawing commands for the current window and frame without exposing native memory. | A value that rejects use after a window, frame, context or session change. | — | -| [`public static bool Inno.Editor.ImGui.ImGui.HasDragPayload(string protocol)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L106) | Checks the active payload's logical protocol without exposing its native buffer.
`protocol`: The exact drag protocol name. | True when a drag of that protocol is currently active. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text, float scale)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L128) | Measures literal text at a relative font scale.
`text`: The literal text to measure.
`scale`: Scale relative to the current font. | The copied width and height in UI pixels. | — | +| [`public static (float minimumX, float maximumX, float rowMinimum, float rowMaximum) Inno.Editor.ImGui.ImGui.GetTableBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L71) | Gets the table and current row bounds needed by row overlays. | The table's horizontal bounds and current row's vertical bounds. | `System.InvalidOperationException`: No valid drawing frame or active table exists. | +| [`public static (float minimumX, float maximumX, System.Numerics.Vector2 workMaximum, System.Numerics.Vector2 padding) Inno.Editor.ImGui.ImGui.GetWindowBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L89) | Gets copied inner and work bounds for the current window. | Screen-space horizontal bounds and work maximum. | `System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public static (System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum) Inno.Editor.ImGui.ImGui.GetClipBounds()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L105) | Copies the current window's effective clip after parent and viewport clipping. | Screen-space minimum and maximum values for bounding frame-local submission work. | `System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public static Inno.Editor.ImGui.EditorDrawList Inno.Editor.ImGui.ImGui.GetDrawList()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L118) | Borrows drawing commands for the current window and frame without exposing native memory. | A value that rejects use after a window, frame, context or session change. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.HasDragPayload(string protocol)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L128) | Checks the active payload's logical protocol without exposing its native buffer.
`protocol`: The exact drag protocol name. | True when a drag of that protocol is currently active. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text, float scale)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs#L150) | Measures literal text at a relative font scale.
`text`: The literal text to measure.
`scale`: Scale relative to the current font. | The copied width and height in UI pixels. | — | | [`public static void Inno.Editor.ImGui.ImGui.Text(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L20) | Draws unformatted text.
`text`: Literal text to draw; percent characters are displayed without format-string interpretation. | — | — | | [`public static bool Inno.Editor.ImGui.ImGui.Button(string label, System.Numerics.Vector2 size = default(System.Numerics.Vector2))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L38) | Draws a standard button.
`label`: The visible label and optional ImGui identity suffix.
`size`: The requested size, or zero components for automatic sizing. | when the button is activated. | — | | [`public static bool Inno.Editor.ImGui.ImGui.SmallButton(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L55) | Draws a compact button.
`label`: The visible label and optional ImGui identity suffix. | when the button is activated. | — | @@ -1587,42 +1556,44 @@ static void DrawHelp() | [`public static bool Inno.Editor.ImGui.ImGui.BeginPopup(string id, Inno.Editor.ImGui.ImGuiWindowFlags flags = Inno.Editor.ImGui.ImGuiWindowFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L685) | Begins a named popup.
`id`: The popup identity.
`flags`: Popup window behavior. | when popup contents should be submitted. | — | | [`public static void Inno.Editor.ImGui.ImGui.EndPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L696) | Ends the current popup. | — | — | | [`public static void Inno.Editor.ImGui.ImGui.CloseCurrentPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L705) | Closes the current popup. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.BeginDisabled(bool disabled = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L717) | Begins a disabled block.
`disabled`: Whether controls in the block are disabled. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndDisabled()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L726) | Ends the current disabled block. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.BeginGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L735) | Begins an item group. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.EndGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L744) | Ends the current item group. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushId(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L756) | Pushes a string identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushId(int id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L768) | Pushes an integer identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopId()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L777) | Pops one identity from the ImGui ID stack. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleColor(Inno.Editor.ImGui.ImGuiCol color, System.Numerics.Vector4 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L792) | Pushes one color override.
`color`: The style color slot.
`value`: The linear RGBA value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopStyleColor(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L806) | Pops color overrides.
`count`: The positive number of overrides to pop. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L821) | Pushes one scalar style override.
`style`: The style variable.
`value`: The scalar value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, System.Numerics.Vector2 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L838) | Pushes one two-component style override.
`style`: The style variable.
`value`: The vector value. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.PopStyleVar(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L852) | Pops style-variable overrides.
`count`: The positive number of overrides to pop. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SameLine(float offset = 0, float spacing = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L867) | Places the next item on the same line.
`offset`: The horizontal offset from the line start, or zero for automatic placement.
`spacing`: The explicit spacing, or a negative value for the current style spacing. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Separator()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L878) | Draws a horizontal separator. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SeparatorText(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L890) | Draws a labeled horizontal separator.
`label`: The label text validated by the separator text operation. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Spacing()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L899) | Adds one standard vertical spacing unit. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.Dummy(System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L911) | Advances layout by an invisible size.
`size`: The size consumed by dummy; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.SetNextItemWidth(float width)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L923) | Sets the width of the next item.
`width`: The width in logical units or pixels required by this operation. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetContentRegionAvailable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L935) | Gets the remaining content size in the current region. | The remaining local width and height. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L947) | Gets the current local cursor position. | The local cursor position. | — | -| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosition(System.Numerics.Vector2 position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L959) | Sets the current local cursor position.
`position`: The position consumed by set cursor position; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorScreenPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L971) | Gets the current screen-space cursor position. | The screen-space cursor position. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMinimum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L983) | Gets the minimum screen-space corner of the previous item. | The item minimum corner. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMaximum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L995) | Gets the maximum screen-space corner of the previous item. | The item maximum corner. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1010) | Measures text using the current font.
`text`: The text text validated by the measure text operation. | The measured width and height. | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemHovered(Inno.Editor.ImGui.ImGuiHoveredFlags flags = Inno.Editor.ImGui.ImGuiHoveredFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1025) | Gets whether the previous item is hovered.
`flags`: Hovered query behavior. | when the query succeeds. | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemActive()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1037) | Gets whether the previous item is active. | while the item owns an active interaction. | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsItemClicked(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1052) | Gets whether the previous item was clicked with a mouse button.
`button`: The mouse button to query. | when the item was clicked. | — | -| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseButtonDown(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1067) | Gets whether a mouse button is held in the current Editor UI frame, including input consumed by UI controls.
`button`: The UI mouse button to query. | True while the button is held; callers must also check their item's active or hovered state before editing. | — | -| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMouseDragDelta(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left, float threshold = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1086) | Gets accumulated pointer displacement from the current UI drag's starting position for timeline and canvas controls.
`button`: The button that owns the drag.
`threshold`: Movement required to start dragging, or a negative value to use the Editor's configured threshold. | Displacement in UI coordinates, or zero before the drag threshold is crossed or when no drag is available. The value is cumulative; it must be applied to a captured starting value, not added again each frame. | — | -| [`public static uint Inno.Editor.ImGui.ImGui.ToPackedColor(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1103) | Converts a floating-point RGBA color to Dear ImGui packed color order.
`color`: The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. | The packed color value. | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1124) | Adds a line to the current window draw list.
`start`: The screen-space start point.
`end`: The screen-space end point.
`color`: The packed ImGui color.
`thickness`: The line thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1152) | Adds a rectangle outline to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius.
`thickness`: The outline thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1178) | Adds a filled rectangle to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawCubicCurve(System.Numerics.Vector2 start, System.Numerics.Vector2 firstControl, System.Numerics.Vector2 secondControl, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1209) | Draws a cubic curve without exposing a native draw list or borrowed pointer.
`start`: The first screen-space endpoint.
`firstControl`: The control point beside the first endpoint.
`secondControl`: The control point beside the final endpoint.
`end`: The final screen-space endpoint.
`color`: The packed presentation color.
`thickness`: The positive stroke width. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1234) | Adds text to the current window draw list.
`position`: The screen-space text origin.
`color`: The packed ImGui color.
`text`: The text to draw. | — | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsAnyPopupOpen()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L720) | Reads whether an open popup or modal currently prevents underlying canvas and list input. | True when any popup is open in this native context, independently of its identifier or nesting. | `System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public static void Inno.Editor.ImGui.ImGui.BeginDisabled(bool disabled = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L732) | Begins a disabled block.
`disabled`: Whether controls in the block are disabled. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndDisabled()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L741) | Ends the current disabled block. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.BeginGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L750) | Begins an item group. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.EndGroup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L759) | Ends the current item group. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushId(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L771) | Pushes a string identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushId(int id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L783) | Pushes an integer identity onto the ImGui ID stack.
`id`: The stable identity used to locate the requested value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopId()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L792) | Pops one identity from the ImGui ID stack. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleColor(Inno.Editor.ImGui.ImGuiCol color, System.Numerics.Vector4 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L807) | Pushes one color override.
`color`: The style color slot.
`value`: The linear RGBA value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopStyleColor(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L821) | Pops color overrides.
`count`: The positive number of overrides to pop. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L836) | Pushes one scalar style override.
`style`: The style variable.
`value`: The scalar value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PushStyleVar(Inno.Editor.ImGui.ImGuiStyleVar style, System.Numerics.Vector2 value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L853) | Pushes one two-component style override.
`style`: The style variable.
`value`: The vector value. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.PopStyleVar(int count = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L867) | Pops style-variable overrides.
`count`: The positive number of overrides to pop. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SameLine(float offset = 0, float spacing = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L882) | Places the next item on the same line.
`offset`: The horizontal offset from the line start, or zero for automatic placement.
`spacing`: The explicit spacing, or a negative value for the current style spacing. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Separator()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L893) | Draws a horizontal separator. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SeparatorText(string label)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L905) | Draws a labeled horizontal separator.
`label`: The label text validated by the separator text operation. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Spacing()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L914) | Adds one standard vertical spacing unit. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.Dummy(System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L926) | Advances layout by an invisible size.
`size`: The size consumed by dummy; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetNextItemWidth(float width)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L938) | Sets the width of the next item.
`width`: The width in logical units or pixels required by this operation. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.FocusNextItem()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L950) | Requests keyboard focus for the next submitted item in the current frame and window. | — | `System.InvalidOperationException`: No matching owner-thread drawing session is active. | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetContentRegionAvailable()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L962) | Gets the remaining content size in the current region. | The remaining local width and height. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L974) | Gets the current local cursor position. | The local cursor position. | — | +| [`public static void Inno.Editor.ImGui.ImGui.SetCursorPosition(System.Numerics.Vector2 position)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L986) | Sets the current local cursor position.
`position`: The position consumed by set cursor position; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetCursorScreenPosition()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L998) | Gets the current screen-space cursor position. | The screen-space cursor position. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMinimum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1010) | Gets the minimum screen-space corner of the previous item. | The item minimum corner. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetItemMaximum()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1022) | Gets the maximum screen-space corner of the previous item. | The item maximum corner. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.MeasureText(string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1037) | Measures text using the current font.
`text`: The text text validated by the measure text operation. | The measured width and height. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemHovered(Inno.Editor.ImGui.ImGuiHoveredFlags flags = Inno.Editor.ImGui.ImGuiHoveredFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1052) | Gets whether the previous item is hovered.
`flags`: Hovered query behavior. | when the query succeeds. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemActive()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1064) | Gets whether the previous item is active. | while the item owns an active interaction. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsItemClicked(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1079) | Gets whether the previous item was clicked with a mouse button.
`button`: The mouse button to query. | when the item was clicked. | — | +| [`public static bool Inno.Editor.ImGui.ImGui.IsMouseButtonDown(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1094) | Gets whether a mouse button is held in the current Editor UI frame, including input consumed by UI controls.
`button`: The UI mouse button to query. | True while the button is held; callers must also check their item's active or hovered state before editing. | — | +| [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMouseDragDelta(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left, float threshold = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1113) | Gets accumulated pointer displacement from the current UI drag's starting position for timeline and canvas controls.
`button`: The button that owns the drag.
`threshold`: Movement required to start dragging, or a negative value to use the Editor's configured threshold. | Displacement in UI coordinates, or zero before the drag threshold is crossed or when no drag is available. The value is cumulative; it must be applied to a captured starting value, not added again each frame. | — | +| [`public static uint Inno.Editor.ImGui.ImGui.ToPackedColor(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1130) | Converts a floating-point RGBA color to Dear ImGui packed color order.
`color`: The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. | The packed color value. | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1151) | Adds a line to the current window draw list.
`start`: The screen-space start point.
`end`: The screen-space end point.
`color`: The packed ImGui color.
`thickness`: The line thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1179) | Adds a rectangle outline to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius.
`thickness`: The outline thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1205) | Adds a filled rectangle to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawCubicCurve(System.Numerics.Vector2 start, System.Numerics.Vector2 firstControl, System.Numerics.Vector2 secondControl, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1236) | Draws a cubic curve without exposing a native draw list or borrowed pointer.
`start`: The first screen-space endpoint.
`firstControl`: The control point beside the first endpoint.
`secondControl`: The control point beside the final endpoint.
`end`: The final screen-space endpoint.
`color`: The packed presentation color.
`thickness`: The positive stroke width. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1261) | Adds text to the current window draw list.
`position`: The screen-space text origin.
`color`: The packed ImGui color.
`text`: The text to draw. | — | — | | [`public enum Inno.Editor.ImGui.ImGuiButtonFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L8) | Defines the supported button flags choices at the Editor presentation boundary. | — | — | | [`Inno.Editor.ImGui.ImGuiButtonFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | | [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonLeft = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L17) | React on left mouse button (default) | — | — | @@ -2230,16 +2201,16 @@ static void DrawHelp() | [`Inno.Editor.ImGui.ImGuiWindowFlags.Modal = 134217728`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L125) | Don't use! For internal use by BeginPopupModal() | — | — | | [`Inno.Editor.ImGui.ImGuiWindowFlags.ChildMenu = 268435456`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiWindowFlags.cs#L129) | Don't use! For internal use by BeginMenu() | — | — | | [`public readonly struct Inno.Editor.ImGui.EditorDrawList`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L11) | A pointer-free drawing borrow valid only in its original window, UI session and frame. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.Line(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L41) | Adds a straight screen-space stroke.
`start`: The first endpoint.
`end`: The final endpoint.
`color`: Packed RGBA color.
`thickness`: Stroke width in UI pixels. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.FilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, bool topCornersOnly = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L65) | Adds a filled rectangular surface.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`topCornersOnly`: Whether only the two top corners should be rounded. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.Rectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L90) | Adds a rectangular outline.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`thickness`: Stroke width in UI pixels. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.FilledCircle(System.Numerics.Vector2 center, float radius, uint color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L109) | Adds a filled circle with backend-selected tessellation.
`center`: The screen-space center.
`radius`: The nonnegative radius.
`color`: Packed RGBA color. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.Text(System.Numerics.Vector2 position, uint color, string text, float scale = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L133) | Adds literal text using the current font and an explicit scale.
`position`: Screen-space text origin.
`color`: Packed RGBA color.
`text`: Literal managed text; encoded only for this synchronous call.
`scale`: Positive scale relative to the current font size. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.PushClip(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, bool intersect = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L159) | Restricts subsequent drawing to one screen rectangle.
`minimum`: The clip minimum.
`maximum`: The clip maximum.
`intersect`: Whether to intersect the enclosing clip. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.PopClip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L171) | Restores the enclosing clip after a matching PushClip. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.SplitChannels(int count)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L178) | Begins ordered draw channels within this window.
`count`: The positive number of layers. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.SetChannel(int index)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L190) | Selects a channel from the current split.
`index`: The zero-based channel. | — | — | -| [`public void Inno.Editor.ImGui.EditorDrawList.MergeChannels()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L194) | Merges the current split back into ordered draw commands. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Line(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L39) | Adds a straight screen-space stroke.
`start`: The first endpoint.
`end`: The final endpoint.
`color`: Packed RGBA color.
`thickness`: Stroke width in UI pixels. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.FilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, bool topCornersOnly = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L63) | Adds a filled rectangular surface.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`topCornersOnly`: Whether only the two top corners should be rounded. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Rectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L88) | Adds a rectangular outline.
`minimum`: The upper-left corner.
`maximum`: The lower-right corner.
`color`: Packed RGBA color.
`rounding`: Corner radius.
`thickness`: Stroke width in UI pixels. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.FilledCircle(System.Numerics.Vector2 center, float radius, uint color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L107) | Adds a filled circle with backend-selected tessellation.
`center`: The screen-space center.
`radius`: The nonnegative radius.
`color`: Packed RGBA color. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.Text(System.Numerics.Vector2 position, uint color, string text, float scale = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L131) | Adds literal text using the current font and an explicit scale.
`position`: Screen-space text origin.
`color`: Packed RGBA color.
`text`: Literal managed text; encoded only for this synchronous call.
`scale`: Positive scale relative to the current font size. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.PushClip(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, bool intersect = true)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L157) | Restricts subsequent drawing to one screen rectangle.
`minimum`: The clip minimum.
`maximum`: The clip maximum.
`intersect`: Whether to intersect the enclosing clip. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.PopClip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L169) | Restores the enclosing clip after a matching PushClip. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.SplitChannels(int count)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L176) | Begins ordered draw channels within this window.
`count`: The positive number of layers. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.SetChannel(int index)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L188) | Selects a channel from the current split.
`index`: The zero-based channel. | — | — | +| [`public void Inno.Editor.ImGui.EditorDrawList.MergeChannels()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs#L192) | Merges the current split back into ordered draw commands. | — | — | | [`public static class Inno.Editor.ImGui.EditorDragDropRenderer`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L12) | Bridges managed editor drag sessions to the native ImGui payload API. | — | — | | [`public static bool Inno.Editor.ImGui.EditorDragDropRenderer.Source(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDragData data, System.Action? drawPreview = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L30) | Publishes managed drag data for the most recently submitted ImGui item.
`interaction`: The interaction area and target that produced the drag source.
`data`: The managed drag data published by the source.
`drawPreview`: An optional callback that draws the native drag preview. | while the item is an active drag source. | — | | [`public static Inno.Editor.ImGui.EditorDropWidgetResult Inno.Editor.ImGui.EditorDragDropRenderer.Target(Inno.Editor.Interactions.EditorInteraction interaction, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorDragDropRenderer.cs#L51) | Evaluates and, on delivery, accepts a managed drag on the most recently submitted ImGui item.
`interaction`: The interaction area and managed drop target.
`placement`: The requested position relative to the target. | The native preview state together with the managed compatibility and delivery results. | — | @@ -2279,13 +2250,13 @@ static void DrawHelp() | [`public float Inno.Editor.ImGui.EditorUiInput.deltaTime { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L41) | Gets the frame duration in seconds. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiInput.framebufferScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L45) | Gets the drawable pixel scale relative to logical UI coordinates. | — | — | | [`public sealed class Inno.Editor.ImGui.EditorUiSession : System.IDisposable`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L12) | Owns editor controls and transient presentation state for one host, independently from other ImGui contexts. | — | — | -| [`public Inno.Editor.ImGui.EditorUiSession.EditorUiSession()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L26) | Creates independent controls and style state; the presentation backend retains its native context ownership. | — | — | -| [`public static Inno.Editor.ImGui.EditorUiSession Inno.Editor.ImGui.EditorUiSession.current { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L38) | Gets the current synchronous drawing scope; this accessor never creates or owns a session. | — | `System.InvalidOperationException`: No UI drawing scope is active on the calling thread. | -| [`public Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget Inno.Editor.ImGui.EditorUiSession.widgets { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L42) | Gets the controls whose transient state belongs exclusively to this session. | — | — | -| [`public Inno.Editor.ImGui.EditorMenuRenderer Inno.Editor.ImGui.EditorUiSession.menus { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L48) | Gets the menu presenter whose search state belongs exclusively to this session. | — | — | -| [`public bool Inno.Editor.ImGui.EditorUiSession.isDisposed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L52) | Gets whether this session has successfully retired its presentation state. | — | — | -| [`public System.IDisposable Inno.Editor.ImGui.EditorUiSession.Enter()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L67) | Activates this session for a synchronous callback while preserving the enclosing scope. | An owner-thread scope that must be released in reverse activation order. | `System.ObjectDisposedException`: The session has retired.
`System.InvalidOperationException`: The caller is not the owner thread. | -| [`public void Inno.Editor.ImGui.EditorUiSession.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L83) | Releases presentation state after all drawing scopes and domain edit gestures have ended. | — | `System.InvalidOperationException`: A drawing scope is active or the caller is not the owner thread. | +| [`public Inno.Editor.ImGui.EditorUiSession.EditorUiSession()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L27) | Creates independent controls and style state; the presentation backend retains its native context ownership. | — | — | +| [`public static Inno.Editor.ImGui.EditorUiSession Inno.Editor.ImGui.EditorUiSession.current { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L39) | Gets the current synchronous drawing scope; this accessor never creates or owns a session. | — | `System.InvalidOperationException`: No UI drawing scope is active on the calling thread. | +| [`public Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget Inno.Editor.ImGui.EditorUiSession.widgets { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L43) | Gets the controls whose transient state belongs exclusively to this session. | — | — | +| [`public Inno.Editor.ImGui.EditorMenuRenderer Inno.Editor.ImGui.EditorUiSession.menus { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L49) | Gets the menu presenter whose search state belongs exclusively to this session. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiSession.isDisposed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L53) | Gets whether this session has successfully retired its presentation state. | — | — | +| [`public System.IDisposable Inno.Editor.ImGui.EditorUiSession.Enter()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L81) | Activates this session for a synchronous callback while preserving the enclosing scope. | An owner-thread scope that must be released in reverse activation order. | `System.ObjectDisposedException`: The session has retired.
`System.InvalidOperationException`: The caller is not the owner thread. | +| [`public void Inno.Editor.ImGui.EditorUiSession.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs#L97) | Releases presentation state after all drawing scopes and domain edit gestures have ended. | — | `System.InvalidOperationException`: A drawing scope is active or the caller is not the owner thread. | | [`public sealed class Inno.Editor.ImGui.ImGuiEditorRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L21) | Presents the backend-independent editor interaction runtime through ImGui. | — | — | | [`public Inno.Editor.ImGui.ImGuiEditorRuntime.ImGuiEditorRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, System.Collections.Generic.IEnumerable hostServices)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L52) | Creates an ImGui editor runtime with stable host-owned extension services.
`context`: The shared editor context that owns project settings and frame state.
`types`: The host-owned type catalog that coordinates extension generations.
`logs`: The host-owned logging router used by editor infrastructure.
`hostServices`: Services available to discovered extension constructors. | — | `System.ArgumentNullException`: Thrown when , , or is . | | [`public Inno.Editor.Interactions.EditorInteractions Inno.Editor.ImGui.ImGuiEditorRuntime.interactions { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L75) | Gets the active presentation-independent interaction entry point. | — | — | @@ -2294,9 +2265,10 @@ static void DrawHelp() | [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Update(Inno.Editor.Core.EditorFrame frame)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L96) | Recomputes owned state from the current validated inputs.
`frame`: The frame consumed by update; ownership remains with the caller unless explicitly stated otherwise. | — | — | | [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.SaveState()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L108) | Captures all stateful active modules and panels and flushes their project state to disk. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | | [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.PrepareShutdown()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L126) | Freezes automatic extension-state persistence and writes the final project state before editor modules begin shutting down. | — | `System.ObjectDisposedException`: Thrown after this runtime has been disposed. | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.Draw()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L137) | Draws the complete editor frame through ImGui. | — | — | -| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L264) | Dispatches contextual shortcuts, leaving editing keys with active text widgets while permitting explicit save.
`keyEvent`: The keyboard event received from the application event stream. | — | — | -| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L285) | Releases the resources owned by this implementation. | — | `Inno.Core.Execution.RetirementPendingException`: Editor extensions still own live work. The interaction runtime remains retained for safe shutdown. | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.PrepareFrame()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L143) | Applies pending style inputs with the owned context before the backend creates its native frame. | — | `System.ObjectDisposedException`: This runtime has retired.
`System.InvalidOperationException`: The owning native context is unavailable, incorrect, or already drawing. | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.Draw()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L154) | Draws the complete editor frame through ImGui. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiEditorRuntime.HandleKeyPressed(Inno.Core.Events.KeyPressedEvent keyEvent)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L281) | Dispatches contextual shortcuts, leaving editing keys with active text widgets while permitting explicit save.
`keyEvent`: The keyboard event received from the application event stream. | — | — | +| [`public override void Inno.Editor.ImGui.ImGuiEditorRuntime.Dispose()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs#L302) | Releases the resources owned by this implementation. | — | `Inno.Core.Execution.RetirementPendingException`: Editor extensions still own live work. The interaction runtime remains retained for safe shutdown. | | [`public static class Inno.Editor.ImGui.EditorPalette`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L11) | Defines the complete editor color palette in one theme surface. | — | — | | [`public const float Inno.Editor.ImGui.EditorPalette.opacityNone = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L16) | Gets zero opacity for invisible surfaces. | — | — | | [`public const float Inno.Editor.ImGui.EditorPalette.opacityTrace = 0.005`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L21) | Gets trace opacity for barely perceptible alternating surfaces. | — | — | @@ -2316,216 +2288,155 @@ static void DrawHelp() | [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.textDisabled { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L91) | Gets disabled text color. | — | — | | [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.error { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L96) | Gets the standard error color. | — | — | | [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.warning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L101) | Gets the standard warning color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L106) | Gets the neutral Shader node heading surface. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderInputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L111) | Gets the orange heading surface shared by Shader input nodes. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderOutputNodeHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L116) | Gets the purple heading surface shared by Shader output nodes. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.windowBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L121) | Gets the editor window background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.popupBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L126) | Gets the editor popup background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L131) | Gets text color used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L136) | Gets the background used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItem { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L141) | Gets the resting background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L146) | Gets the hovered background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L151) | Gets the active background of an editor context-menu item. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuSeparator { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L156) | Gets the separator color used by editor context menus. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.border { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L161) | Gets the standard border color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.borderShadow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L166) | Gets the standard border shadow color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frame { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L171) | Gets the standard frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L176) | Gets the hovered frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L181) | Gets the active frame background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.title { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L186) | Gets the inactive title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L191) | Gets the active title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleCollapsed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L196) | Gets the collapsed title background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L201) | Gets the standard scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L206) | Gets the hovered scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L211) | Gets the active scrollbar thumb. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L216) | Gets the standard accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L221) | Gets the hovered accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L226) | Gets the active accent color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L231) | Gets the standard tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L236) | Gets the hovered tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L241) | Gets the selected tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L246) | Gets the dimmed tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmedSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L251) | Gets the selected dimmed tab color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelectedOverline { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L256) | Gets the selected tab overline color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L261) | Gets the table header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderStrong { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L266) | Gets the strong table border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderLight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L271) | Gets the light table border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L276) | Gets the alternate table row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.dragDropTarget { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L281) | Gets the standard drag target color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationHighlight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L286) | Gets navigation highlight color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L291) | Gets navigation dim background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.modalDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L296) | Gets modal dim background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L301) | Gets inspector card header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L306) | Gets the persistent Inspector target header background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTargetHeaderBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L311) | Gets the persistent Inspector target header border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorTagLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L316) | Gets the subdued background of Tag labels in Inspector target headers. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorLayerLabel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L321) | Gets the subdued background of Layer labels in Inspector target headers. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBody { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L326) | Gets inspector card body background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardBodyBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L331) | Gets inspector card body border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorSectionBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L336) | Gets the outline color of framed Inspector sections. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeNumeric { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L341) | Gets the accent used by numeric Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeInteger { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L346) | Gets the accent used by integer Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeBoolean { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L351) | Gets the accent used by Boolean Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeResource { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L356) | Gets the accent used by Shader resource type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.shaderTypeOther { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L361) | Gets the accent used by unclassified Shader type badges. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisabledText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L366) | Gets disabled inspector card text. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.inspectorCardDisclosureHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L371) | Gets inspector disclosure hover background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.compactControlHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L376) | Gets compact control hover color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L381) | Gets the deepest collection background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L386) | Gets the primary collection row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L391) | Gets the alternate collection row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L396) | Gets the translucent primary background of a complete Settings field. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.settingsFieldAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L401) | Gets the translucent alternate background of a complete Settings field. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L406) | Gets the asset browser field background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L411) | Gets the asset browser border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBorderSoft { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L416) | Gets the soft asset browser border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L421) | Gets the asset browser text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetTextMuted { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L426) | Gets the muted asset browser text color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetBreadcrumbText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L431) | Gets the subdued text color used by asset browser breadcrumb paths. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.assetAccent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L436) | Gets the opaque asset browser accent. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetHovered(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L447) | Gets a hover treatment derived from a base theme color.
`color`: The color consumed by get hovered; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard hover amount. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetActive(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L458) | Gets an active treatment derived from a base theme color.
`color`: The color consumed by get active; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard active amount. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchySceneRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L463) | Gets scene row background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.hierarchyInactiveText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L468) | Gets inactive hierarchy text. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.treeGuide { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L473) | Gets hierarchy tree guide color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisX { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L478) | Gets X axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisY { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L483) | Gets Y axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisZ { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L488) | Gets Z axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisW { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L493) | Gets W axis color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logDebug { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L498) | Gets debug log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logInfo { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L503) | Gets informational log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logWarning { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L508) | Gets warning log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logError { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L513) | Gets error log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logFatal { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L518) | Gets fatal log color. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedCard { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L523) | Gets collapsed log card background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logCollapsedBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L528) | Gets collapsed log card border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L533) | Gets the base expanded log card background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logExpandedBorderBase { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L538) | Gets the base expanded log card border. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggle { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L543) | Gets log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L548) | Gets hovered log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.logToggleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L553) | Gets active log header button background. | — | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedCard(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L564) | Gets the expanded log card background derived from a severity color.
`severityColor`: The palette color representing the log severity. | The standard expanded-card base blended with the severity color. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogExpandedBorder(System.Numerics.Vector4 severityColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L575) | Gets the expanded log card border derived from a severity color.
`severityColor`: The palette color representing the log severity. | The standard expanded-border base blended with the severity color. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetLogSeparator(System.Numerics.Vector4 cardColor)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L586) | Gets the separator color used inside an expanded log card.
`cardColor`: The resolved background color of the expanded card. | The card color blended toward the palette text color. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.Lerp(System.Numerics.Vector4 from, System.Numerics.Vector4 to, float amount)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L603) | Linearly interpolates two palette colors using a clamped amount.
`from`: The color returned when is zero.
`to`: The color returned when is one.
`amount`: The interpolation amount, clamped to the inclusive zero-to-one range. | The component-wise interpolated color. | — | -| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.WithOpacity(System.Numerics.Vector4 color, float opacity)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L686) | Returns a theme color with a caller-selected opacity, normally one of the shared opacity levels.
`color`: Base RGB color.
`opacity`: Replacement alpha in the inclusive zero-to-one range. | The validated vector4 that represents the completed operation. | — | -| [`public sealed class Inno.Editor.ImGui.EditorStyleMetrics`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | Defines named editor layout metrics used by widgets and feature panels. | — | — | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MIN_ZOOM = 0.75`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L14) | Defines the smallest supported editor UI zoom. | — | — | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MAX_ZOOM = 1.5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L19) | Defines the largest supported editor UI zoom. | — | — | -| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_ZOOM_STEP = 0.1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L24) | Defines one keyboard or menu zoom increment. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L32) | Gets the current editor UI zoom multiplier. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.isCompact { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L37) | Gets whether compact editor density is active. | — | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetCompactMode(bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L48) | Switches between comfortable and compact editor density.
`value`: for compact density. | when the density changed. | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetZoom(float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L68) | Sets the editor UI zoom after clamping it to the supported range.
`value`: The requested UI zoom multiplier. | when the effective zoom changed. | `System.ArgumentOutOfRangeException`: Thrown when is not finite. | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomIn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L85) | Increases editor UI zoom by one bounded increment. | when the zoom changed. | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomOut()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L93) | Decreases editor UI zoom by one bounded increment. | when the zoom changed. | — | -| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ResetZoom()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L101) | Restores the baseline editor UI zoom. | when the zoom changed. | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.fontScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L106) | Gets global content scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disabledAlpha { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L111) | Gets disabled content opacity. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L116) | Gets standard window padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.windowRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L121) | Gets standard window rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.borderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L126) | Gets standard border thickness. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L131) | Gets minimum window size. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.framePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L136) | Gets standard frame padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.panelTabFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L141) | Gets the frame padding used while docked panel tabs are laid out. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L146) | Gets the uniform content padding of editor context menus. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L151) | Gets the uniform item padding of editor context menus. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuSearchFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L157) | Gets the compact padding used by the search field at the top of editor context menus. Its vertical padding matches the half-spacing that menu entries add above and below a line. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L162) | Gets the uniform spacing between editor context-menu items. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L167) | Gets editor context-menu corner rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuBorderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L172) | Gets editor context-menu border thickness. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L177) | Gets compact frame padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inlineRenameFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L182) | Gets the compact inner padding of inline rename fields. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.toolbarFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L187) | Gets toolbar frame padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.breadcrumbFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L192) | Gets breadcrumb frame padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.frameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L197) | Gets standard frame rounding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L202) | Gets standard item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L207) | Gets compact vertical item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.hierarchyItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L212) | Gets hierarchy item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemInnerSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L217) | Gets standard inner item spacing. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L222) | Gets standard table cell padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.settingsFieldPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L227) | Gets the inner padding applied to one complete Settings field. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L232) | Gets asset browser window padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L237) | Gets asset browser cell padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.assetItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L242) | Gets asset browser item spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetFrameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L247) | Gets asset browser frame rounding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.indentSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L252) | Gets tree indentation. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.columnMinimumSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L257) | Gets minimum column spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L262) | Gets scrollbar width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L267) | Gets the inset that keeps an overlay scrollbar grab visually lightweight. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.grabMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L272) | Gets minimum grab size. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetPaneMinimumVisibleWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L277) | Gets the minimum visible width retained for either asset browser pane while its splitter is dragged. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetSplitterMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L282) | Gets the minimum draggable asset browser splitter width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L287) | Gets asset breadcrumb bar height. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumCellSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L292) | Gets minimum asset grid cell size. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L297) | Gets asset grid cell padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridDefaultScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L302) | Gets default asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMinimumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L307) | Gets minimum asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridMaximumScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L312) | Gets maximum asset grid scale. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L317) | Gets spacing between asset list rows. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L322) | Gets spacing between inspector cards. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L327) | Gets the inner padding of the persistent Inspector target header. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetHeaderRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L332) | Gets spacing between the two rows of the persistent Inspector target header. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorTargetIconScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L337) | Gets the font-size multiplier used by the large Inspector target icon. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardBodyPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L342) | Gets inspector card body padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorCardHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L347) | Gets inspector card header padding. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.sectionHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L352) | Gets the shared padding around separator-style section titles. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L357) | Gets the inner padding of a framed Inspector section. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionLegendGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L362) | Gets the gap between an Inspector section legend and its interrupted top border. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorCollapsedSectionCapLength { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L367) | Gets the length of each centered end cap on a collapsed Inspector section. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyMetadataSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L372) | Gets the spacing around the separator between inline metadata and its value. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L377) | Gets the vertical distance after a framed Inspector section. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorSectionRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L382) | Gets the subtle corner rounding used by framed content sections inside editor containers. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disclosureButtonInset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L387) | Gets inspector disclosure inset. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyLabelRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L392) | Gets property label width ratio. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.logAutoScrollTolerance { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L397) | Gets the distance from the bottom treated as auto-scroll. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.logDisclosurePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L402) | Gets log disclosure padding. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.searchPopupWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L407) | Gets the default width of searchable editor popups. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.iconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L412) | Gets spacing between a leading icon and its text. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.compactCheckboxSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L417) | Gets the compact checkbox size used by card headers. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderControlSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L422) | Gets spacing between leading controls inside card headers. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.labelChipPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L427) | Gets the inner padding of compact colored label chips. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.labelChipRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L432) | Gets the corner rounding of compact colored label chips. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.typeBadgePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L437) | Gets the compact inner padding of outlined type badges. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L442) | Gets the corner rounding of outlined type badges. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L447) | Gets the horizontal gap between a property name and its type badge. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.statisticRowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L452) | Gets the vertical padding inside one statistics row. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorHeaderSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L457) | Gets spacing between distinct control groups in an Inspector target header row. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.inspectorAddButtonTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L462) | Gets top padding above inspector add buttons. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L467) | Gets the minimum inline hierarchy rename width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyRenameTrailingGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L472) | Gets the hierarchy rename trailing-control gap. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.hierarchyBlankMinimumHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L477) | Gets the minimum height of the hierarchy blank drop area. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarTightSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L482) | Gets the tight asset browser toolbar spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L487) | Gets the regular asset browser toolbar spacing. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetToolbarSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L492) | Gets spacing between asset browser toolbar sections. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListNameSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L497) | Gets the default normalized position of the asset list name/type separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListTypeSeparatorPosition { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L502) | Gets the default normalized position of the asset list type/source separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListMinimumColumnRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L507) | Gets the minimum normalized width reserved for each asset list column. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListSeparatorHitWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L512) | Gets the horizontal hit width of an asset list column separator. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetListContentHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L517) | Gets the horizontal inset between an asset list separator and column content. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L522) | Gets horizontal padding for asset grid labels. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelBottomPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L527) | Gets bottom padding for asset grid labels. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridLabelLineSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L532) | Gets the additional vertical spacing between asset grid label lines. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridFixedCellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L537) | Gets fixed padding added to calculated asset grid cells. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridScaleBias { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L542) | Gets the scale bias added to asset grid icons. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L547) | Gets the top inset reserved above an asset grid icon. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconHorizontalPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L552) | Gets the horizontal inset that constrains an asset grid icon inside its card. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetGridIconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L557) | Gets the vertical spacing between an asset grid icon and its label. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.assetBreadcrumbSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L562) | Gets spacing around breadcrumb separators. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisValueMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L567) | Gets the minimum width of one axis value field. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L572) | Gets the minimum width of an axis prefix. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixWidthRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L577) | Gets the axis prefix share of the complete control. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.interactionOverlayThickness { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L582) | Gets the standard foreground interaction-overlay thickness. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scriptCompilationWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L587) | Gets script compilation modal width. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.modalWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L592) | Gets the standard centered modal width. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeInSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L597) | Gets the modal fade-in duration in seconds. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalMinimumVisibleSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L602) | Gets the minimum modal visibility duration in seconds. | — | — | -| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeOutSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L607) | Gets the modal fade-out duration in seconds. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLeftOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L612) | Gets tree guide left offset. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLineOverlap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L617) | Gets the overlap used to join adjacent tree guide segments. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeFolderConnectorPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L622) | Gets additional connector padding for expandable tree nodes. | — | — | -| [`public float Inno.Editor.ImGui.EditorStyleMetrics.textDecorationOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L627) | Gets the vertical offset of text decorations from the baseline. | — | — | -| [`public Inno.Editor.ImGui.EditorStyleMetrics.EditorStyleMetrics()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.windowBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L106) | Gets the editor window background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.popupBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L111) | Gets the editor popup background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L116) | Gets text color used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuBackground { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L121) | Gets the background used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItem { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L126) | Gets the resting background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L131) | Gets the hovered background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuItemActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L136) | Gets the active background of an editor context-menu item. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.menuSeparator { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L141) | Gets the separator color used by editor context menus. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.border { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L146) | Gets the standard border color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.borderShadow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L151) | Gets the standard border shadow color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frame { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L156) | Gets the standard frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L161) | Gets the hovered frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.frameActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L166) | Gets the active frame background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.title { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L171) | Gets the inactive title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L176) | Gets the active title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.titleCollapsed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L181) | Gets the collapsed title background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L186) | Gets the standard scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L191) | Gets the hovered scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.scrollbarGrabActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L196) | Gets the active scrollbar thumb. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L201) | Gets the standard accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L206) | Gets the hovered accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.accentActive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L211) | Gets the active accent color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tab { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L216) | Gets the standard tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L221) | Gets the hovered tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L226) | Gets the selected tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmed { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L231) | Gets the dimmed tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabDimmedSelected { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L236) | Gets the selected dimmed tab color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tabSelectedOverline { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L241) | Gets the selected tab overline color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L246) | Gets the table header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderStrong { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L251) | Gets the strong table border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableBorderLight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L256) | Gets the light table border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.tableRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L261) | Gets the alternate table row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.dragDropTarget { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L266) | Gets the standard drag target color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationHighlight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L271) | Gets navigation highlight color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.navigationDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L276) | Gets navigation dim background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.modalDim { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L281) | Gets modal dim background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.cardHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L286) | Gets card header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.targetHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L291) | Gets the persistent target header background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.targetHeaderBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L296) | Gets the persistent target header border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.cardBody { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L301) | Gets card body background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.cardBodyBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L306) | Gets card body border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.sectionBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L311) | Gets the outline color of framed sections. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.cardDisabledText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L316) | Gets disabled card text. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.cardDisclosureHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L321) | Gets card disclosure hover background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.compactControlHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L326) | Gets compact control hover color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionHeader { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L331) | Gets the deepest collection background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRow { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L336) | Gets the primary collection row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.collectionRowAlternate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L341) | Gets the alternate collection row background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentField { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L346) | Gets the content field background. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentBorder { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L351) | Gets the content border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentBorderSoft { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L356) | Gets the soft content border. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L361) | Gets the content text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentTextMuted { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L366) | Gets the muted content text color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contextText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L371) | Gets the subdued contextual text color used by paths and origin hints. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.contentAccent { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L376) | Gets the opaque content accent. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetHovered(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L387) | Gets a hover treatment derived from a base theme color.
`color`: The color consumed by get hovered; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard hover amount. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.GetActive(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L398) | Gets an active treatment derived from a base theme color.
`color`: The color consumed by get active; ownership remains with the caller unless explicitly stated otherwise. | The base color blended toward the palette text color using the standard active amount. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.treeGuide { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L403) | Gets the guide color of reusable tree controls. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisX { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L408) | Gets X axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisY { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L413) | Gets Y axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisZ { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L418) | Gets Z axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.axisW { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L423) | Gets W axis color. | — | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.Lerp(System.Numerics.Vector4 from, System.Numerics.Vector4 to, float amount)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L440) | Linearly interpolates two palette colors using a clamped amount.
`from`: The color returned when is zero.
`to`: The color returned when is one.
`amount`: The interpolation amount, clamped to the inclusive zero-to-one range. | The component-wise interpolated color. | — | +| [`public static System.Numerics.Vector4 Inno.Editor.ImGui.EditorPalette.WithOpacity(System.Numerics.Vector4 color, float opacity)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs#L523) | Returns a theme color with a caller-selected opacity, normally one of the shared opacity levels.
`color`: Base RGB color.
`opacity`: Replacement alpha in the inclusive zero-to-one range. | The validated vector4 that represents the completed operation. | — | +| [`public sealed class Inno.Editor.ImGui.EditorStyleMetrics`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L14) | Defines reusable layout metrics and separates owned frame values from requested style changes. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MIN_ZOOM = 0.75`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L19) | Defines the smallest supported editor UI zoom. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_MAX_ZOOM = 1.5`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L24) | Defines the largest supported editor UI zoom. | — | — | +| [`public const float Inno.Editor.ImGui.EditorStyleMetrics.C_ZOOM_STEP = 0.1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L29) | Defines one keyboard or menu zoom increment. | — | — | +| [`public Inno.Editor.ImGui.EditorStyleMetrics.EditorStyleMetrics()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L40) | Creates independent metrics whose changes apply immediately without a native context. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L51) | Gets the effective zoom used by the current presentation frame. | — | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.isCompact { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L56) | Gets whether compact editor density is active. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.trendHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L61) | Gets the scaled height of a compact sampled-value trend plot. | — | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetCompactMode(bool value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L78) | Requests comfortable or compact density without changing an active session frame.
`value`: for compact density. | when the requested density changed; owned drawing applies it at the next frame preparation. | `System.ObjectDisposedException`: The owning session has retired or is no longer available.
`System.InvalidOperationException`: The caller is not the owning session's thread. | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.SetZoom(float value)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L106) | Requests a bounded zoom without changing metrics partway through an owned native frame.
`value`: The requested UI zoom multiplier. | when the requested zoom changed; owned drawing applies it at the next frame preparation. | `System.ArgumentOutOfRangeException`: Thrown when is not finite.
`System.ObjectDisposedException`: The owning session has retired or is no longer available.
`System.InvalidOperationException`: The caller is not the owning session's thread. | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomIn()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L125) | Increases editor UI zoom by one bounded increment. | when the requested zoom changed. | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ZoomOut()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L133) | Decreases editor UI zoom by one bounded increment. | when the requested zoom changed. | — | +| [`public bool Inno.Editor.ImGui.EditorStyleMetrics.ResetZoom()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L141) | Restores the baseline editor UI zoom. | when the requested zoom changed. | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.fontScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L146) | Gets global content scale. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disabledAlpha { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L151) | Gets disabled content opacity. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L156) | Gets standard window padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.windowRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L161) | Gets standard window rounding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.borderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L166) | Gets standard border thickness. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.windowMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L171) | Gets minimum window size. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.framePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L176) | Gets standard frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.panelTabFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L181) | Gets the frame padding used while docked panel tabs are laid out. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuWindowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L186) | Gets the uniform content padding of editor context menus. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L191) | Gets the uniform item padding of editor context menus. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuSearchFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L197) | Gets the compact padding used by the search field at the top of editor context menus. Its vertical padding matches the half-spacing that menu entries add above and below a line. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.menuItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L202) | Gets the uniform spacing between editor context-menu items. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L207) | Gets editor context-menu corner rounding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.menuBorderSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L212) | Gets editor context-menu border thickness. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L217) | Gets compact frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.inlineRenameFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L222) | Gets the compact inner padding of inline rename fields. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.toolbarFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L227) | Gets toolbar frame padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.breadcrumbFramePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L232) | Gets breadcrumb frame padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.frameRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L237) | Gets standard frame rounding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L242) | Gets standard item spacing. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.compactItemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L247) | Gets compact vertical item spacing. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.itemInnerSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L252) | Gets standard inner item spacing. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cellPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L257) | Gets standard table cell padding. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.indentSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L262) | Gets tree indentation. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.columnMinimumSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L267) | Gets minimum column spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L272) | Gets scrollbar width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scrollbarPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L277) | Gets the inset that keeps an overlay scrollbar grab visually lightweight. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.grabMinimumSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L282) | Gets minimum grab size. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.paneMinimumVisibleWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L287) | Gets the minimum visible width retained for either resizable content pane while its splitter is dragged. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.splitterMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L292) | Gets the minimum draggable resizable content splitter width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.cardSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L297) | Gets spacing between cards. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.targetHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L302) | Gets the inner padding of the persistent target header. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.targetHeaderRowSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L307) | Gets spacing between the two rows of the persistent target header. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.targetIconScale { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L312) | Gets the font-size multiplier used by the large target icon. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cardBodyPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L317) | Gets card body padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.cardHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L322) | Gets card header padding. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.sectionHeaderPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L327) | Gets the shared padding around separator-style section titles. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.sectionPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L332) | Gets the inner padding of a framed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.sectionLegendGap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L337) | Gets the gap between an Inspector section legend and its interrupted top border. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.collapsedSectionCapLength { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L342) | Gets the length of each centered end cap on a collapsed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyMetadataSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L347) | Gets the spacing around the separator between inline metadata and its value. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.sectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L352) | Gets the vertical distance after a framed Inspector section. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.sectionRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L357) | Gets the subtle corner rounding used by framed content sections inside editor containers. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.disclosureButtonInset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L362) | Gets card disclosure inset. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.propertyLabelRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L367) | Gets property label width ratio. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.searchPopupWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L372) | Gets the default width of searchable editor popups. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.iconLabelSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L377) | Gets spacing between a leading icon and its text. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.compactCheckboxSize { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L382) | Gets the compact checkbox size used by card headers. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.headerControlSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L387) | Gets spacing between leading controls inside card headers. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.labelChipPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L392) | Gets the inner padding of compact colored label chips. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.labelChipRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L397) | Gets the corner rounding of compact colored label chips. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorStyleMetrics.typeBadgePadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L402) | Gets the compact inner padding of outlined type badges. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeRounding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L407) | Gets the corner rounding of outlined type badges. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.typeBadgeSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L412) | Gets the horizontal gap between a property name and its type badge. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.headerSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L417) | Gets spacing between distinct control groups in the target header row. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.addButtonTopPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L422) | Gets top padding above trailing add buttons. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.toolbarTightSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L427) | Gets the tight resizable content toolbar spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.toolbarSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L432) | Gets the regular resizable content toolbar spacing. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.toolbarSectionSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L437) | Gets spacing between resizable content toolbar sections. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisValueMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L442) | Gets the minimum width of one axis value field. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixMinimumWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L447) | Gets the minimum width of an axis prefix. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.axisPrefixWidthRatio { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L452) | Gets the axis prefix share of the complete control. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.interactionOverlayThickness { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L457) | Gets the standard foreground interaction-overlay thickness. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.scriptCompilationWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L462) | Gets script compilation modal width. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.modalWidth { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L467) | Gets the standard centered modal width. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeInSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L472) | Gets the modal fade-in duration in seconds. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalMinimumVisibleSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L477) | Gets the minimum modal visibility duration in seconds. | — | — | +| [`public double Inno.Editor.ImGui.EditorStyleMetrics.modalFadeOutSeconds { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L482) | Gets the modal fade-out duration in seconds. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLeftOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L487) | Gets tree guide left offset. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeGuideLineOverlap { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L492) | Gets the overlap used to join adjacent tree guide segments. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.treeFolderConnectorPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L497) | Gets additional connector padding for expandable tree nodes. | — | — | +| [`public float Inno.Editor.ImGui.EditorStyleMetrics.textDecorationOffset { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs#L502) | Gets the vertical offset of text decorations from the baseline. | — | — | | [`public readonly struct Inno.Editor.ImGui.EditorUiLayout`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L8) | Copies the presentation metrics needed by custom layouts without retaining native style memory. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.itemSpacing { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L13) | Gets spacing between neighboring controls. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiLayout.windowPadding { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorUiLayout.cs#L17) | Gets the current window inset. | — | — | @@ -2594,6 +2505,7 @@ static void DrawHelp() | [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CollapsingCard(string id, string title, System.Action? drawLeadingControl = null, System.Action? drawTrailingControl = null, bool defaultOpen = true, bool dimmed = false, float trailingControlWidth = 0, System.Action? drawContextMenu = null)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L42) | Draws a component-style collapsible card header.
`id`: The stable card identifier.
`title`: The visible card title.
`drawLeadingControl`: An optional control drawn before the title.
`drawTrailingControl`: An optional control aligned to the right edge.
`defaultOpen`: Whether the card starts expanded.
`dimmed`: Whether header controls and title use the inactive text color.
`trailingControlWidth`: The optional width reserved for the trailing control group.
`drawContextMenu`: An optional callback that binds a context menu to the complete header item. | when card content should be drawn; otherwise, . | — | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DrawDisclosureIndicator(System.Numerics.Vector2 min, System.Numerics.Vector2 max, bool open, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L195) | Draws a vertically centered disclosure triangle with button-style hover feedback.
`min`: The minimum screen coordinate of the indicator area.
`max`: The maximum screen coordinate of the indicator area.
`open`: Whether the represented content is expanded.
`dimmed`: Whether the indicator uses the inactive text color. | — | — | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.CardBody(string id, System.Action drawContent, bool dimmed = false)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs#L238) | Draws expanded collapsible-card content inside a framed body.
`id`: The stable card identifier.
`drawContent`: The callback that draws card content.
`dimmed`: Whether content is visually disabled and non-interactive. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.ClippedRows(int itemCount, float itemHeight, TState state, System.Action drawRange, System.ReadOnlySpan preservedRows = default(System.ReadOnlySpan))`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Clipping.cs#L42) | Draws visible row ranges and preserves native scrolling, navigation and table cursor bookkeeping.
`itemCount`: The nonnegative count in the caller's current filtered and sorted view.
`itemHeight`: The complete fixed row advance in screen pixels, or -1 to measure the first row through the native clipper.
`state`: Data borrowed only for this call; it is never stored by the widget.
`drawRange`: A synchronous drawing callback receiving inclusive start and exclusive end indices.
`preservedRows`: Optional rows that must still submit an active edit, drag source or requested focus.
`TState`: Call-scoped drawing data, passed directly so static callbacks need no captured closure. | — | `System.ArgumentNullException`: The drawing callback is null.
`System.ArgumentOutOfRangeException`: A count, row height or preserved index is invalid.
`System.InvalidOperationException`: No native frame is active in this UI session. | | [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuPopup(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L31) | Begins an explicitly opened popup using the editor context-menu presentation contract. The popup stays in its parent viewport, sizes itself to submitted content, and scrolls when its work-area bound is reached.
`id`: The stable identifier previously passed to ImGui when opening the popup. | when popup content should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty. | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndMenuPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L47) | Ends a popup opened by and restores the previous style. | — | — | | [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuTooltip()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs#L61) | Begins a parent-viewport tooltip using the same padding, colors, border, and spacing as editor menus. | when tooltip content should be submitted; otherwise, . | — | @@ -2651,10 +2563,11 @@ static void DrawHelp() | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Disabled(bool disabled, System.Action draw)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L349) | Draws content inside an ImGui disabled scope when requested.
`disabled`: Whether interaction is disabled.
`draw`: Drawing callback. | — | — | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.InsertionLine(float fromX, float toX, float y)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L378) | Draws a horizontal insertion marker above normal window content in screen coordinates.
`fromX`: Marker start X coordinate.
`toX`: Marker end X coordinate.
`y`: Marker Y coordinate. | — | — | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.DropTargetHighlight(System.Numerics.Vector2 min, System.Numerics.Vector2 max)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Property.cs#L398) | Draws the standard yellow rectangular drag-and-drop target highlight above all normal window content in screen coordinates.
`min`: Minimum target coordinate.
`max`: Maximum target coordinate. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SearchInput(string id, string hint, ref string query, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L32) | Draws a single-line search field with a stable identifier.
`id`: The stable control identifier.
`hint`: The hint shown while the query is empty.
`query`: The mutable search query.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The control width, or a negative value to fill the available space. | when the query changed; otherwise, . | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginSearchPopup(string id, ref string query, string hint, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L66) | Begins a popup and draws its focused search field.
`id`: The popup identifier previously supplied to ImGui.
`query`: The mutable search query.
`hint`: The hint shown while the query is empty.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The search field width, or a negative value to use the editor default. | when popup content should be drawn; otherwise, . | — | -| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndSearchPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L89) | Ends a popup opened by . | — | — | -| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SelectTarget(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Core.Identity.IdentityObject target, System.Collections.Generic.IReadOnlyList visibleTargets)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L26) | Applies ordinary, additive or visible-range selection using the active keyboard modifiers.
`interactions`: The session's authoritative interaction service.
`target`: The freshly resolved clicked target.
`visibleTargets`: Current view order; collapsed or filtered items must be omitted. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SearchInput(string id, string hint, ref string query, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L35) | Draws a single-line search field with a stable identifier.
`id`: The stable control identifier.
`hint`: The hint shown while the query is empty.
`query`: The mutable search query.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The control width, or a negative value to fill the available space. | when the query changed; otherwise, . | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginSearchPopup(string id, ref string query, string hint, nuint capacity = 256, float width = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L69) | Begins a popup and draws its focused search field.
`id`: The popup identifier previously supplied to ImGui.
`query`: The mutable search query.
`hint`: The hint shown while the query is empty.
`capacity`: The maximum UTF-8 buffer capacity.
`width`: The search field width, or a negative value to use the editor default. | when popup content should be drawn; otherwise, . | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndSearchPopup()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs#L92) | Ends a popup opened by . | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SelectTarget(Inno.Editor.Interactions.EditorInteractions interactions, Inno.Core.Identity.IdentityObject target, System.Collections.Generic.IReadOnlyList visibleTargets)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L28) | Applies ordinary, additive or visible-range selection using the active keyboard modifiers.
`interactions`: The session's authoritative interaction service.
`target`: The freshly resolved clicked target.
`visibleTargets`: Current runtime-identity view order; collapsed or filtered items must be omitted. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.NavigateList(Inno.Editor.Interactions.EditorInteractions interactions, int itemCount, int focusedIndex, int pageSize = 10)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L74) | Reads one focused list's keyboard navigation without retaining its entries or changing domain selection.
`interactions`: The authoritative interaction owner supplying the product modifier policy.
`itemCount`: The number of currently visible, ordered entries.
`focusedIndex`: The current visible focus index, or minus one when absent.
`pageSize`: The positive number of visible rows advanced by Page Up or Page Down. | Neutral focus and selection intents; text editing, active controls and popups produce no intent. | `System.ArgumentNullException`: The interaction owner is null.
`System.ArgumentOutOfRangeException`: The count, focus index or page size is invalid.
`System.InvalidOperationException`: No matching owner-thread drawing session is active. | | [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginBoundedCombo(string id, string preview, Inno.Editor.ImGui.ImGuiComboFlags flags = Inno.Editor.ImGui.ImGuiComboFlags.None)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L37) | Begins a combo whose popup opens below the control and stays within its containing window.
`id`: Stable combo identifier in the current ImGui scope.
`preview`: Text displayed by the closed combo.
`flags`: Native combo presentation flags. Only is supported. | when the combo popup is open and its contents should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is null.
`System.ArgumentOutOfRangeException`: Thrown when contains a presentation mode unsupported by the editor selector. | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.EndBoundedCombo()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L55) | Ends a combo opened by . | — | — | | [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.BeginMenuSelector(string id, string preview, float width, float minimumPopupWidth)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selector.cs#L84) | Draws a compact selector control and begins a work-area-bounded menu popup.
`id`: Stable selector identifier in the current ImGui scope.
`preview`: Text displayed by the closed selector.
`width`: Width reserved for the selector control.
`minimumPopupWidth`: Minimum outer width of the popup, including its window padding. | when the popup is open and its contents should be submitted; otherwise, . | `System.ArgumentException`: Thrown when is empty.
`System.ArgumentNullException`: Thrown when is .
`System.ArgumentOutOfRangeException`: Thrown when or is not positive. | @@ -2676,8 +2589,12 @@ static void DrawHelp() | [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactClickableTextSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L514) | Gets the compact fixed interaction size used by icon-style clickable text. | A fixed-size area that follows the editor icon slot convention. | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.GetCompactIconSize()`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L527) | Gets the square icon interaction size shared by dock-header close controls and compact editor icons. | A square size based on the current ImGui font size. | — | | [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.IconText(string icon, string text, bool highlight)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Text.cs#L560) | Draws icon and text with the icon's visible glyph bounds centered in a slot that expands when the glyph is wider than the normal editor icon slot.
`icon`: Icon text.
`text`: Main text.
`highlight`: Whether to underline and emphasize the drawn icon and text. | — | — | -| [`public Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TreeNode(string id, System.Action onDraw, in Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions options)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L39) | Draws a full-width interactive tree row whose non-leaf content toggles expansion when double-clicked, while preserving single-click disclosure-arrow behavior.
`id`: Stable row identifier.
`onDraw`: Content drawing callback that receives the native row geometry established by ImGui.
`options`: Tree row options. | Interaction and row geometry for the submitted item. | — | -| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetNextTreeNodeOpen(bool open)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L138) | Overrides the retained expansion state of the next submitted non-leaf tree row.
`open`: Whether the next row should be expanded. | — | — | +| [`public Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.TreeNode(string id, System.Action onDraw, in Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions options)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L42) | Draws a full-width interactive tree row whose non-leaf content toggles expansion when double-clicked, while preserving single-click disclosure-arrow behavior.
`id`: Stable row identifier.
`onDraw`: Frame-local content drawing for visible or explicitly preserved rows; clipped ordinary rows do not invoke it.
`options`: Tree row options. | Interaction and row geometry for the submitted item. | `System.ArgumentOutOfRangeException`: An explicit flattened depth is negative. | +| [`public ulong Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.treeExpansionRevision { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L70) | Gets this window and owner's expansion change counter for invalidating neutral flattened views. | — | `System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.IsTreeNodeOpen(string id)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L94) | Reads a retained expansion value without submitting a native row or retaining domain objects.
`id`: The stable row identifier within this window and owner. | True when the row was explicitly expanded; unknown rows are collapsed. | `System.ArgumentException`: The row identifier is empty or whitespace.
`System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetTreeNodeOpen(string id, bool open)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L116) | Changes retained expansion before a flattened view is computed, without submitting a row.
`id`: The stable row identifier within this window and owner.
`open`: The requested disclosure state. | — | `System.ArgumentException`: The row identifier is empty or whitespace.
`System.InvalidOperationException`: No valid drawing session or native frame is active. | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.SetNextTreeNodeOpen(bool open)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L261) | Overrides the retained expansion state of the next submitted non-leaf tree row.
`open`: Whether the next row should be expanded. | — | — | +| [`public void Inno.Editor.ImGui.ImGuiWidget.ImGuiWidget.Trend(string label, System.ReadOnlySpan values, int offset = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Trend.cs#L34) | Draws a bounded line plot from borrowed numeric samples without copying their buffer.
`label`: The visible plot title and stable item identifier in its current owner.
`values`: Finite samples borrowed only for this call; an empty span draws an empty plot.
`offset`: The first chronological sample in a circular buffer, or zero for empty input. | — | `System.ArgumentException`: The label is empty or a sample is not finite.
`System.ArgumentOutOfRangeException`: The offset is outside the supplied sample span.
`System.InvalidOperationException`: No matching owner-thread drawing session is active. | | [`public enum Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L14) | Chooses the transform handles rendered by the viewport presenter. | — | — | | [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.Translate = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L19) | Renders translation handles. | — | — | | [`Inno.Editor.ImGui.ImGuiWidget.TransformGizmoOperation.Rotate = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Gizmo.cs#L23) | Renders rotation handles. | — | — | @@ -2730,24 +2647,37 @@ static void DrawHelp() | [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Commit = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L224) | Indicates that the edited text should be committed. | — | — | | [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.FocusLost = 2`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L228) | Indicates that the input lost focus and its valid value should be committed before closing. | — | — | | [`Inno.Editor.ImGui.ImGuiWidget.InlineRenameResult.Cancel = 3`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.InlineRename.cs#L232) | Indicates that the edited text should be discarded. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L335) | Provides the native geometry of a tree row while its custom content is being drawn. | — | — | -| [`public float Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L345) | Gets the actual native row height established for the current tree node. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L351) | Configures an interactive tree row. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.selected { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L356) | Gets whether the row is selected. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.isLeaf { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L360) | Gets whether the row has no expandable children. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.showBackground { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L364) | Gets whether a custom background is drawn behind an unselected row. | — | — | -| [`public System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.backgroundColor { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L368) | Gets the custom background color used when is enabled. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.suppressHoverHighlight { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L372) | Gets whether the row keeps its configured background while hovered. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.hideGuideLines { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L376) | Gets whether ancestor and branch guide lines are omitted for this row. | — | — | -| [`public System.Action? Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.drawViewportOverlay { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L381) | Gets the callback that draws controls fixed to the current viewport without extending the tree's horizontal content boundary. | — | — | -| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L387) | Describes interaction state produced by a tree row. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isOpen { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L410) | Gets whether child content should be rendered. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L414) | Gets whether the content row was clicked. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isDoubleClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L419) | Gets whether the content row was double-clicked. Double-clicking a non-leaf content row also toggles its retained expansion state for the next frame. | — | — | -| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L423) | Gets whether the full row is hovered. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.min { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L427) | Gets the row minimum screen coordinate. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.max { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L431) | Gets the row maximum screen coordinate. | — | — | -| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.contentMin { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L435) | Gets the minimum screen coordinate of the row's interactive content, excluding tree indentation. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L166) | Carries frame-local list navigation intents over visible indices without owning objects or selection state. | — | — | +| [`public int Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.focusedIndex { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L191) | Gets the resulting visible focus index, or minus one for no focus. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.focusChanged { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L195) | Gets whether the visible focus index changed. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.selectionRequested { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L199) | Gets whether the caller should apply a domain selection operation. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.extendRange { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L203) | Gets whether the selection should extend from its existing anchor. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.additive { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L207) | Gets whether the product's primary modifier requests preserving other selected entries. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.toggle { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L211) | Gets whether Space with the primary modifier requests toggling the focused entry. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.activate { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L215) | Gets whether Enter requests activation of the focused entry. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult.cancel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs#L219) | Gets whether Escape requests canceling the current list workflow. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L464) | Provides the native geometry of a tree row while its custom content is being drawn. | — | — | +| [`public float Inno.Editor.ImGui.ImGuiWidget.TreeNodeDrawContext.rowHeight { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L474) | Gets the actual native row height established for the current tree node. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L480) | Configures an interactive tree row. | — | — | +| [`public int? Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.depth { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L486) | Gets explicit indentation for a flattened row, or null for a nested native tree. Explicit-depth rows never push a native tree scope; callers must not call TreePop for them. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.requestFocus { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L490) | Gets whether this row should receive keyboard focus during this submission when its tree window is focused. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.keepContentActive { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L495) | Gets whether an active edit requires content submission even while its row is clipped. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.selected { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L500) | Gets whether the row is selected. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.isLeaf { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L504) | Gets whether the row has no expandable children. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.showBackground { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L508) | Gets whether a custom background is drawn behind an unselected row. | — | — | +| [`public System.Numerics.Vector4 Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.backgroundColor { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L512) | Gets the custom background color used when is enabled. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.suppressHoverHighlight { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L516) | Gets whether the row keeps its configured background while hovered. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.hideGuideLines { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L520) | Gets whether ancestor and branch guide lines are omitted for this row. | — | — | +| [`public System.Action? Inno.Editor.ImGui.ImGuiWidget.TreeNodeOptions.drawViewportOverlay { get; init; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L525) | Gets the callback that draws controls fixed to the current viewport without extending the tree's horizontal content boundary. | — | — | +| [`public readonly struct Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L531) | Describes interaction state produced by a tree row. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isSubmitted { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L556) | Gets whether row controls were submitted; ordinary clipped rows are false, while preserved active edits remain true. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isOpen { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L561) | Gets whether child content should be rendered. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L565) | Gets whether the content row was clicked. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isDoubleClicked { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L570) | Gets whether the content row was double-clicked. Double-clicking a non-leaf content row also toggles its retained expansion state for the next frame. | — | — | +| [`public bool Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.isHovered { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L574) | Gets whether the full row is hovered. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.min { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L578) | Gets the row minimum screen coordinate. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.max { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L582) | Gets the row maximum screen coordinate. | — | — | +| [`public System.Numerics.Vector2 Inno.Editor.ImGui.ImGuiWidget.TreeNodeResult.contentMin { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs#L586) | Gets the minimum screen coordinate of the row's interactive content, excluding tree indentation. | — | — | ## 项目依赖 diff --git a/docs/editor/Inno.Editor.Interactions.md b/docs/editor/Inno.Editor.Interactions.md index cb3d8c50f..c16af315b 100644 --- a/docs/editor/Inno.Editor.Interactions.md +++ b/docs/editor/Inno.Editor.Interactions.md @@ -799,6 +799,7 @@ Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重 + @@ -811,7 +812,7 @@ Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重 - + @@ -1319,25 +1320,25 @@ Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重 | [`public Inno.Editor.Interactions.EditorMenuSourceAttribute.EditorMenuSourceAttribute(string area, int priority = 0)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L23) | Creates a dynamic menu-source registration for an exact interaction surface.
`area`: The exact menu area that receives placements from the source.
`priority`: The ordering priority used when multiple dynamic sources contribute to the surface. | — | `System.ArgumentException`: Thrown when is empty. | | [`public string Inno.Editor.Interactions.EditorMenuSourceAttribute.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L36) | Gets the contributed menu area. | — | — | | [`public int Inno.Editor.Interactions.EditorMenuSourceAttribute.priority { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Menus/EditorMenuSourceAttribute.cs#L41) | Gets the provider ordering priority. | — | — | -| [`public readonly struct Inno.Editor.Interactions.EditorInteraction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L8) | Provides a lightweight fluent handle for one interaction area and optional target. | — | — | -| [`public string Inno.Editor.Interactions.EditorInteraction.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L25) | Gets the stable interaction area. | — | — | -| [`public object? Inno.Editor.Interactions.EditorInteraction.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L29) | Gets the optional target represented by this handle. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.isSelected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L33) | Gets whether this handle's target is the current editor selection. | — | — | -| [`public void Inno.Editor.Interactions.EditorInteraction.Focus()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L38) | Marks this area and target as the active keyboard context. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Select()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L45) | Selects this target, or clears selection when the target is . | when the built-in selection action executed; otherwise, . | — | -| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorInteraction.Query(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L61) | Queries an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | The current action state. | `System.ArgumentException`: Thrown when is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Execute(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L80) | Executes an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | when a visible and enabled action executed; otherwise, . | `System.ArgumentException`: Thrown when is empty. | -| [`public void Inno.Editor.Interactions.EditorInteraction.Enqueue(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L96) | Queues an action until the current UI traversal completes.
`action`: The stable action name.
`argument`: An optional action argument. | — | `System.ArgumentException`: Thrown when is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.Present(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L115) | Presents an active action in place of this target's normal content.
`action`: The stable action name.
`argument`: Optional presentation data supplied by the current view. | when the action presented content; otherwise, . | `System.ArgumentException`: Thrown when is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.IsActive(string action)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L131) | Gets whether an action owns an active multi-frame operation for this target.
`action`: The stable action name. | when the action is active for this exact target; otherwise, . | `System.ArgumentException`: Thrown when is empty. | -| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildMenu()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L138) | Builds the complete contextual menu for this area and target. | The immutable resolved menu model. | — | -| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildCommands()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L145) | Builds searchable commands from this area's menus and applicable unplaced Actions. | Current command states and neutral placement arguments; execution revalidates their targets. | — | -| [`public Inno.Editor.Interactions.EditorToolbarModel Inno.Editor.Interactions.EditorInteraction.BuildToolbar()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L152) | Builds the complete compact toolbar for this area and target. | The immutable resolved toolbar model. | — | -| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetShortcut(string action, out Inno.Editor.Interactions.HotKeyGesture gesture)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L168) | Resolves the shortcut displayed for an action in this area.
`action`: The stable action name.
`gesture`: The resolved gesture when successful. | when a compatible shortcut exists; otherwise, . | `System.ArgumentException`: Thrown when is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L181) | Gets the currently active drag identity, if its source remains registered.
`identity`: The active domain-qualified runtime identity when available. | when a live drag source exists; otherwise, . | — | -| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorInteraction.BeginDrag(Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L191) | Begins a managed drag originating from this area.
`data`: The managed source data. | The source object's domain-qualified transient identity. | — | -| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorInteraction.QueryDrop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L204) | Queries this handle as a drop target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The drop compatibility and visual state. | — | -| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorInteraction.Drop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L220) | Delivers a managed drop to this target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The completed drop result. | — | +| [`public readonly struct Inno.Editor.Interactions.EditorInteraction`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L14) | Provides a fluent handle that borrows an optional target for a synchronous interaction request. | — | — | +| [`public string Inno.Editor.Interactions.EditorInteraction.area { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L31) | Gets the stable interaction area. | — | — | +| [`public object? Inno.Editor.Interactions.EditorInteraction.target { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L35) | Gets the borrowed target without extending its validity or resolving a replacement generation. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.isSelected { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L39) | Gets whether this handle's target is the current editor selection. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteraction.Focus()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L44) | Marks this area and target as the active keyboard context. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Select()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L51) | Selects this target, or clears selection when the target is . | when the built-in selection action executed; otherwise, . | — | +| [`public Inno.Editor.Interactions.EditorActionState Inno.Editor.Interactions.EditorInteraction.Query(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L67) | Queries an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | The current action state. | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Execute(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L86) | Executes an action for this area and target.
`action`: The stable action name.
`argument`: An optional action argument. | when a visible and enabled action executed; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public void Inno.Editor.Interactions.EditorInteraction.Enqueue(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L102) | Queues an action until the current UI traversal completes.
`action`: The stable action name.
`argument`: An optional action argument. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.Present(string action, object? argument = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L121) | Presents an active action in place of this target's normal content.
`action`: The stable action name.
`argument`: Optional presentation data supplied by the current view. | when the action presented content; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.IsActive(string action)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L137) | Gets whether an action owns an active multi-frame operation for this target.
`action`: The stable action name. | when the action is active for this exact target; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildMenu()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L144) | Builds the complete contextual menu for this area and target. | The immutable resolved menu model. | — | +| [`public Inno.Editor.Interactions.EditorMenuModel Inno.Editor.Interactions.EditorInteraction.BuildCommands()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L151) | Builds searchable commands from this area's menus and applicable unplaced Actions. | Current command states and neutral placement arguments; execution revalidates their targets. | — | +| [`public Inno.Editor.Interactions.EditorToolbarModel Inno.Editor.Interactions.EditorInteraction.BuildToolbar()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L158) | Builds the complete compact toolbar for this area and target. | The immutable resolved toolbar model. | — | +| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetShortcut(string action, out Inno.Editor.Interactions.HotKeyGesture gesture)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L174) | Resolves the shortcut displayed for an action in this area.
`action`: The stable action name.
`gesture`: The resolved gesture when successful. | when a compatible shortcut exists; otherwise, . | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteraction.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L187) | Gets the currently active drag identity, if its source remains registered.
`identity`: The active domain-qualified runtime identity when available. | when a live drag source exists; otherwise, . | — | +| [`public Inno.Core.Identity.RuntimeIdentity Inno.Editor.Interactions.EditorInteraction.BeginDrag(Inno.Editor.Interactions.EditorDragData data)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L197) | Begins a managed drag originating from this area.
`data`: The managed source data. | The source object's domain-qualified transient identity. | — | +| [`public Inno.Editor.Interactions.EditorDropStatus Inno.Editor.Interactions.EditorInteraction.QueryDrop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L210) | Queries this handle as a drop target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The drop compatibility and visual state. | — | +| [`public Inno.Editor.Interactions.EditorDropResult Inno.Editor.Interactions.EditorInteraction.Drop(Inno.Core.Identity.RuntimeIdentity identity, Inno.Editor.Interactions.EditorDropPlacement placement = Inno.Editor.Interactions.EditorDropPlacement.None)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs#L226) | Delivers a managed drop to this target.
`identity`: The active domain-qualified runtime identity.
`placement`: The requested placement relative to the target. | The completed drop result. | — | | [`public sealed class Inno.Editor.Interactions.EditorInteractionRuntime : Inno.Editor.Core.EditorRuntime`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L17) | Hosts attribute-discovered editor extensions without depending on a presentation backend. | — | — | | [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(string projectDirectory, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs, Inno.Editor.Core.EditorKeyboardPolicy keyboard)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L46) | Creates an interaction runtime for one project.
`projectDirectory`: The project root containing Assets and Library.
`types`: The type catalog that owns editor extension generations.
`logs`: The host-owned logging router used by editor infrastructure.
`keyboard`: The explicit immutable product keyboard conventions. | — | — | | [`public Inno.Editor.Interactions.EditorInteractionRuntime.EditorInteractionRuntime(Inno.Editor.Core.EditorContext context, Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Logging.LogRouter logs)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractionRuntime.cs#L71) | Creates an interaction runtime for an existing passive editor context.
`context`: The passive editor context shared with the presentation backend.
`types`: The type catalog that owns editor extension generations.
`logs`: The host-owned logging router used by editor infrastructure. | — | `System.ArgumentNullException`: Thrown when or is . | @@ -1356,27 +1357,28 @@ Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重 | [`public sealed class Inno.Editor.Interactions.EditorInteractions : Inno.Editor.Interactions.IEditorSelectionCoordinator, Inno.Editor.Interactions.IEditorHistoryIsolation`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L15) | Provides the single presentation-independent entry point for editor actions, menus, selection, focus, and drag-and-drop. | — | — | | [`public Inno.Core.Events.EventDispatcher Inno.Editor.Interactions.EditorInteractions.events { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L57) | Gets synchronous Editor lifecycle notifications implemented by the Core event dispatcher. | — | — | | [`public Inno.Editor.Interactions.EditorSelectionState Inno.Editor.Interactions.EditorInteractions.selection { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L63) | Gets the shared read-only editor selection state. | — | — | -| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorInteractions.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L69) | Gets the transactional Undo and Redo history owned by this editor runtime. | — | — | -| [`public Inno.Editor.Interactions.IEditorDocumentService Inno.Editor.Interactions.EditorInteractions.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L73) | Gets the headless reload-safe document lifetime used by dedicated asset editors and Inspectors. | — | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetModule(out TModule? module) where TModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L90) | Resolves an active feature module for immediate use in the current Editor callback.
`module`: The started, non-quarantined module, or null when unavailable.
`TModule`: The feature module contract to resolve. | True when the active generation supplies the requested module. | — | -| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L113) | Starts an isolated temporary Undo and Redo branch while retaining the current editing branch. | A scope that restores the retained history branch when disposed. | `System.InvalidOperationException`: Thrown during an Undo, Redo, transaction, or another isolated branch. | -| [`public string Inno.Editor.Interactions.EditorInteractions.focusedArea { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L118) | Gets the area that most recently received keyboard focus. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractions.GetPanelDeclarations()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L126) | Gets immutable discovery metadata for the active, available panels. | Current panel declarations; the snapshot contains no panel or extension instances. | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetPanelState(string panelId, out bool isOpen)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L139) | Reads whether one available panel is currently open.
`panelId`: The stable panel identity.
`isOpen`: Receives the open state, or false when the panel is unavailable. | True when the current generation contains the requested available panel. | — | -| [`public object? Inno.Editor.Interactions.EditorInteractions.focusedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L151) | Gets the target associated with the focused area. | — | — | -| [`public Inno.Editor.Interactions.EditorInteraction Inno.Editor.Interactions.EditorInteractions.For(string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L168) | Creates a lightweight interaction handle for one area and optional target.
`area`: The stable interaction area.
`target`: The optional object represented by the area. | A lightweight interaction handle. | `System.ArgumentException`: Thrown when is empty. | -| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L178) | Replaces the editor selection after closing presentations owned by other targets.
`target`: The target to select, or to clear the selection. | — | — | -| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(System.Collections.Generic.IEnumerable targets, Inno.Core.Identity.IdentityObject? anchor)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L203) | Replaces the ordered selection; duplicates are removed and the final target becomes primary.
`targets`: Registered objects resolved for the current callback.
`anchor`: The range anchor, or null to use the first selected target. | — | `System.ArgumentNullException`: The target collection or one member is null.
`System.InvalidOperationException`: A target has no live identity registration. | -| [`public void Inno.Editor.Interactions.EditorInteractions.ToggleSelection(Inno.Core.Identity.IdentityObject target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L221) | Adds or removes a member while preserving the existing range anchor.
`target`: A currently registered selection target. | — | `System.ArgumentNullException`: The target is null. | -| [`public void Inno.Editor.Interactions.EditorInteractions.SelectRange(System.Collections.Generic.IReadOnlyList orderedTargets, Inno.Core.Identity.IdentityObject target, bool additive = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L250) | Selects the inclusive visible range between the current anchor and the clicked target.
`orderedTargets`: Targets in the current visible sort and filter order.
`target`: The clicked range endpoint, which becomes primary.
`additive`: Whether to preserve existing members outside the selected range. | — | `System.ArgumentNullException`: The visible targets or endpoint is null.
`System.ArgumentException`: The endpoint is not in the visible target order. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetDragSource(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? source)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L303) | Resolves the live source for an active runtime identity.
`identity`: The domain-qualified runtime identity carried by the drag protocol.
`source`: The currently registered drag source when resolution succeeds. | when the identity is active and resolves to a live source; otherwise, . | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L316) | Gets the active drag identity used to select the matching native payload domain.
`identity`: The active runtime identity when one remains valid. | when a live drag source exists; otherwise, . | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TogglePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L329) | Toggles one panel in the currently active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found and toggled. | `System.ArgumentException`: Thrown when is empty. | -| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.RegisterIdentityDomain(Inno.Core.Identity.IdentityAllocator allocator)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L354) | Makes a temporary Core identity domain available to selection, focus and drag resolution.
`allocator`: The borrowed allocator owned by the temporary runtime session. | A routing lease; retire it after restoring Edit selection and before releasing the session. | `System.ArgumentNullException`: The allocator is null.
`System.InvalidOperationException`: The domain is already registered or the caller is not the owner thread.
`System.ObjectDisposedException`: The interaction runtime has retired its identity routing. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L377) | Resolves a domain-qualified runtime identity through the Editor's complete identity-domain set.
`identity`: Runtime identity captured from a live Editor target.
`target`: Receives the current live object, or when its domain or generation is unavailable. | when the exact identity resolves in its owning domain. | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.IdentityDomainId domainId, System.Guid persistentId, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L401) | Resolves a persistent identity through one explicitly selected Editor identity domain.
`domainId`: Identity domain that owns the object and any replacement generation.
`persistentId`: Stable object identity to resolve inside that domain.
`target`: Receives the current live object, or when the domain or object is unavailable. | when the stable identity resolves in the requested domain. | — | -| [`public bool Inno.Editor.Interactions.EditorInteractions.OpenPanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L422) | Opens and requests presentation focus for one panel in the active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | -| [`public bool Inno.Editor.Interactions.EditorInteractions.ClosePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L440) | Closes one panel in the active extension generation without toggling its current state.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | +| [`public Inno.Editor.Core.EditorKeyboardPolicy Inno.Editor.Interactions.EditorInteractions.keyboard { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L68) | Gets the immutable product keyboard policy used by commands and selection controls. | — | — | +| [`public Inno.Editor.Interactions.IEditorHistory Inno.Editor.Interactions.EditorInteractions.history { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L74) | Gets the transactional Undo and Redo history owned by this editor runtime. | — | — | +| [`public Inno.Editor.Interactions.IEditorDocumentService Inno.Editor.Interactions.EditorInteractions.documents { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L78) | Gets the headless reload-safe document lifetime used by dedicated asset editors and Inspectors. | — | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetModule(out TModule? module) where TModule : Inno.Editor.Core.EditorModule`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L95) | Resolves an active feature module for immediate use in the current Editor callback.
`module`: The started, non-quarantined module, or null when unavailable.
`TModule`: The feature module contract to resolve. | True when the active generation supplies the requested module. | — | +| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.BeginHistoryIsolation()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L118) | Starts an isolated temporary Undo and Redo branch while retaining the current editing branch. | A scope that restores the retained history branch when disposed. | `System.InvalidOperationException`: Thrown during an Undo, Redo, transaction, or another isolated branch. | +| [`public string Inno.Editor.Interactions.EditorInteractions.focusedArea { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L123) | Gets the area that most recently received keyboard focus. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Interactions.EditorInteractions.GetPanelDeclarations()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L131) | Gets immutable discovery metadata for the active, available panels. | Current panel declarations; the snapshot contains no panel or extension instances. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetPanelState(string panelId, out bool isOpen)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L144) | Reads whether one available panel is currently open.
`panelId`: The stable panel identity.
`isOpen`: Receives the open state, or false when the panel is unavailable. | True when the current generation contains the requested available panel. | — | +| [`public object? Inno.Editor.Interactions.EditorInteractions.focusedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L156) | Gets the target associated with the focused area. | — | — | +| [`public Inno.Editor.Interactions.EditorInteraction Inno.Editor.Interactions.EditorInteractions.For(string area, object? target = null)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L173) | Creates a lightweight interaction handle for one area and optional target.
`area`: The stable interaction area.
`target`: The optional object represented by the area. | A lightweight interaction handle. | `System.ArgumentException`: Thrown when is empty. | +| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L183) | Replaces the editor selection after closing presentations owned by other targets.
`target`: The target to select, or to clear the selection. | — | — | +| [`public void Inno.Editor.Interactions.EditorInteractions.SetSelection(System.Collections.Generic.IEnumerable targets, Inno.Core.Identity.IdentityObject? anchor)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L208) | Replaces the ordered selection; duplicates are removed and the final target becomes primary.
`targets`: Registered objects resolved for the current callback.
`anchor`: The range anchor, or null to use the first selected target. | — | `System.ArgumentNullException`: The target collection or one member is null.
`System.InvalidOperationException`: A target has no live identity registration. | +| [`public void Inno.Editor.Interactions.EditorInteractions.ToggleSelection(Inno.Core.Identity.IdentityObject target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L226) | Adds or removes a member while preserving the existing range anchor.
`target`: A currently registered selection target. | — | `System.ArgumentNullException`: The target is null. | +| [`public void Inno.Editor.Interactions.EditorInteractions.SelectRange(System.Collections.Generic.IReadOnlyList orderedTargets, Inno.Core.Identity.IdentityObject target, bool additive = false)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L258) | Selects the inclusive visible range between the current anchor and the clicked target.
`orderedTargets`: Runtime identities in the current visible sort and filter order, resolved through Core Identity only when selected.
`target`: The clicked range endpoint, which becomes primary.
`additive`: Whether to preserve existing members outside the selected range. | — | `System.ArgumentNullException`: The visible targets or endpoint is null.
`System.ArgumentException`: The endpoint is not in the visible target order.
`System.InvalidOperationException`: A range identity is unavailable; the current selection remains unchanged. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetDragSource(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? source)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L313) | Resolves the live source for an active runtime identity.
`identity`: The domain-qualified runtime identity carried by the drag protocol.
`source`: The currently registered drag source when resolution succeeds. | when the identity is active and resolves to a live source; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryGetActiveDragIdentity(out Inno.Core.Identity.RuntimeIdentity identity)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L326) | Gets the active drag identity used to select the matching native payload domain.
`identity`: The active runtime identity when one remains valid. | when a live drag source exists; otherwise, . | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TogglePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L339) | Toggles one panel in the currently active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found and toggled. | `System.ArgumentException`: Thrown when is empty. | +| [`public System.IDisposable Inno.Editor.Interactions.EditorInteractions.RegisterIdentityDomain(Inno.Core.Identity.IdentityAllocator allocator)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L364) | Makes a temporary Core identity domain available to selection, focus and drag resolution.
`allocator`: The borrowed allocator owned by the temporary runtime session. | A routing lease; retire it after restoring Edit selection and before releasing the session. | `System.ArgumentNullException`: The allocator is null.
`System.InvalidOperationException`: The domain is already registered or the caller is not the owner thread.
`System.ObjectDisposedException`: The interaction runtime has retired its identity routing. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.RuntimeIdentity identity, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L387) | Resolves a domain-qualified runtime identity through the Editor's complete identity-domain set.
`identity`: Runtime identity captured from a live Editor target.
`target`: Receives the current live object, or when its domain or generation is unavailable. | when the exact identity resolves in its owning domain. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.TryResolveIdentity(Inno.Core.Identity.IdentityDomainId domainId, System.Guid persistentId, out Inno.Core.Identity.IdentityObject? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L411) | Resolves a persistent identity through one explicitly selected Editor identity domain.
`domainId`: Identity domain that owns the object and any replacement generation.
`persistentId`: Stable object identity to resolve inside that domain.
`target`: Receives the current live object, or when the domain or object is unavailable. | when the stable identity resolves in the requested domain. | — | +| [`public bool Inno.Editor.Interactions.EditorInteractions.OpenPanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L432) | Opens and requests presentation focus for one panel in the active extension generation.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | +| [`public bool Inno.Editor.Interactions.EditorInteractions.ClosePanel(string panelId)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs#L450) | Closes one panel in the active extension generation without toggling its current state.
`panelId`: The stable panel identifier to resolve. | when an available panel was found. | `System.ArgumentException`: Thrown when is empty. | | [`public sealed class Inno.Editor.Interactions.EditorModalExtension`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L11) | Describes one active modal extension. | — | — | | [`public string Inno.Editor.Interactions.EditorModalExtension.id { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L34) | Gets the stable modal identifier. | — | — | | [`public string Inno.Editor.Interactions.EditorModalExtension.title { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorModalExtension.cs#L39) | Gets the visible modal title. | — | — | @@ -1416,14 +1418,14 @@ Action 的延迟 target、focus 和选择均使用 Core Identity;执行前重 | [`public int Inno.Editor.Interactions.EditorSelectionSnapshot.CopyTargets(Inno.Editor.Interactions.EditorInteractions interactions, System.Collections.Generic.ICollection destination) where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L46) | Resolves this immutable selection in its original domains, including same-identity recovery after a generation change.
`interactions`: The active session supplying the original identity domains.
`destination`: A temporary caller-owned collection to clear and fill.
`TTarget`: The target type accepted by the current presenter. | The number of currently available compatible targets; missing members remain represented by this snapshot. | — | | [`public Inno.Core.Identity.RuntimeIdentity? Inno.Editor.Interactions.EditorSelectionSnapshot.primary { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionSnapshot.cs#L64) | Gets the primary identity, or null when this snapshot contains no registered targets. | — | — | | [`public sealed class Inno.Editor.Interactions.EditorSelectionState`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L10) | Owns ordered transient selection through domain-qualified identities instead of retaining live objects. | — | — | -| [`public object? Inno.Editor.Interactions.EditorSelectionState.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L24) | Gets the current primary target for this callback, or null when unavailable or unselected. | — | — | -| [`public int Inno.Editor.Interactions.EditorSelectionState.count { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L28) | Gets the number of selected identities, including temporarily unavailable members. | — | — | -| [`public object? Inno.Editor.Interactions.EditorSelectionState.anchor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L32) | Gets the current range anchor, or null when the anchor is unavailable. | — | — | -| [`public ulong Inno.Editor.Interactions.EditorSelectionState.version { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L36) | Gets the selection revision, incremented only when membership, order or resolved generation changes. | — | — | -| [`public bool Inno.Editor.Interactions.EditorSelectionState.Contains(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L47) | Checks membership by the target's owning identity domain and generation.
`target`: The immediate target to inspect. | True when the target is a member of this selection. | — | -| [`public int Inno.Editor.Interactions.EditorSelectionState.CopyTargets(System.Collections.Generic.ICollection destination) where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L75) | Copies currently available selected targets into a caller-owned temporary collection.
`destination`: The collection to clear and fill; do not retain live entries across callbacks.
`TTarget`: The target type accepted by the caller. | The number of targets resolved in this callback. | `System.ArgumentNullException`: The destination is null. | -| [`public Inno.Editor.Interactions.EditorSelectionSnapshot Inno.Editor.Interactions.EditorSelectionState.Capture()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L95) | Captures the ordered runtime identities without retaining their resolved objects. | An immutable callback-independent snapshot; resolve identities afresh before using targets. | — | -| [`public bool Inno.Editor.Interactions.EditorSelectionState.TryGet(out TTarget? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L114) | Resolves the primary selected target as the requested type for immediate use.
`target`: Receives the available primary target, or null.
`TTarget`: The expected target type. | True when the current primary target has the requested type. | — | +| [`public object? Inno.Editor.Interactions.EditorSelectionState.selectedTarget { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L25) | Gets the current primary target for this callback, or null when unavailable or unselected. | — | — | +| [`public int Inno.Editor.Interactions.EditorSelectionState.count { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L29) | Gets the number of selected identities, including temporarily unavailable members. | — | — | +| [`public object? Inno.Editor.Interactions.EditorSelectionState.anchor { get; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L33) | Gets the current range anchor, or null when the anchor is unavailable. | — | — | +| [`public ulong Inno.Editor.Interactions.EditorSelectionState.version { get; private set; }`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L37) | Gets the selection revision, incremented only when membership, order or resolved generation changes. | — | — | +| [`public bool Inno.Editor.Interactions.EditorSelectionState.Contains(object? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L48) | Checks membership by the target's owning identity domain and generation.
`target`: The immediate target to inspect. | True when the target is a member of this selection. | — | +| [`public int Inno.Editor.Interactions.EditorSelectionState.CopyTargets(System.Collections.Generic.ICollection destination) where TTarget : class`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L78) | Copies currently available selected targets into a caller-owned temporary collection.
`destination`: The collection to clear and fill; do not retain live entries across callbacks.
`TTarget`: The target type accepted by the caller. | The number of targets resolved in this callback. | `System.ArgumentNullException`: The destination is null. | +| [`public Inno.Editor.Interactions.EditorSelectionSnapshot Inno.Editor.Interactions.EditorSelectionState.Capture()`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L98) | Captures the ordered runtime identities without retaining their resolved objects. | An immutable callback-independent snapshot; resolve identities afresh before using targets. | — | +| [`public bool Inno.Editor.Interactions.EditorSelectionState.TryGet(out TTarget? target)`](../../src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs#L117) | Resolves the primary selected target as the requested type for immediate use.
`target`: Receives the available primary target, or null.
`TTarget`: The expected target type. | True when the current primary target has the requested type. | — | | [`public enum Inno.Editor.Interactions.EditorToolbarIcon : int`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L6) | Identifies a presentation-independent symbol for an editor toolbar command. | — | — | | [`Inno.Editor.Interactions.EditorToolbarIcon.None = 0`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L11) | No symbol is requested. | — | — | | [`Inno.Editor.Interactions.EditorToolbarIcon.Play = 1`](../../src/composition/editor/framework/Inno.Editor.Interactions/Toolbars/EditorToolbarIcon.cs#L16) | Starts an operation or simulation. | — | — | diff --git a/docs/editor/Inno.Editor.Panel.Hierarchy.md b/docs/editor/Inno.Editor.Panel.Hierarchy.md index 82f0d4e53..14881d1b3 100644 --- a/docs/editor/Inno.Editor.Panel.Hierarchy.md +++ b/docs/editor/Inno.Editor.Panel.Hierarchy.md @@ -4,6 +4,8 @@ 该项目拥有 Hierarchy Panel、Scene/GameObject Action、上下文菜单以及 Scene/GameObject 拖放排序。Scene document 和可逆编辑实现位于独立的 `Inno.Editor.Scene`;Hierarchy 只把查询/工作流交给 `IEditorSceneWorkspace`,把可逆修改交给 `SceneEdits`,不维护图快照或 Undo 实现。 +内部 `Styling/HierarchyStyle` 拥有层级行距、重命名与空白拖放区域的尺寸,以及 inactive 行的颜色。可见行缓存只保存 Core runtime identity 和 depth,并由 Scene hierarchy revision 与展开状态更新;正常离屏行不提交完整控件。公开 drop target 是当前同步请求的借用,不可保存到后续帧、History 或 generation。 + ## 公共 API | API | 作用 | diff --git a/docs/editor/Inno.Editor.Panel.Inspector.md b/docs/editor/Inno.Editor.Panel.Inspector.md index a9f834f3d..87468042f 100644 --- a/docs/editor/Inno.Editor.Panel.Inspector.md +++ b/docs/editor/Inno.Editor.Panel.Inspector.md @@ -100,14 +100,14 @@ EditorScripts 使用 `InnoEditor.Inspection`,可声明 InspectionDrawer、Prop | API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public sealed class Inno.Editor.Panel.Inspector.AssetReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L8) | Provides an assignable asset-reference property drop target. | — | — | -| [`public Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.AssetReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L28) | Creates a drop target that validates an asset type and assigns its persistent identity to a property.
`expectedType`: The runtime asset type accepted by the property.
`assign`: The callback that writes an accepted persistent identity to the property. | — | `System.ArgumentNullException`: Thrown when either argument is .
`System.InvalidOperationException`: Thrown when the type does not belong to the active type catalog. | -| [`public System.Type Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L40) | Gets the required asset type. | — | — | -| [`public void Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.Assign(System.Guid persistentId)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L48) | Assigns a persistent asset identity to the represented property.
`persistentId`: The stable persistent identity used for lookup. | — | — | -| [`public sealed class Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L11) | Provides an assignable engine-object-reference property drop target. | — | — | -| [`public Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.EngineObjectReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L31) | Creates a drop target that validates and assigns an engine object to a serialized property.
`expectedType`: The engine object type accepted by the property.
`assign`: The callback that writes an accepted object to the property. | — | `System.ArgumentNullException`: Thrown when either argument is .
`System.InvalidOperationException`: Thrown when the type does not belong to the active type catalog. | -| [`public System.Type Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L43) | Gets the required engine object type. | — | — | -| [`public void Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.Assign(Inno.Scene.EngineObject value)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L51) | Assigns an accepted engine object to the represented property.
`value`: The concrete value read or transformed by this operation. | — | — | +| [`public sealed class Inno.Editor.Panel.Inspector.AssetReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L12) | Borrows an asset property's type and assignment callback for one synchronous drop request. | — | — | +| [`public Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.AssetReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L29) | Creates a drop target that validates an asset type and assigns its persistent identity to a property.
`expectedType`: The runtime asset type accepted by the property.
`assign`: The callback that writes an accepted persistent identity to the property. | — | `System.ArgumentNullException`: Thrown when either argument is . | +| [`public System.Type Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L41) | Gets the required asset type. | — | — | +| [`public void Inno.Editor.Panel.Inspector.AssetReferenceDropTarget.Assign(System.Guid persistentId)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs#L49) | Assigns a persistent asset identity to the represented property.
`persistentId`: The stable persistent identity used for lookup. | — | — | +| [`public sealed class Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L15) | Borrows an object property's type and assignment callback for one synchronous drop request. | — | — | +| [`public Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.EngineObjectReferenceDropTarget(System.Type expectedType, System.Action assign)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L32) | Creates a drop target that validates and assigns an engine object to a serialized property.
`expectedType`: The engine object type accepted by the property.
`assign`: The callback that writes an accepted object to the property. | — | `System.ArgumentNullException`: Thrown when either argument is . | +| [`public System.Type Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.expectedType { get; }`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L44) | Gets the required engine object type. | — | — | +| [`public void Inno.Editor.Panel.Inspector.EngineObjectReferenceDropTarget.Assign(Inno.Scene.EngineObject value)`](../../src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs#L52) | Assigns an accepted engine object to the represented property.
`value`: The concrete value read or transformed by this operation. | — | — | ## 项目依赖 diff --git a/docs/editor/Inno.Editor.Panel.Logging.md b/docs/editor/Inno.Editor.Panel.Logging.md index 408ec767b..6fa6a8e18 100644 --- a/docs/editor/Inno.Editor.Panel.Logging.md +++ b/docs/editor/Inno.Editor.Panel.Logging.md @@ -4,11 +4,15 @@ 该项目同时订阅彼此独立的 `Inno.Core.Logging` 与 `Inno.Core.Diagnostics`,并把两种数据组合成统一的 Editor Console。目录和项目名使用 `Logging`,避免 `Log` 被 ignore 规则或输出目录约定误判。 +内部 `Styling/ConsoleStyle` 拥有等级颜色、日志卡片、展开按钮与自动滚动容差;通用 Palette 不再保存 Console 策略。这些内部值不属于脚本公开 API。可变高度测量和原生子窗口槽位由 Panel 维护,按内容、宽度、尺度和展开状态失效,退休记录不会按操作次数积累原生窗口。 + ## 组成 - `EditorConsole`:位于 `Inno.Editor.Diagnostics`,拥有有界日志、当前诊断、全局 fingerprint 分组和 Play Session 保留策略。 - `ConsolePanel`:只读取 `IEditorConsole` 的不可变 Snapshot,负责等级过滤、折叠、详情和操作表现。 +搜索采用不区分大小写的普通子串匹配,覆盖 message、source、file、code、category 和已有 stack trace;与等级过滤共同生效。派生视图只在 snapshot revision、查询或筛选变化时更新,不修改 Diagnostics 原始记录。Collapse 只统计匹配 occurrences,展开详情和 Copy Message/Copy Full Entry 指向同一匹配组。复制目标由当前视图拥有,Clear、过滤失去目标或 Panel 退休时释放;不会为每个可见行预先编码完整复制文本。当前选择支持键盘导航与 Enter 展开,主修饰键+C / 主修饰键+Shift+C 复用同一 Action。 + Console 根 Panel 与布局 Child 不滚动,仅 entry region 承担纵向滚动,避免父子同时显示滚动条。当滚动条原本位于底部时,新日志会继续滚到底部;用户向上浏览后不会抢夺滚动位置。按钮、折叠三角、行距与颜色全部来自 `ImGuiWidget`、`EditorStyleMetrics` 和 `EditorPalette`。相邻 card 只使用一次标准 `ItemSpacing`,不会再叠加额外占位元素,因此展开与收起状态都保持确定、均匀的外部间距。 内部实现中,Console entry identity 由来源种类与完整 64 位序号共同组成。普通 Log 与 Diagnostic 的计数空间彼此独立,ImGui scope 会分别压入来源、序号高位和低位,不能直接使用 `long.GetHashCode()` 代替身份;后者会让部分正负序号产生相同的 32 位值,并导致 auto-resize child 错误复用另一张 card 的布局状态。 diff --git a/docs/editor/Inno.Editor.Panel.Settings.md b/docs/editor/Inno.Editor.Panel.Settings.md index 834b11b02..2c82e18ac 100644 --- a/docs/editor/Inno.Editor.Panel.Settings.md +++ b/docs/editor/Inno.Editor.Panel.Settings.md @@ -11,6 +11,8 @@ Settings └─ Build/... → Settings.Build.inno ``` +Settings 专用字段底色与内边距由内部 `Styling/SettingsStyle` 维护。它只读取当前帧的共享尺度,不把 Settings 策略或 Session 引用放回公共 metrics。 + ## 窗口行为 - 主菜单 `Edit/Settings...` 打开可移动、可缩放但不可 Dock/Collapse 的 Modal。 diff --git a/docs/editor/Inno.Editor.Panel.Stats.md b/docs/editor/Inno.Editor.Panel.Stats.md index 5114712b1..63f41d091 100644 --- a/docs/editor/Inno.Editor.Panel.Stats.md +++ b/docs/editor/Inno.Editor.Panel.Stats.md @@ -4,7 +4,7 @@ 该项目提供最小独立 Stats Panel,不拥有 Scene、Asset、Rendering 或 Interaction 业务。 -`FrameStatisticsSampler` 对瞬时 delta time/FPS 进行时间窗口平滑,Panel 展示稳定的人类可读值,而不是每帧直接显示抖动的倒数。采样仍保留当前 frame time,平均值仅影响诊断显示,不改变 `Time` 或引擎更新。 +`FrameStatisticsSampler` 对瞬时 delta time/FPS 进行时间窗口平滑,Panel 展示稳定的人类可读值,而不是每帧直接显示抖动的倒数。每 0.5 秒产生一次显示样本,最多保存最近 60 秒的 120 个 FPS/frame-time 样本;停止绘制、时间倒退或长期暂停后重置。趋势直接借用 Panel 的有界数值缓冲,不把普通统计字符串推断成数值。平均值仅影响诊断显示,不改变 `Time` 或引擎更新。 显示层保留 `—— 分组 ——` 的诊断结构,但每组正文使用带共享内边距的两列自适应表格:左侧是弱化 label,右侧是可换行 value。交替行直接复用 Hierarchy GameObject 的 `collectionRow/collectionRowAlternate`;分组标题通过 `ImGuiWidget.CollectionSectionHeader` 复用 Hierarchy Scene 的背景、Inspector 的线色与统一 border thickness。标题 padding 来自共享 `EditorStyleMetrics.sectionHeaderPadding`,Inspector fieldset legend 使用同一缩进,不再由两个 Panel 分别估算。`Time`、`Delta` 和 `FPS` 归入 `Frame Timing`,与 Rendering、Game View 等 feature 统计使用同一视觉层级;长 Pipeline ID、Contributor 名称和分辨率说明不会再横向撑出 Panel。 @@ -27,7 +27,7 @@ static void PublishPending( } ``` -同一帧重复发布相同 ID 时最后值替换之前值。交换保留一个 completed-frame handoff,使贡献者 +内容及排序不变时 `GetSnapshot()` 复用同一不可变快照;Stats 只在快照变化时重建分组。同一帧重复发布相同 ID 时最后值替换之前值。交换保留一个 completed-frame handoff,使贡献者 位于 Stats 前后任意 Panel draw order 都能被看到;停止发布后不会形成长期 stale 数据。 宿主通过 internal lifecycle bridge 调用 Panel 的 protected `OnAttach`、`OnDraw` 与 `OnDetach`;扩展和脚本不能手工驱动生命周期。当前 generation 的 Draw 失败只 quarantine Stats Panel,不会中断其他 Editor 内容绘制。 diff --git a/docs/platform/Inno.Adapter.Presentation.md b/docs/platform/Inno.Adapter.Presentation.md index 1862fa162..f419c3337 100644 --- a/docs/platform/Inno.Adapter.Presentation.md +++ b/docs/platform/Inno.Adapter.Presentation.md @@ -38,72 +38,27 @@ Presentation 是 Application 生命周期资源,必须先于 shared render dev ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Adapter.Presentation.IAuthoringAdapterCatalog` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Adapter.Presentation.IPresentationBackendFactory Inno.Adapter.Presentation.IAuthoringAdapterCatalog.presentation { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L18) | Gets the graphical host-presentation factory used by authoring products. | — | — | -| [`Inno.Adapter.Rendering.IRenderingAuthoringBackendFactory Inno.Adapter.Presentation.IAuthoringAdapterCatalog.renderingAuthoring { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L13) | Gets the rendering toolchain factory used to compile authoring artifacts. | — | — | -| [`public interface Inno.Adapter.Presentation.IAuthoringAdapterCatalog : Inno.Adapter.IAdapterCatalog`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L8) | Extends the runtime adapter catalog with tooling and presentation factories required by authoring products. | — | — | - -### `Inno.Adapter.Presentation.IPresentationBackendFactory` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.IPresentationBackendFactory.CreateContext(Inno.Adapter.Presentation.PresentationBackendId backend, Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L30) | Creates a presentation context over compatible platform and rendering adapters.
返回:A caller-owned backend-neutral presentation context.
`backend`:Stable presentation implementation selected by the composition root.
`options`:Host-owned resources and presentation policy. | — | `System.NotSupportedException`:Thrown when the selected platform, rendering, and presentation backends are incompatible. | -| [`System.Collections.Generic.IReadOnlyList Inno.Adapter.Presentation.IPresentationBackendFactory.supportedBackends { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L13) | Gets the implementation identities available in this immutable factory snapshot. | — | — | -| [`public interface Inno.Adapter.Presentation.IPresentationBackendFactory`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L8) | Creates host presentation contexts from explicit backend selections. | — | — | - -### `Inno.Adapter.Presentation.IPresentationContext` - - + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Adapter.Presentation.IPresentationContext.DrawImage(Inno.Adapter.Presentation.PresentationTextureHandle texture, System.Numerics.Vector2 size)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L101) | Draws a registered texture in the active presentation surface.
`texture`:Opaque presentation texture token.
`size`:Destination size in logical presentation units. | — | — | -| [`void Inno.Adapter.Presentation.IPresentationContext.LoadLayout(string? settings)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L43) | Loads complete layout text captured by the host.
`settings`:Layout text, or an empty value when no layout exists. | — | — | -| [`Inno.Adapter.Presentation.PresentationTextureHandle Inno.Adapter.Presentation.IPresentationContext.RegisterTexture(Inno.Rendering.PersistentTextureHandle texture)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L79) | Registers a persistent render texture for presentation drawing.
返回:An opaque presentation token.
`texture`:Texture owned by the active rendering-device generation. | — | — | -| [`void Inno.Adapter.Presentation.IPresentationContext.RenderFrame(System.Action drawFrame)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L68) | Builds one presentation frame by invoking the host draw callback.
`drawFrame`:Callback that emits presentation commands for the current frame. | — | — | -| [`void Inno.Adapter.Presentation.IPresentationContext.SetLayoutFile(string? filePath)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L35) | Disables or redirects backend-owned layout-file persistence.
`filePath`:Layout path, or null when the host persists layout text itself. | — | — | -| [`bool Inno.Adapter.Presentation.IPresentationContext.TryCaptureLayout(out string settings, bool force = false)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L57) | Captures complete layout text when persistence is required.
返回:true when layout text was captured.
`settings`:Complete layout text when capture succeeds.
`force`:Whether to capture even when the backend did not mark the layout dirty. | — | — | -| [`bool Inno.Adapter.Presentation.IPresentationContext.TryGetWindowId(uint viewportId, out uint windowId)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L24) | Resolves the platform window that owns a presentation viewport.
返回:true when the viewport has a live platform window.
`viewportId`:The presentation viewport identity.
`windowId`:The owning platform window identity when available. | — | — | -| [`bool Inno.Adapter.Presentation.IPresentationContext.UnregisterTexture(Inno.Adapter.Presentation.PresentationTextureHandle texture)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L90) | Releases a presentation token without taking ownership of the source texture.
返回:true when the token was registered and released.
`texture`:Previously allocated presentation token. | — | — | -| [`public interface Inno.Adapter.Presentation.IPresentationContext : Inno.Rendering.IRenderFrameGraphContributor, System.IDisposable`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L10) | Defines the backend-neutral lifecycle used by a graphical composition host. | — | — | - -### `Inno.Adapter.Presentation.PresentationBackendCatalog` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.PresentationBackendCatalog.PresentationBackendCatalog(System.Collections.Generic.IEnumerable providers)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L26) | Validates and captures a complete provider set without creating any service.
`providers`:Providers whose lifetime remains with the composition owner. | — | `System.ArgumentNullException`:The provider sequence is null.
`System.ArgumentException`:A provider is null, unassigned, or duplicates a registration identity. | -| [`public Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.PresentationBackendCatalog.CreateContext(Inno.Adapter.Presentation.PresentationBackendId backend, Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L44) | Creates a presentation context over compatible platform and rendering adapters.
返回:A caller-owned backend-neutral presentation context.
`backend`:Stable presentation implementation selected by the composition root.
`options`:Host-owned resources and presentation policy. | — | `System.NotSupportedException`:Thrown when the selected platform, rendering, and presentation backends are incompatible.
`System.InvalidOperationException`:The selected provider returned no service. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Adapter.Presentation.PresentationBackendCatalog.supportedBackends { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L38) | Gets the implementation identities available in this immutable factory snapshot. | — | — | -| [`public sealed class Inno.Adapter.Presentation.PresentationBackendCatalog : Inno.Adapter.Presentation.IPresentationBackendFactory`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L10) | Resolves presentation providers from one immutable, composition-owned registration snapshot. | — | — | - -### `Inno.Adapter.Presentation.PresentationBackendId` - @@ -115,71 +70,21 @@ Presentation 是 Application 生命周期资源,必须先于 shared render dev - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.PresentationBackendId.PresentationBackendId(string value)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L19) | Creates an ordinal, case-sensitive implementation identifier.
`value`:A stable nonempty identifier containing no whitespace. | — | `System.ArgumentException`:The identifier is empty or contains whitespace. | -| [`public bool Inno.Adapter.Presentation.PresentationBackendId.Equals(Inno.Adapter.Presentation.PresentationBackendId other)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Adapter.Presentation.PresentationBackendId.Equals(object obj)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Adapter.Presentation.PresentationBackendId.GetHashCode()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Adapter.Presentation.PresentationBackendId.operator ==(Inno.Adapter.Presentation.PresentationBackendId left, Inno.Adapter.Presentation.PresentationBackendId right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Adapter.Presentation.PresentationBackendId.operator !=(Inno.Adapter.Presentation.PresentationBackendId left, Inno.Adapter.Presentation.PresentationBackendId right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Adapter.Presentation.PresentationBackendId.ToString()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L49) | Returns the identifier without resolving a provider.
返回:The stable identifier, or an empty string when unassigned. | — | — | -| [`public static Inno.Adapter.Presentation.PresentationBackendId Inno.Adapter.Presentation.PresentationBackendId.imGui { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L31) | Gets the identifier of the bundled ImGui implementation. | — | — | -| [`public bool Inno.Adapter.Presentation.PresentationBackendId.isValid { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L41) | Gets whether this value identifies an implementation. | — | — | -| [`public string Inno.Adapter.Presentation.PresentationBackendId.value { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L36) | Gets the stable identifier; a default value is unassigned. | — | — | -| [`public readonly record struct Inno.Adapter.Presentation.PresentationBackendId : System.IEquatable`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Identifies a presentation implementation without closing the set of supported backends. | — | — | - -### `Inno.Adapter.Presentation.PresentationBackendOptions` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.PresentationBackendOptions.PresentationBackendOptions()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public Inno.Adapter.Presentation.PresentationFeatures Inno.Adapter.Presentation.PresentationBackendOptions.features { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L29) | Gets or sets optional presentation features requested by the host. | — | — | -| [`public required Inno.Platform.IPlatformApplication Inno.Adapter.Presentation.PresentationBackendOptions.platformApplication { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L14) | Gets or sets the platform application that owns presentation windows and events. | — | — | -| [`public required Inno.Rendering.IRenderDevice Inno.Adapter.Presentation.PresentationBackendOptions.renderDevice { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L24) | Gets or sets the rendering device used to present host draw data. | — | — | -| [`public required Inno.Platform.IPlatformWindow Inno.Adapter.Presentation.PresentationBackendOptions.window { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L19) | Gets or sets the primary presentation window. | — | — | -| [`public sealed class Inno.Adapter.Presentation.PresentationBackendOptions`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L9) | Collects the validated presentation backend options values that configure one owned operation. | — | — | - -### `Inno.Adapter.Presentation.PresentationBackendProvider` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Adapter.Presentation.PresentationBackendProvider.PresentationBackendProvider(Inno.Adapter.Presentation.PresentationBackendId id)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L19) | Captures the implementation identity assigned by the composition owner.
`id`:The assigned, immutable implementation identifier. | — | `System.ArgumentException`:The identifier is unassigned. | -| [`public abstract Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.PresentationBackendProvider.CreateContext(Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L44) | Creates a new caller-owned presentation context over the supplied host resources.
返回:A new context whose caller must dispose it before releasing the supplied resources. Providers must not return null.
`options`:Externally owned platform and rendering resources, together with the requested presentation policy. | — | `System.NotSupportedException`:The host resources or requested features are incompatible with this implementation. | -| [`public Inno.Adapter.Presentation.PresentationBackendId Inno.Adapter.Presentation.PresentationBackendProvider.id { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L29) | Gets this registration's immutable implementation identity. | — | — | -| [`public abstract class Inno.Adapter.Presentation.PresentationBackendProvider`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L8) | Describes an explicitly composed host presentation implementation and its creation boundary. | — | — | - -### `Inno.Adapter.Presentation.PresentationFeatures` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Adapter.Presentation.PresentationFeatures.Docking = 2`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L24) | Enables dockable host surfaces when supported. | — | — | -| [`Inno.Adapter.Presentation.PresentationFeatures.MultipleWindows = 1`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L19) | Allows the presentation to create detached native windows. | — | — | -| [`Inno.Adapter.Presentation.PresentationFeatures.None = 0`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L14) | Requests no optional presentation features. | — | — | -| [`Inno.Adapter.Presentation.PresentationFeatures.SmoothResize = 4`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L29) | Enables smooth rendering while native windows are being resized. | — | — | -| [`public enum Inno.Adapter.Presentation.PresentationFeatures : int`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L9) | Describes optional capabilities requested from a host presentation backend. | — | — | - -### `Inno.Adapter.Presentation.PresentationTextureHandle` - @@ -191,23 +96,70 @@ Presentation 是 Application 生命周期资源,必须先于 shared render dev -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Adapter.Presentation.PresentationTextureHandle.PresentationTextureHandle(ulong value)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L16) | Creates an opaque presentation texture token.
`value`:Non-zero token allocated by the active presentation backend. | — | — | -| [`public bool Inno.Adapter.Presentation.PresentationTextureHandle.Equals(Inno.Adapter.Presentation.PresentationTextureHandle other)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Adapter.Presentation.PresentationTextureHandle.Equals(object obj)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Adapter.Presentation.PresentationTextureHandle.GetHashCode()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Adapter.Presentation.PresentationTextureHandle.operator ==(Inno.Adapter.Presentation.PresentationTextureHandle left, Inno.Adapter.Presentation.PresentationTextureHandle right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Adapter.Presentation.PresentationTextureHandle.operator !=(Inno.Adapter.Presentation.PresentationTextureHandle left, Inno.Adapter.Presentation.PresentationTextureHandle right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Adapter.Presentation.PresentationTextureHandle.ToString()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public bool Inno.Adapter.Presentation.PresentationTextureHandle.isValid { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L31) | Gets whether this token identifies a registered presentation texture. | — | — | -| [`public ulong Inno.Adapter.Presentation.PresentationTextureHandle.value { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L26) | Gets the opaque backend token. | — | — | +| [`public interface Inno.Adapter.Presentation.IAuthoringAdapterCatalog : Inno.Adapter.IAdapterCatalog`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L8) | Extends the runtime adapter catalog with tooling and presentation factories required by authoring products. | — | — | +| [`Inno.Adapter.Rendering.IRenderingAuthoringBackendFactory Inno.Adapter.Presentation.IAuthoringAdapterCatalog.renderingAuthoring { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L13) | Gets the rendering toolchain factory used to compile authoring artifacts. | — | — | +| [`Inno.Adapter.Presentation.IPresentationBackendFactory Inno.Adapter.Presentation.IAuthoringAdapterCatalog.presentation { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IAuthoringAdapterCatalog.cs#L18) | Gets the graphical host-presentation factory used by authoring products. | — | — | +| [`public interface Inno.Adapter.Presentation.IPresentationBackendFactory`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L8) | Creates host presentation contexts from explicit backend selections. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Adapter.Presentation.IPresentationBackendFactory.supportedBackends { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L13) | Gets the implementation identities available in this immutable factory snapshot. | — | — | +| [`Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.IPresentationBackendFactory.CreateContext(Inno.Adapter.Presentation.PresentationBackendId backend, Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationBackendFactory.cs#L30) | Creates a presentation context over compatible platform and rendering adapters.
`backend`: Stable presentation implementation selected by the composition root.
`options`: Host-owned resources and presentation policy. | A caller-owned backend-neutral presentation context. | `System.NotSupportedException`: Thrown when the selected platform, rendering, and presentation backends are incompatible. | +| [`public interface Inno.Adapter.Presentation.IPresentationContext : Inno.Rendering.IRenderFrameGraphContributor, System.IDisposable`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L10) | Defines the backend-neutral lifecycle used by a graphical composition host. | — | — | +| [`bool Inno.Adapter.Presentation.IPresentationContext.TryGetWindowId(uint viewportId, out uint windowId)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L24) | Resolves the platform window that owns a presentation viewport.
`viewportId`: The presentation viewport identity.
`windowId`: The owning platform window identity when available. | when the viewport has a live platform window. | — | +| [`void Inno.Adapter.Presentation.IPresentationContext.SetLayoutFile(string? filePath)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L35) | Disables or redirects backend-owned layout-file persistence.
`filePath`: Layout path, or when the host persists layout text itself. | — | — | +| [`void Inno.Adapter.Presentation.IPresentationContext.LoadLayout(string? settings)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L43) | Loads complete layout text captured by the host.
`settings`: Layout text, or an empty value when no layout exists. | — | — | +| [`bool Inno.Adapter.Presentation.IPresentationContext.TryCaptureLayout(out string settings, bool force = false)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L57) | Captures complete layout text when persistence is required.
`settings`: Complete layout text when capture succeeds.
`force`: Whether to capture even when the backend did not mark the layout dirty. | when layout text was captured. | — | +| [`void Inno.Adapter.Presentation.IPresentationContext.RenderFrame(System.Action drawFrame, System.Action? prepareFrame = null)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L78) | Builds one presentation frame by invoking the host draw callback.
`drawFrame`: Callback that emits presentation commands for the current frame.
`prepareFrame`: Optional synchronous preparation after the owned context and display metrics are selected, before frame creation. Failure prevents drawing and no partial frame is published. | — | `System.ArgumentNullException`: The draw callback is null.
`System.InvalidOperationException`: Another frame operation is active on this context. | +| [`Inno.Adapter.Presentation.PresentationTextureHandle Inno.Adapter.Presentation.IPresentationContext.RegisterTexture(Inno.Rendering.PersistentTextureHandle texture)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L92) | Registers a persistent render texture for presentation drawing.
`texture`: Texture owned by the active rendering-device generation. | An opaque presentation token. | — | +| [`bool Inno.Adapter.Presentation.IPresentationContext.UnregisterTexture(Inno.Adapter.Presentation.PresentationTextureHandle texture)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L103) | Releases a presentation token without taking ownership of the source texture.
`texture`: Previously allocated presentation token. | when the token was registered and released. | — | +| [`void Inno.Adapter.Presentation.IPresentationContext.DrawImage(Inno.Adapter.Presentation.PresentationTextureHandle texture, System.Numerics.Vector2 size)`](../../src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs#L114) | Draws a registered texture in the active presentation surface.
`texture`: Opaque presentation texture token.
`size`: Destination size in logical presentation units. | — | — | +| [`public sealed class Inno.Adapter.Presentation.PresentationBackendCatalog : Inno.Adapter.Presentation.IPresentationBackendFactory`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L10) | Resolves presentation providers from one immutable, composition-owned registration snapshot. | — | — | +| [`public Inno.Adapter.Presentation.PresentationBackendCatalog.PresentationBackendCatalog(System.Collections.Generic.IEnumerable providers)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L26) | Validates and captures a complete provider set without creating any service.
`providers`: Providers whose lifetime remains with the composition owner. | — | `System.ArgumentNullException`: The provider sequence is null.
`System.ArgumentException`: A provider is null, unassigned, or duplicates a registration identity. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Adapter.Presentation.PresentationBackendCatalog.supportedBackends { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L38) | Gets the implementation identities available in this immutable factory snapshot. | — | — | +| [`public Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.PresentationBackendCatalog.CreateContext(Inno.Adapter.Presentation.PresentationBackendId backend, Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendCatalog.cs#L44) | Creates a presentation context over compatible platform and rendering adapters.
`backend`: Stable presentation implementation selected by the composition root.
`options`: Host-owned resources and presentation policy. | A caller-owned backend-neutral presentation context. | `System.NotSupportedException`: Thrown when the selected platform, rendering, and presentation backends are incompatible.
`System.InvalidOperationException`: The selected provider returned no service. | +| [`public readonly record struct Inno.Adapter.Presentation.PresentationBackendId : System.IEquatable`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Identifies a presentation implementation without closing the set of supported backends. | — | — | +| [`public Inno.Adapter.Presentation.PresentationBackendId.PresentationBackendId(string value)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L19) | Creates an ordinal, case-sensitive implementation identifier.
`value`: A stable nonempty identifier containing no whitespace. | — | `System.ArgumentException`: The identifier is empty or contains whitespace. | +| [`public static Inno.Adapter.Presentation.PresentationBackendId Inno.Adapter.Presentation.PresentationBackendId.imGui { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L31) | Gets the identifier of the bundled ImGui implementation. | — | — | +| [`public string Inno.Adapter.Presentation.PresentationBackendId.value { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L36) | Gets the stable identifier; a default value is unassigned. | — | — | +| [`public bool Inno.Adapter.Presentation.PresentationBackendId.isValid { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L41) | Gets whether this value identifies an implementation. | — | — | +| [`public override string Inno.Adapter.Presentation.PresentationBackendId.ToString()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L49) | Returns the identifier without resolving a provider. | The stable identifier, or an empty string when unassigned. | — | +| [`public static bool Inno.Adapter.Presentation.PresentationBackendId.operator !=(Inno.Adapter.Presentation.PresentationBackendId left, Inno.Adapter.Presentation.PresentationBackendId right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Adapter.Presentation.PresentationBackendId.operator ==(Inno.Adapter.Presentation.PresentationBackendId left, Inno.Adapter.Presentation.PresentationBackendId right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Adapter.Presentation.PresentationBackendId.GetHashCode()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Adapter.Presentation.PresentationBackendId.Equals(object obj)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Adapter.Presentation.PresentationBackendId.Equals(Inno.Adapter.Presentation.PresentationBackendId other)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendId.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed class Inno.Adapter.Presentation.PresentationBackendOptions`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L9) | Collects the validated presentation backend options values that configure one owned operation. | — | — | +| [`public required Inno.Platform.IPlatformApplication Inno.Adapter.Presentation.PresentationBackendOptions.platformApplication { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L14) | Gets or sets the platform application that owns presentation windows and events. | — | — | +| [`public required Inno.Platform.IPlatformWindow Inno.Adapter.Presentation.PresentationBackendOptions.window { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L19) | Gets or sets the primary presentation window. | — | — | +| [`public required Inno.Rendering.IRenderDevice Inno.Adapter.Presentation.PresentationBackendOptions.renderDevice { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L24) | Gets or sets the rendering device used to present host draw data. | — | — | +| [`public Inno.Adapter.Presentation.PresentationFeatures Inno.Adapter.Presentation.PresentationBackendOptions.features { get; set; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L29) | Gets or sets optional presentation features requested by the host. | — | — | +| [`public Inno.Adapter.Presentation.PresentationBackendOptions.PresentationBackendOptions()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendOptions.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Adapter.Presentation.PresentationBackendProvider`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L8) | Describes an explicitly composed host presentation implementation and its creation boundary. | — | — | +| [`protected Inno.Adapter.Presentation.PresentationBackendProvider.PresentationBackendProvider(Inno.Adapter.Presentation.PresentationBackendId id)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L19) | Captures the implementation identity assigned by the composition owner.
`id`: The assigned, immutable implementation identifier. | — | `System.ArgumentException`: The identifier is unassigned. | +| [`public Inno.Adapter.Presentation.PresentationBackendId Inno.Adapter.Presentation.PresentationBackendProvider.id { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L29) | Gets this registration's immutable implementation identity. | — | — | +| [`public abstract Inno.Adapter.Presentation.IPresentationContext Inno.Adapter.Presentation.PresentationBackendProvider.CreateContext(Inno.Adapter.Presentation.PresentationBackendOptions options)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationBackendProvider.cs#L44) | Creates a new caller-owned presentation context over the supplied host resources.
`options`: Externally owned platform and rendering resources, together with the requested presentation policy. | A new context whose caller must dispose it before releasing the supplied resources. Providers must not return null. | `System.NotSupportedException`: The host resources or requested features are incompatible with this implementation. | +| [`public enum Inno.Adapter.Presentation.PresentationFeatures : int`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L9) | Describes optional capabilities requested from a host presentation backend. | — | — | +| [`Inno.Adapter.Presentation.PresentationFeatures.None = 0`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L14) | Requests no optional presentation features. | — | — | +| [`Inno.Adapter.Presentation.PresentationFeatures.MultipleWindows = 1`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L19) | Allows the presentation to create detached native windows. | — | — | +| [`Inno.Adapter.Presentation.PresentationFeatures.Docking = 2`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L24) | Enables dockable host surfaces when supported. | — | — | +| [`Inno.Adapter.Presentation.PresentationFeatures.SmoothResize = 4`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationFeatures.cs#L29) | Enables smooth rendering while native windows are being resized. | — | — | | [`public readonly record struct Inno.Adapter.Presentation.PresentationTextureHandle : System.IEquatable`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Identifies a renderer-owned texture through an opaque host-presentation token. | — | — | +| [`public Inno.Adapter.Presentation.PresentationTextureHandle.PresentationTextureHandle(ulong value)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L16) | Creates an opaque presentation texture token.
`value`: Non-zero token allocated by the active presentation backend. | — | — | +| [`public ulong Inno.Adapter.Presentation.PresentationTextureHandle.value { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L26) | Gets the opaque backend token. | — | — | +| [`public bool Inno.Adapter.Presentation.PresentationTextureHandle.isValid { get; }`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L31) | Gets whether this token identifies a registered presentation texture. | — | — | +| [`public override string Inno.Adapter.Presentation.PresentationTextureHandle.ToString()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Adapter.Presentation.PresentationTextureHandle.operator !=(Inno.Adapter.Presentation.PresentationTextureHandle left, Inno.Adapter.Presentation.PresentationTextureHandle right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Adapter.Presentation.PresentationTextureHandle.operator ==(Inno.Adapter.Presentation.PresentationTextureHandle left, Inno.Adapter.Presentation.PresentationTextureHandle right)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Adapter.Presentation.PresentationTextureHandle.GetHashCode()`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Adapter.Presentation.PresentationTextureHandle.Equals(object obj)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Adapter.Presentation.PresentationTextureHandle.Equals(Inno.Adapter.Presentation.PresentationTextureHandle other)`](../../src/adapters/presentation/Inno.Adapter.Presentation/PresentationTextureHandle.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | ## 项目依赖 -- [Inno.Adapter](../runtime/Inno.Adapter.md):公开引用边界由实际签名核对。 -- [Inno.Adapter.Rendering.Authoring](../rendering/Inno.Adapter.Rendering.Authoring.md):公开引用边界由实际签名核对。 -- [Inno.Platform](Inno.Platform.md):公开引用边界由实际签名核对。 -- [Inno.Rendering](../rendering/Inno.Rendering.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Adapter` | 公开签名依赖 | +| `Inno.Adapter.Rendering.Authoring` | 公开签名依赖 | +| `Inno.Platform` | 公开签名依赖 | +| `Inno.Rendering` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/scene/Inno.Scene.md b/docs/scene/Inno.Scene.md index 937c5cd08..288a0a009 100644 --- a/docs/scene/Inno.Scene.md +++ b/docs/scene/Inno.Scene.md @@ -8,6 +8,8 @@ ## 多 Scene +`GameScene.hierarchyRevision` 是当前 live Scene 的只读变更计数。对象注册、移除、重设父级和 sibling 排序使其失效;它只用于刷新派生视图,不是持久 ID 或文件格式。内部层级 owner 同时维护有序 roots 和派生位置索引,Editor 不保存第二份 live 层级。名字、属性及 active 状态不由这个计数描述,呈现仍在提交行时解析当前对象。 + Scene reload 的旧 element 普通退休失败会聚合抛出,且仍尝试其余旧 element;统一 generation 的不可逆 Complete 阶段据此 Fault。RetirementPendingException 则保持 owner、立即阻止后续清理并交给共享 gate Fault。不能只发布 Warning 后继续报告成功,也不能撤销已经提交的新 generation 假装回滚。 以下脚本例在有效 Runtime Session 与 SceneWorld 执行作用域内调用;first/second 为同一 world 准备的不同 Scene,player 属于 first。LoadScene 会卸载当前集合,不用于保留尚未保存的编辑场景。 @@ -283,30 +285,36 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Scene.AllowMultipleComponentAttribute` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + + + + + + + + + + + + + + + - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.AllowMultipleComponentAttribute.AllowMultipleComponentAttribute()`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleComponentAttribute.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public sealed class Inno.Scene.AllowMultipleComponentAttribute : System.Attribute`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleComponentAttribute.cs#L9) | Allows multiple instances of a concrete component type on one game object. | — | — | - -### `Inno.Scene.AllowMultipleSystemAttribute` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.AllowMultipleSystemAttribute.AllowMultipleSystemAttribute()`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleSystemAttribute.cs#L9) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public sealed class Inno.Scene.AllowMultipleSystemAttribute : System.Attribute`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleSystemAttribute.cs#L9) | Allows multiple instances of a concrete Inno.Scene.GameSystem type in one scene. | — | — | - -### `Inno.Scene.Components.Transform` - + + + + + + @@ -326,45 +334,9 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.Components.Transform.Transform()`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L31) | Creates a transform with identity local values. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.InverseTransformPoint(Inno.Core.Mathematics.Vector3 point)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L201) | Transforms a world-space point into this transform's local space.
返回:The corresponding local-space point.
`point`:Point expressed in world space. | — | — | -| [`protected override void Inno.Scene.Components.Transform.Reset()`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L206) | Restores this instance to its initial reusable state. | — | — | -| [`public void Inno.Scene.Components.Transform.SetParent(Inno.Scene.Components.Transform? parent)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L153) | Sets the parent while preserving this transform's world-space values.
`parent`:New parent, or null to move to scene level. | — | `System.InvalidOperationException`:Thrown for hierarchy cycles, cross-scene parents, or a non-invertible parent hierarchy. | -| [`public void Inno.Scene.Components.Transform.SetSiblingIndex(int siblingIndex)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L161) | Moves this transform within its current sibling collection.
`siblingIndex`:Requested zero-based sibling index. | — | — | -| [`public void Inno.Scene.Components.Transform.SetWorldTransform(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, Inno.Core.Mathematics.Vector3 scale)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L175) | Atomically applies world-space translation, rotation, and scale.
`position`:Requested world-space translation.
`rotation`:Requested world-space rotation.
`scale`:Requested world-space scale. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.TransformPoint(Inno.Core.Mathematics.Vector3 point)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L190) | Transforms a local-space point through the complete parent hierarchy.
返回:The corresponding world-space point.
`point`:Point expressed in this transform's local space. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.Components.Transform.children { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L133) | Gets child transforms in sibling order. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.localPosition { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L40) | Gets or sets the local position relative to the parent. | — | — | -| [`public Inno.Core.Mathematics.Quaternion Inno.Scene.Components.Transform.localRotation { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L54) | Gets or sets the local rotation relative to the parent. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.localScale { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L68) | Gets or sets the local scale relative to the parent. | — | — | -| [`public Inno.Core.Mathematics.Matrix Inno.Scene.Components.Transform.localToWorldMatrix { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L108) | Gets the exact local-to-world matrix, including the complete parent hierarchy. | — | — | -| [`public Inno.Scene.Components.Transform? Inno.Scene.Components.Transform.parent { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L128) | Gets the parent transform, or null for a scene-level object. | — | — | -| [`public int Inno.Scene.Components.Transform.siblingIndex { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L138) | Gets or sets this transform's index among its siblings. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.worldPosition { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L81) | Gets or sets the world-space position. | — | — | -| [`public Inno.Core.Mathematics.Quaternion Inno.Scene.Components.Transform.worldRotation { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L90) | Gets or sets the world-space rotation. | — | — | -| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.worldScale { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L99) | Gets or sets the world-space scale. | — | — | -| [`public Inno.Core.Mathematics.Matrix Inno.Scene.Components.Transform.worldToLocalMatrix { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L116) | Gets the inverse of the exact local-to-world matrix. | — | `System.InvalidOperationException`:Thrown when a zero-scale hierarchy is not invertible. | -| [`public sealed class Inno.Scene.Components.Transform : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L15) | Stores local transform data and exposes the scene hierarchy relationship. | — | — | - -### `Inno.Scene.EngineAssetContent` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.EngineAssetContent.EngineAssetContent(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L28) | Creates a detached engine asset content snapshot.
`payload`:The complete serialized runtime graph.
`dependencies`:The direct persistent asset dependencies of the graph. | — | `System.ArgumentException`:Thrown when payload is empty. | -| [`public Inno.Assets.AssetDependency[] Inno.Scene.EngineAssetContent.GetDependencies()`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L53) | Gets a detached copy of direct persistent asset dependencies.
返回:A dependency array owned by the caller. | — | — | -| [`public byte[] Inno.Scene.EngineAssetContent.GetPayload()`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L45) | Gets a detached copy of the serialized runtime graph.
返回:A payload copy owned by the caller. | — | — | -| [`public sealed class Inno.Scene.EngineAssetContent`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L11) | Carries immutable scene or prefab runtime content across the runtime-to-authoring asset boundary. | — | — | - -### `Inno.Scene.EngineObject` - + + + @@ -372,19 +344,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Scene.EngineObject.EngineObject()`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L11) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public override bool Inno.Scene.EngineObject.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L71) | Compares this instance to another object using reference identity.
返回:true when obj is this instance.
`obj`:The object to compare with this instance. | — | — | -| [`public override int Inno.Scene.EngineObject.GetHashCode()`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L79) | Returns a stable runtime reference hash code.
返回:The reference hash code. | — | — | -| [`public static bool Inno.Scene.EngineObject.operator ==(Inno.Scene.EngineObject? left, Inno.Scene.EngineObject? right)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L32) | Compares engine objects using reference identity and destroyed-object null semantics.
返回:true when both operands refer to the same live object or both behave as null.
`left`:Left operand.
`right`:Right operand. | — | — | -| [`public static bool Inno.Scene.EngineObject.operator !=(Inno.Scene.EngineObject? left, Inno.Scene.EngineObject? right)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L57) | Compares engine objects using reference identity and destroyed-object null semantics.
返回:true when the operands do not represent the same object.
`left`:Left operand.
`right`:Right operand. | — | — | -| [`public bool Inno.Scene.EngineObject.isDestroyed { get; }`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L18) | Gets whether the engine has destroyed this object. | — | — | -| [`public abstract class Inno.Scene.EngineObject : Inno.Core.Identity.IdentityObject`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L11) | Provides identity and destruction state shared by all managed scene objects. | — | — | - -### `Inno.Scene.GameBehavior` - @@ -397,40 +356,11 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Scene.GameBehavior.GameBehavior()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L10) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.Awake()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L44) | Called once before this behavior first becomes active. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.FixedUpdate()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L79) | Called during the fixed-rate update stage. Use Time.fixedDeltaTime for step timing. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.LateUpdate()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L86) | Called during the late update stage. Use Time.deltaTime for frame timing. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.OnDestroy()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L93) | Called immediately before this behavior is detached and destroyed. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.OnDisable()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L65) | Called when this component stops being active and enabled. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.OnEnable()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L58) | Called when this component becomes active and enabled. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.Start()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L51) | Called once immediately before the first update of this behavior. | — | — | -| [`protected virtual void Inno.Scene.GameBehavior.Update()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L72) | Called during the variable-rate update stage. Use Time.deltaTime for frame timing. | — | — | -| [`public bool Inno.Scene.GameBehavior.enabled { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L22) | Gets or sets whether this component participates in scene lifecycle and frame updates. | Changing this value while the component belongs to a loaded scene immediately reconciles Inno.Scene.GameBehavior.OnEnable and Inno.Scene.GameBehavior.OnDisable against hierarchy activation. | — | -| [`public bool Inno.Scene.GameBehavior.isActiveAndEnabled { get; }`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L39) | Gets whether this component is enabled and active in its owning hierarchy. | — | — | -| [`public abstract class Inno.Scene.GameBehavior : Inno.Scene.GameComponent, Inno.Scene.ISceneLifecycleObject`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L10) | Base component for independently enabled scene functionality with frame lifecycle callbacks. | — | — | - -### `Inno.Scene.GameComponent` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Scene.GameComponent.GameComponent()`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L11) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected virtual void Inno.Scene.GameComponent.Reset()`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L37) | Restores this component to its default state when it is added or explicitly reset. | — | — | -| [`public Inno.Scene.GameObject Inno.Scene.GameComponent.gameObject { get; }`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L21) | Gets the owning game object. | — | `System.InvalidOperationException`:Thrown when the component is detached or destroyed. | -| [`public Inno.Scene.Components.Transform Inno.Scene.GameComponent.transform { get; }`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L32) | Gets the owning game object's transform. | — | `System.InvalidOperationException`:Thrown when the component is detached or destroyed. | -| [`public abstract class Inno.Scene.GameComponent : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L11) | Base type for data and behavior objects attached to a Inno.Scene.GameObject. | — | — | - -### `Inno.Scene.GameObject` - @@ -457,42 +387,11 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const string Inno.Scene.GameObject.defaultTag = "Untagged"`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L20) | Defines the tag assigned to newly created game objects. | — | — | -| [`public Inno.Scene.GameComponent Inno.Scene.GameObject.AddComponent(System.Type componentType)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L201) | Creates and attaches a component of the requested runtime type.
返回:The attached component.
`componentType`:Concrete component type. | — | — | -| [`public TComponent Inno.Scene.GameObject.AddComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L190) | Creates and attaches a component of the requested type.
返回:The attached component. | — | — | -| [`public Inno.Scene.GameComponent Inno.Scene.GameObject.GetComponent(System.Type componentType)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L235) | Gets the first attached component assignable to a runtime type.
返回:The first matching component.
`componentType`:Requested component type. | — | `System.InvalidOperationException`:Thrown when no matching component exists. | -| [`public TComponent Inno.Scene.GameObject.GetComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L216) | Gets the first attached component assignable to the requested type.
返回:The first matching component. | — | `System.InvalidOperationException`:Thrown when no matching component exists. | -| [`public int Inno.Scene.GameObject.GetComponentIndex(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L306) | Gets the attachment index of a component on this object.
返回:The zero-based attachment index.
`component`:Attached component to locate. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameObject.GetComponents()`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L295) | Gets all attached components in attachment order.
返回:A stable component snapshot. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameObject.GetComponents() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L286) | Gets all attached components assignable to the requested type in attachment order.
返回:A stable component snapshot. | — | — | -| [`public bool Inno.Scene.GameObject.HasComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L275) | Gets whether a matching component is attached.
返回:true when a matching component exists. | — | — | -| [`public bool Inno.Scene.GameObject.RemoveComponent(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L360) | Removes and destroys a specific attached component.
返回:true when the component was attached and removed.
`component`:GameComponent instance to remove. | — | `System.InvalidOperationException`:Thrown when attempting to remove the mandatory transform. | -| [`public void Inno.Scene.GameObject.ResetComponent(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L342) | Restores an attached component to the defaults defined by its optional Reset message.
`component`:GameComponent instance to reset. | — | `System.ArgumentNullException`:Thrown when component is null.
`System.InvalidOperationException`:Thrown when the object is invalid or the component is not attached to it. | -| [`public void Inno.Scene.GameObject.SetActive(bool value)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L179) | Changes this object's explicit active state and updates its hierarchy subtree.
`value`:The concrete value read or transformed by this operation. | — | — | -| [`public void Inno.Scene.GameObject.SetComponentIndex(Inno.Scene.GameComponent component, int componentIndex)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L322) | Moves an attached component to a requested attachment index. The mandatory Transform participates in ordering but remains non-removable.
`component`:Attached component to move.
`componentIndex`:Requested zero-based attachment index. | — | — | -| [`public bool Inno.Scene.GameObject.TryGetComponent(out TComponent? component) where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L255) | Tries to get the first attached component assignable to the requested type.
返回:true when a matching component exists.
`component`:Matching component when found. | — | — | -| [`public bool Inno.Scene.GameObject.activeInHierarchy { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L156) | Gets whether this object is active after parent hierarchy state is applied. | — | — | -| [`public bool Inno.Scene.GameObject.activeSelf { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L144) | Gets whether this object is explicitly active. | — | — | -| [`public bool Inno.Scene.GameObject.isPartOfPrefabInstance { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L161) | Gets whether this object retains a prefab source connection. | — | — | -| [`public bool Inno.Scene.GameObject.isRuntimeValid { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L43) | Gets whether this object is live in its owning scene. | — | — | -| [`public Inno.Scene.Layers.GameLayer Inno.Scene.GameObject.layer { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L109) | Gets or sets the single runtime layer used to filter this game object. | GameLayer names are project configuration stored separately from scene data. Scene and prefab serialization persist only the stable numeric layer slot. | — | -| [`public string Inno.Scene.GameObject.name { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L56) | Gets or sets the display name stored in the owning scene. | — | — | -| [`public Inno.Scene.PrefabInstanceInfo? Inno.Scene.GameObject.prefabInstance { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L171) | Gets read-only information about this object's prefab source connection. | — | — | -| [`public Inno.Scene.GameObject? Inno.Scene.GameObject.prefabInstanceRoot { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L166) | Gets the root of this object's prefab instance connection. | — | — | -| [`public Inno.Scene.GameScene Inno.Scene.GameObject.scene { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L51) | Gets the owning scene. | — | `System.InvalidOperationException`:Thrown when this object is destroyed or detached. | -| [`public string Inno.Scene.GameObject.tag { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L84) | Gets or sets the ordinal tag used to categorize and query this game object. | Tag definitions are project configuration stored separately from scene data. Scene and prefab serialization persist the assigned ordinal value even when its current project definition is absent. | `System.ArgumentException`:Thrown when the assigned tag is empty or contains only white-space characters. | -| [`public Inno.Scene.Components.Transform Inno.Scene.GameObject.transform { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L132) | Gets the mandatory transform component. | — | `System.InvalidOperationException`:Thrown when this object is destroyed or incompletely initialized. | -| [`public sealed class Inno.Scene.GameObject : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L15) | Represents a scene-owned object and exposes its component-oriented API. | — | — | - -### `Inno.Scene.GameScene` - + @@ -511,34 +410,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.GameScene.GameScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L36) | Creates an empty scene.
`name`:Initial scene name. | — | — | -| [`public void Inno.Scene.GameScene.AddSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L321) | Registers a game system instance.
`system`:System to register. | — | — | -| [`public Inno.Scene.GameSystem Inno.Scene.GameScene.AddSystem(System.Type systemType)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L303) | Creates and registers a concrete game system by runtime type.
返回:The validated game system that represents the completed operation.
`systemType`:The system type consumed by add system; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public TSystem Inno.Scene.GameScene.AddSystem() where TSystem : Inno.Scene.GameSystem, new()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L288) | Creates and registers a parameterless game system.
返回:The registered system. | — | — | -| [`public Inno.Scene.GameObject Inno.Scene.GameScene.CreateObject(string name = "GameObject")`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L84) | Creates a game object with a mandatory transform component.
返回:The created game object.
`name`:Initial game object name. | — | — | -| [`public bool Inno.Scene.GameScene.DestroyObject(Inno.Scene.GameObject gameObject)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L121) | Recursively destroys a game object and its complete child subtree.
返回:true when the object belonged to this scene and was destroyed.
`gameObject`:Root object to destroy. | — | — | -| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObject(string name)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L191) | Finds the first game object with an ordinally matching name.
返回:The first match, or null when no object matches.
`name`:Name to find. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithLayer(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L258) | Finds every game object assigned to one layer in scene storage order.
返回:A stable snapshot containing every matching game object.
`layer`:The layer slot to find. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithLayers(Inno.Scene.Layers.GameLayerMask layers)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L273) | Finds every game object assigned to any layer contained in a mask.
返回:A stable snapshot in scene storage order.
`layers`:The set of accepted layer slots. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithTag(string tag)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L228) | Finds every game object with an ordinally matching tag in scene storage order.
返回:A stable snapshot containing every matching game object.
`tag`:Tag to find. | — | `System.ArgumentException`:Thrown when tag is empty or contains only white-space characters. | -| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObjectWithLayer(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L243) | Finds the first game object assigned to a layer in scene storage order.
返回:The first matching game object, or null when no object matches.
`layer`:The layer slot to find. | — | — | -| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObjectWithTag(string tag)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L210) | Finds the first game object with an ordinally matching tag in scene storage order.
返回:The first match, or null when no object matches.
`tag`:Tag to find. | — | `System.ArgumentException`:Thrown when tag is empty or contains only white-space characters. | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.GetObjects()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L176) | Gets all committed live game objects in scene storage order.
返回:A stable object snapshot. | — | — | -| [`public int Inno.Scene.GameScene.GetSystemIndex(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L378) | Gets the display index of a registered system.
返回:The zero-based display index.
`system`:Registered system to locate. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.GetSystems()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L363) | Gets registered systems in display and serialization order. Explicit Inno.Scene.GameSystem.order values independently control lifecycle execution priority.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`public Inno.Scene.GameObject Inno.Scene.GameScene.InstantiatePrefab(Inno.Scene.PrefabAsset prefab, Inno.Scene.Components.Transform? parent = null)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L105) | Creates a connected instance of an imported prefab in this loaded scene.
返回:The newly instantiated prefab root.
`prefab`:The prefab asset that supplies the object subtree.
`parent`:Optional parent for the new instance. | — | `System.ArgumentNullException`:The prefab is null.
`System.InvalidOperationException`:The scene is not loaded in its owning world or prefab services are unavailable. | -| [`public bool Inno.Scene.GameScene.RemoveSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L336) | Removes a registered game system.
返回:true when the system was registered.
`system`:System to remove. | — | — | -| [`public void Inno.Scene.GameScene.ResetSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L349) | Explicitly restores a registered system to its default state.
`system`:The system consumed by reset system; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public void Inno.Scene.GameScene.SetSystemIndex(Inno.Scene.GameSystem system, int systemIndex)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L395) | Moves a registered system to a requested display and serialization index. This operation does not change its explicit Inno.Scene.GameSystem.order.
`system`:Registered system to move.
`systemIndex`:Requested zero-based display index. | — | — | -| [`public bool Inno.Scene.GameScene.isLoaded { get; }`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L70) | Gets whether this scene is currently owned by Inno.Scene.SceneManager. | — | — | -| [`public string Inno.Scene.GameScene.name { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L57) | Gets or sets the scene display name. | — | — | -| [`public sealed class Inno.Scene.GameScene : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L17) | Owns a runtime scene, including its objects, components, hierarchy, and systems. | — | — | - -### `Inno.Scene.GameSystem` - @@ -559,32 +430,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`protected Inno.Scene.GameSystem.GameSystem()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L13) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`protected virtual void Inno.Scene.GameSystem.Awake()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L63) | Called once before the system first becomes active. | — | — | -| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.GetComponents() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L125) | Gets all scene components assignable to a requested type.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.GetObjects()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L133) | Gets all committed live game objects in deterministic scene storage order.
返回:The immutable structure snapshot retained by the scene until its object or component structure changes. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnDestroy()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L91) | Called before a system that entered runtime lifecycle is destroyed. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnDisable()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L84) | Called when the system stops being active and enabled. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnEnable()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L77) | Called when the system becomes active and enabled. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnFixedUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L98) | Called during the fixed-rate scene stage. Use Time.fixedDeltaTime for step timing. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnLateUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L112) | Called during the late scene stage. Use Time.deltaTime for frame timing. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.OnUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L105) | Called during the variable-rate scene stage. Use Time.deltaTime for frame timing. | — | — | -| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L144) | Queries game objects containing one required component type.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent where T2 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L158) | Queries game objects containing two required component types.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent where T2 : Inno.Scene.GameComponent where T3 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L178) | Queries game objects containing three required component types.
返回:An immutable snapshot of the values selected by the operation. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.Reset()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L56) | Restores this system to defaults when added or explicitly reset. | — | — | -| [`protected virtual void Inno.Scene.GameSystem.Start()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L70) | Called once immediately before the first update. | — | — | -| [`public bool Inno.Scene.GameSystem.enabled { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L22) | Gets or sets whether this system participates in scene lifecycle updates. | — | — | -| [`public bool Inno.Scene.GameSystem.isActiveAndEnabled { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L39) | Gets whether this system is registered, enabled, and dispatchable. | — | — | -| [`public virtual int Inno.Scene.GameSystem.order { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L44) | Gets the ascending execution order used by the owning scene. | — | — | -| [`protected Inno.Scene.GameScene Inno.Scene.GameSystem.scene { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L49) | Gets the owning scene after this system has been registered. | — | — | -| [`public abstract class Inno.Scene.GameSystem : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable, Inno.Scene.ISceneLifecycleObject`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L13) | Base type for serializable, ordered scene-level behaviors. | — | — | - -### `Inno.Scene.GameTagCatalog` - @@ -596,47 +441,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const string Inno.Scene.GameTagCatalog.settingProtocolId = "inno.scene.tags"`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L21) | Gets the immutable project-setting protocol value used by discovery metadata. | — | — | -| [`public Inno.Scene.GameTagCatalog.GameTagCatalog()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L16) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public bool Inno.Scene.GameTagCatalog.Add(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L108) | Adds one normalized project tag definition.
返回:true when a definition was added.
`tag`:The unique tag to define. | — | `System.ArgumentException`:Thrown when tag is empty or invalid. | -| [`public Inno.Scene.GameTagCatalog Inno.Scene.GameTagCatalog.Clone()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L155) | Creates a detached mutable copy of this catalog.
返回:An independently owned catalog with the same definitions. | — | — | -| [`public Inno.Core.Settings.ProjectScopedId Inno.Scene.GameTagCatalog.GetId(Inno.Core.Settings.ProjectId projectId, string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L72) | Gets the complete project-scoped identity of a defined tag.
返回:The canonical projectId.name identity.
`projectId`:The current project namespace.
`tag`:The configured tag name. | — | — | -| [`public Inno.Core.Settings.ProjectLocalId Inno.Scene.GameTagCatalog.GetLocalId(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L52) | Gets the stable project-independent identity of a defined tag.
返回:The deterministic local identity.
`tag`:The configured tag name. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameTagCatalog.GetTags()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L37) | Gets the defined tags with the immutable default tag first.
返回:An independently owned deterministic tag snapshot. | — | — | -| [`public bool Inno.Scene.GameTagCatalog.IsDefined(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L89) | Determines whether an ordinal tag is defined by the project.
返回:true when the normalized tag is defined.
`tag`:The tag to resolve. | — | `System.ArgumentException`:Thrown when tag is empty or invalid. | -| [`public bool Inno.Scene.GameTagCatalog.Remove(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L134) | Removes one custom project tag definition without rewriting scene assignments.
返回:true when an existing definition was removed.
`tag`:The custom tag to remove. | — | `System.ArgumentException`:Thrown when tag is empty or invalid. | -| [`public static Inno.Core.Settings.ProjectSettingId Inno.Scene.GameTagCatalog.settingId { get; }`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L29) | Gets the stable project setting protocol for the project-wide tag catalog. | — | — | -| [`public sealed class Inno.Scene.GameTagCatalog : Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L16) | Stores the project-wide tag definitions used to author and validate scene object assignments. | — | — | - -### `Inno.Scene.ISceneReloadStateTransfer` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`void Inno.Scene.ISceneReloadStateTransfer.Apply()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L34) | Creates replacement instances and restores their serialized state. | — | — | -| [`void Inno.Scene.ISceneReloadStateTransfer.Complete()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L49) | Finalizes replacement instances after the assembly reload is committed. | — | — | -| [`void Inno.Scene.ISceneReloadStateTransfer.PrepareForActivation()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L29) | Disables active retiring lifecycle objects before the new assembly generation becomes active. | — | — | -| [`void Inno.Scene.ISceneReloadStateTransfer.RestorePreviousState()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L44) | Restores lifecycle state on the previous scene instances after rollback. | — | — | -| [`void Inno.Scene.ISceneReloadStateTransfer.RollbackStructure()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L39) | Restores the previous scene structure after a failed state transfer. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.diagnostics { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L19) | Gets non-fatal state transfer decisions produced while replacing reloadable scene objects. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.recoveryChanges { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L24) | Gets the shared candidate resolutions for actual scene element and asset dependency slots after application. | — | — | -| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.retiredObjects { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L14) | Gets old scene objects whose runtime types belong to the retiring assembly generation. | — | — | -| [`public interface Inno.Scene.ISceneReloadStateTransfer`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L9) | Represents a staged scene object state transfer associated with one assembly reload transaction. | — | — | - -### `Inno.Scene.Layers.GameLayer` - @@ -649,24 +453,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const int Inno.Scene.Layers.GameLayer.C_MAX_COUNT = 32`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L13) | Defines the number of layer slots supported by the runtime bit-mask representation. | — | — | -| [`public Inno.Scene.Layers.GameLayer.GameLayer(int index)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L29) | Creates a layer identifier from a zero-based layer index.
`index`:The layer index in the inclusive range from zero through thirty-one. | — | `System.ArgumentOutOfRangeException`:Thrown when index is outside the supported range. | -| [`public int Inno.Scene.Layers.GameLayer.CompareTo(Inno.Scene.Layers.GameLayer other)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L50) | Compares this layer with another layer by index.
返回:A signed value describing the relative index ordering.
`other`:The value to compare with this instance. | — | — | -| [`public bool Inno.Scene.Layers.GameLayer.Equals(Inno.Scene.Layers.GameLayer other)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L61) | Determines whether another layer identifies the same index.
返回:true when both layers identify the same index.
`other`:The layer to compare with this value. | — | — | -| [`public override bool Inno.Scene.Layers.GameLayer.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L72) | Determines whether this instance and the supplied value represent the same logical state.
返回:true when both values represent the same logical state; otherwise, false.
`obj`:The object to compare with this instance. | — | — | -| [`public override int Inno.Scene.Layers.GameLayer.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L80) | Computes a hash code from the fields that participate in logical equality.
返回:A hash code consistent with the implemented equality contract. | — | — | -| [`public static bool Inno.Scene.Layers.GameLayer.operator ==(Inno.Scene.Layers.GameLayer left, Inno.Scene.Layers.GameLayer right)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L102) | Determines whether two layer identifiers contain the same index.
返回:true when the identifiers are equal.
`left`:The first layer identifier.
`right`:The second layer identifier. | — | — | -| [`public static bool Inno.Scene.Layers.GameLayer.operator !=(Inno.Scene.Layers.GameLayer left, Inno.Scene.Layers.GameLayer right)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L119) | Determines whether two layer identifiers contain different indices.
返回:true when the identifiers are different.
`left`:The first layer identifier.
`right`:The second layer identifier. | — | — | -| [`public override string Inno.Scene.Layers.GameLayer.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L88) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | — | — | -| [`public static Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayer.defaultLayer { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L18) | Gets the built-in default layer stored in slot zero. | — | — | -| [`public int Inno.Scene.Layers.GameLayer.index { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L39) | Gets the zero-based layer index. | — | — | -| [`public readonly struct Inno.Scene.Layers.GameLayer : System.IEquatable, System.IComparable`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L8) | Identifies one of the thirty-two runtime layers available to scene objects. | — | — | - -### `Inno.Scene.Layers.GameLayerCatalog` - @@ -688,51 +474,12 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const string Inno.Scene.Layers.GameLayerCatalog.settingProtocolId = "inno.scene.layers"`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L23) | Gets the immutable project-setting protocol value used by discovery metadata. | — | — | -| [`public Inno.Scene.Layers.GameLayerCatalog.GameLayerCatalog()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L44) | Creates a layer stack containing the immutable default layer. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.CanInteract(Inno.Scene.Layers.GameLayer first, Inno.Scene.Layers.GameLayer second)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L386) | Determines whether two layer slots may interact.
返回:true when interaction is enabled.
`first`:The first layer.
`second`:The second layer. | — | — | -| [`public Inno.Scene.Layers.GameLayerCatalog Inno.Scene.Layers.GameLayerCatalog.Clone()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L432) | Creates a detached copy of this stack.
返回:An independent layer stack. | — | — | -| [`public void Inno.Scene.Layers.GameLayerCatalog.Define(Inno.Scene.Layers.GameLayer layer, string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L293) | Defines or renames a project layer without accepting an authored ID.
`layer`:The compact runtime slot.
`name`:The unique display name. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.Layers.GameLayerCatalog.GetDefinitions()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L71) | Gets an immutable snapshot of every named layer ordered by slot index.
返回:The ordered layer-definition snapshot. | — | — | -| [`public Inno.Scene.Layers.GameLayerId? Inno.Scene.Layers.GameLayerCatalog.GetId(Inno.Core.Settings.ProjectId projectId, Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L146) | Gets the complete identity assigned to a layer slot.
返回:The qualified identity, or null.
`projectId`:The current project namespace.
`layer`:The layer slot to resolve. | — | — | -| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerCatalog.GetInteractionMask(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L368) | Gets the interaction mask assigned to one layer.
返回:The configured interaction mask.
`layer`:The source layer. | — | — | -| [`public Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayerCatalog.GetLayer(string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L259) | Resolves an ordinal layer name to its compact slot.
返回:The matching runtime layer.
`name`:The configured layer name. | — | — | -| [`public Inno.Core.Settings.ProjectLocalId? Inno.Scene.Layers.GameLayerCatalog.GetLocalId(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L127) | Gets the stable project-independent identity assigned to a layer slot.
返回:The local identity, or null.
`layer`:The layer slot to resolve. | — | — | -| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerCatalog.GetMask(System.Collections.Generic.IEnumerable names)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L275) | Creates a mask from configured ordinal layer names.
返回:A mask containing every resolved layer.
`names`:The configured names to include. | — | — | -| [`public string? Inno.Scene.Layers.GameLayerCatalog.GetName(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L112) | Gets the display name assigned to a layer slot.
返回:The configured name, or null.
`layer`:The layer slot to resolve. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.IsDefined(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L97) | Determines whether a layer slot is defined.
返回:true when the slot is defined.
`layer`:The layer slot to test. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.Remove(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L347) | Removes a custom layer definition while retaining its numeric slot and interactions.
返回:true when a definition was removed.
`layer`:The custom layer slot to undefine. | — | — | -| [`public void Inno.Scene.Layers.GameLayerCatalog.SetInteraction(Inno.Scene.Layers.GameLayer first, Inno.Scene.Layers.GameLayer second, bool canInteract)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L406) | Sets a symmetric interaction pair.
`first`:The first layer.
`second`:The second layer.
`canInteract`:Whether interaction is enabled. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(Inno.Core.Settings.ProjectId projectId, Inno.Scene.Layers.GameLayerId id, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L199) | Tries to resolve a qualified identity to its compact runtime slot.
返回:true when the identity belongs to this project and is defined.
`projectId`:The current project namespace.
`id`:The complete layer identity.
`layer`:The resolved runtime slot. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(Inno.Core.Settings.ProjectLocalId localId, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L166) | Tries to resolve a local identity to its compact runtime slot.
返回:true when the identity is defined.
`localId`:The stable project-local identity.
`layer`:The resolved runtime slot. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(string name, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L233) | Tries to resolve an ordinal layer name to its compact slot.
返回:true when a matching layer exists.
`name`:The layer name to find.
`layer`:The resolved layer. | — | — | -| [`public int Inno.Scene.Layers.GameLayerCatalog.count { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L56) | Gets the number of currently named layer slots. | — | — | -| [`public static Inno.Core.Settings.ProjectSettingId Inno.Scene.Layers.GameLayerCatalog.settingId { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L39) | Gets the stable project setting protocol for the project-wide layer catalog. | — | — | -| [`public sealed class Inno.Scene.Layers.GameLayerCatalog : Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L18) | Stores the named layer catalog and symmetric layer-interaction matrix used by a project. | — | — | - -### `Inno.Scene.Layers.GameLayerDefinition` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.Layers.GameLayerDefinition.GameLayerDefinition(Inno.Core.Settings.ProjectLocalId localId, Inno.Scene.Layers.GameLayer layer, string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L24) | Creates an immutable layer definition.
`localId`:The stable project-independent identity.
`layer`:The layer slot represented by the definition.
`name`:The non-empty display and lookup name. | — | — | -| [`public Inno.Scene.Layers.GameLayerId Inno.Scene.Layers.GameLayerDefinition.GetId(Inno.Core.Settings.ProjectId projectId)`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L60) | Resolves the complete identity under a project namespace.
返回:The canonical project-scoped layer identity.
`projectId`:The current project namespace. | — | — | -| [`public Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayerDefinition.layer { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L44) | Gets the layer slot represented by this definition. | — | — | -| [`public Inno.Core.Settings.ProjectLocalId Inno.Scene.Layers.GameLayerDefinition.localId { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L39) | Gets the stable project-independent identity. | — | — | -| [`public string Inno.Scene.Layers.GameLayerDefinition.name { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L49) | Gets the unique ordinal layer name. | — | — | -| [`public sealed class Inno.Scene.Layers.GameLayerDefinition`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L10) | Describes one named layer slot in a Inno.Scene.Layers.GameLayerCatalog snapshot. | — | — | - -### `Inno.Scene.Layers.GameLayerId` - @@ -744,23 +491,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.Layers.GameLayerId.GameLayerId(Inno.Core.Settings.ProjectId projectId, Inno.Core.Settings.ProjectLocalId name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L34) | Creates a project-scoped layer identity from its project and local parts.
`projectId`:The current project identity.
`name`:The stable project-local layer identity. | — | — | -| [`public Inno.Scene.Layers.GameLayerId.GameLayerId(Inno.Core.Settings.ProjectScopedId value)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L18) | Creates a project-scoped layer identity.
`value`:The canonical qualified identity. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerId.Equals(Inno.Scene.Layers.GameLayerId other)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Scene.Layers.GameLayerId.Equals(object obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Scene.Layers.GameLayerId.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Scene.Layers.GameLayerId.operator ==(Inno.Scene.Layers.GameLayerId left, Inno.Scene.Layers.GameLayerId right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Scene.Layers.GameLayerId.operator !=(Inno.Scene.Layers.GameLayerId left, Inno.Scene.Layers.GameLayerId right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Scene.Layers.GameLayerId.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L76) | Formats this value as a human-readable representation.
返回:The qualified identity. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerId.isValid { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L68) | Gets whether this value contains a usable identity. | — | — | -| [`public string Inno.Scene.Layers.GameLayerId.value { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L63) | Gets the canonical projectId.name identity string. | — | — | -| [`public readonly record struct Inno.Scene.Layers.GameLayerId : System.IEquatable`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Identifies one project-scoped game layer independently from its compact runtime slot. | — | — | - -### `Inno.Scene.Layers.GameLayerMask` - @@ -779,72 +509,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.Layers.GameLayerMask.GameLayerMask(uint value)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L27) | Creates a mask from its raw thirty-two-bit representation.
`value`:The raw mask bits. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerMask.Contains(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L67) | Determines whether the supplied layer is contained in this mask.
返回:true when the layer bit is enabled.
`layer`:The layer consumed by contains; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public bool Inno.Scene.Layers.GameLayerMask.Equals(Inno.Scene.Layers.GameLayerMask other)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L100) | Determines whether another mask contains the same layer bits.
返回:true when both masks contain the same bits.
`other`:The mask to compare with this value. | — | — | -| [`public override bool Inno.Scene.Layers.GameLayerMask.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L111) | Determines whether this instance and the supplied value represent the same logical state.
返回:true when both values represent the same logical state; otherwise, false.
`obj`:The object to compare with this instance. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.FromLayers(System.Collections.Generic.IEnumerable layers)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L49) | Creates a mask containing the supplied layers.
返回:A mask containing every supplied layer.
`layers`:The layers whose bits should be enabled. | — | `System.ArgumentNullException`:Thrown when layers is null. | -| [`public override int Inno.Scene.Layers.GameLayerMask.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L119) | Computes a hash code from the fields that participate in logical equality.
返回:A hash code consistent with the implemented equality contract. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator &(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L158) | Retains only layer bits enabled in both masks.
返回:The bitwise intersection of both masks.
`left`:The first mask.
`right`:The second mask. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator |(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L141) | Combines the enabled bits from two masks.
返回:The bitwise union of both masks.
`left`:The first mask.
`right`:The second mask. | — | — | -| [`public static bool Inno.Scene.Layers.GameLayerMask.operator ==(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L186) | Determines whether two masks contain the same bits.
返回:true when both masks are equal.
`left`:The first mask.
`right`:The second mask. | — | — | -| [`public static bool Inno.Scene.Layers.GameLayerMask.operator !=(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L203) | Determines whether two masks contain different bits.
返回:true when both masks differ.
`left`:The first mask.
`right`:The second mask. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator ~(Inno.Scene.Layers.GameLayerMask mask)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L172) | Inverts every layer bit in a mask.
返回:The complement of the supplied mask.
`mask`:The mask to invert. | — | — | -| [`public override string Inno.Scene.Layers.GameLayerMask.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L127) | Formats this value as a human-readable representation.
返回:The human-readable representation of this value. | — | — | -| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.With(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L78) | Returns a mask with the supplied layer enabled.
返回:The updated immutable mask.
`layer`:The layer consumed by with; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.Without(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L89) | Returns a mask with the supplied layer disabled.
返回:The updated immutable mask.
`layer`:The layer consumed by without; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.everything { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L19) | Gets a mask containing every supported layer. | — | — | -| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.none { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L14) | Gets a mask containing no layers. | — | — | -| [`public uint Inno.Scene.Layers.GameLayerMask.value { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L35) | Gets the raw thirty-two-bit mask value. | — | — | -| [`public readonly struct Inno.Scene.Layers.GameLayerMask : System.IEquatable`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L9) | Stores a compact set of scene layers for rendering, physics, and query filtering. | — | — | - -### `Inno.Scene.MissingGameComponent` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Extensibility.Types.TypeRef Inno.Scene.MissingGameComponent.missingType { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L45) | Gets the logical identity of the unavailable component type. | — | — | -| [`public string Inno.Scene.MissingGameComponent.missingTypeName { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L50) | Gets the last known managed type name for diagnostics and editor presentation. | — | — | -| [`public sealed class Inno.Scene.MissingGameComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L19) | Preserves the identity, order, and serialized state of a component whose managed type is unavailable. | The scene serializer and script reload pipeline create this placeholder automatically. It does not retain the unavailable managed System.Type or any object from its collectible assembly load context. | — | - -### `Inno.Scene.MissingGameSystem` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Extensibility.Types.TypeRef Inno.Scene.MissingGameSystem.missingType { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L46) | Gets the logical identity of the unavailable system type. | — | — | -| [`public string Inno.Scene.MissingGameSystem.missingTypeName { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L51) | Gets the last known managed type name for diagnostics and editor presentation. | — | — | -| [`public sealed class Inno.Scene.MissingGameSystem : Inno.Scene.GameSystem`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L19) | Preserves the identity, order, and serialized state of a scene system whose managed type is unavailable. | The scene serializer and script reload pipeline create this placeholder automatically. It remains disabled and does not retain the unavailable managed System.Type or its collectible assembly load context. | — | - -### `Inno.Scene.PrefabAsset` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.PrefabAsset.PrefabAsset()`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L18) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public static Inno.Scene.PrefabAsset Inno.Scene.PrefabAsset.Capture(Inno.Scene.GameObject root, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L40) | Captures a game object subtree into a new unsaved prefab asset.
返回:A prefab asset ready to save.
`root`:Prefab source root.
`serialization`:The serialization registry that owns the active prefab converter generation.
`assets`:The runtime asset generation used to resolve persistent references in the prefab graph. | — | — | -| [`public Inno.Scene.EngineAssetContent Inno.Scene.PrefabAsset.CaptureContent()`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L133) | Captures the immutable runtime payload and dependency descriptors required by the prefab asset pipeline.
返回:A detached authoring-content snapshot owned by the caller. | — | — | -| [`public static Inno.Scene.PrefabAsset Inno.Scene.PrefabAsset.CreateImported(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L112) | Creates an imported prefab asset from validated runtime content produced by the prefab asset pipeline.
返回:A prefab asset ready for an asset import writer to commit.
`payload`:The complete serialized runtime object subtree.
`dependencies`:The direct persistent asset dependencies captured with the subtree. | — | — | -| [`public Inno.Scene.GameObject Inno.Scene.PrefabAsset.Instantiate(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Scene.Components.Transform? parent = null)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L77) | Instantiates this prefab into a scene using newly generated object identities.
返回:The instantiated root game object.
`scene`:Target scene.
`serialization`:The serialization registry that owns the active prefab converter generation.
`assets`:The runtime asset generation used to resolve persistent references in the prefab graph.
`parent`:Optional target parent. | — | — | -| [`public sealed class Inno.Scene.PrefabAsset : Inno.Assets.AssetObject`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L18) | Stores a persistent game object subtree that can be instantiated repeatedly. | — | — | - -### `Inno.Scene.PrefabInstanceInfo` - @@ -854,71 +518,27 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.GameObject Inno.Scene.PrefabInstanceInfo.instanceRoot { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L47) | Gets the root object of this prefab instance connection. | — | — | -| [`public bool Inno.Scene.PrefabInstanceInfo.isMissing { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L62) | Gets whether the source prefab was unavailable during restoration. | — | — | -| [`public bool Inno.Scene.PrefabInstanceInfo.isRoot { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L52) | Gets whether this object is the instance connection root. | — | — | -| [`public bool Inno.Scene.PrefabInstanceInfo.isVariant { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L57) | Gets whether the connection originates from a prefab variant. | — | — | -| [`public int Inno.Scene.PrefabInstanceInfo.orphanedOverrideCount { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L72) | Gets the number of retained overrides that no longer match the current source. | — | — | -| [`public int Inno.Scene.PrefabInstanceInfo.overrideCount { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L67) | Gets the number of retained overrides for this instance connection. | — | — | -| [`public System.Guid Inno.Scene.PrefabInstanceInfo.sourceAssetId { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L37) | Gets the persistent identity of the source prefab asset. | — | — | -| [`public System.Guid Inno.Scene.PrefabInstanceInfo.sourceObjectId { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L42) | Gets the source-local identity represented by this object. | — | — | -| [`public sealed class Inno.Scene.PrefabInstanceInfo`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L10) | Describes the read-only source connection retained by one prefab instance object. | — | — | - -### `Inno.Scene.SceneAsset` - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.SceneAsset.SceneAsset()`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L16) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public static Inno.Scene.SceneAsset Inno.Scene.SceneAsset.Capture(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L38) | Captures a runtime scene into a new unsaved scene asset.
返回:A detached scene asset that an authoring asset database can save.
`scene`:Scene to capture.
`serialization`:The serialization registry that owns the active scene converter generation.
`assets`:The asset database generation used to resolve references while collecting dependencies. | — | — | -| [`public Inno.Scene.EngineAssetContent Inno.Scene.SceneAsset.CaptureContent()`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L136) | Captures the immutable runtime payload and dependency descriptors required by the scene asset pipeline.
返回:A detached authoring-content snapshot owned by the caller. | — | — | -| [`public void Inno.Scene.SceneAsset.CaptureFrom(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L61) | Replaces the pending source content with a fresh capture while preserving this asset identity.
`scene`:Scene to capture.
`serialization`:The serialization registry that owns the active scene converter generation.
`assets`:The asset database generation used to resolve references while collecting dependencies. | — | — | -| [`public static Inno.Scene.SceneAsset Inno.Scene.SceneAsset.CreateImported(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L115) | Creates an imported scene asset from validated runtime content produced by the scene asset pipeline.
返回:A scene asset ready for an asset import writer to commit.
`payload`:The complete serialized runtime scene graph.
`dependencies`:The direct persistent asset dependencies captured with the scene graph. | — | — | -| [`public Inno.Scene.GameScene Inno.Scene.SceneAsset.Instantiate(Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L88) | Creates an unloaded runtime scene and acquires its hard asset dependencies.
返回:A newly allocated scene.
`serialization`:The serialization registry that owns the active scene converter generation.
`assets`:The runtime asset generation used to resolve persistent references in the scene graph. | — | — | -| [`protected override void Inno.Scene.SceneAsset.OnRuntimePayloadChanged(System.ReadOnlyMemory previousPayload, System.ReadOnlyMemory currentPayload)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L156) | Rebuilds runtime-derived state after the serialized asset payload changes.
`previousPayload`:The previous payload consumed by on runtime payload changed; ownership remains with the caller unless explicitly stated otherwise.
`currentPayload`:The current payload consumed by on runtime payload changed; ownership remains with the caller unless explicitly stated otherwise. | — | — | -| [`public sealed class Inno.Scene.SceneAsset : Inno.Assets.AssetObject`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L16) | Stores imported scene source that can create independent runtime scenes. | — | — | - -### `Inno.Scene.SceneContentSource` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.References.ContentReadScope Inno.Scene.SceneContentSource.CreateScope(Inno.Scene.SceneWorld world)`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L22) | Captures the ordered roots and primary identity of one scene world.
返回:A caller-owned, operation-scoped identity view.
`world`:The owner whose current scene set should be visible. | — | — | -| [`public static Inno.References.ContentReadScope Inno.Scene.SceneContentSource.CreateScope(System.Collections.Generic.IEnumerable scenes, Inno.Scene.GameScene? activeScene)`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L40) | Captures an explicit coherent scene presentation selected by a host.
返回:A caller-owned scope that retains no scene instances.
`scenes`:The ordered live scene roots.
`activeScene`:The optional primary root, which must be in the collection. | — | — | -| [`public static class Inno.Scene.SceneContentSource`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L11) | Projects loaded scenes into the shared content protocol without depending on a rendering or audio model. | — | — | - -### `Inno.Scene.SceneElementSerialization` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static byte[] Inno.Scene.SceneElementSerialization.CaptureState(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L39) | Captures one element's logical type, neutral properties, asset dependencies and scene-reference aliases.
返回:Neutral element bytes suitable for History and restoration without retaining the current managed type.
`target`:The live component or system, including a missing placeholder.
`serialization`:The owner's current serializer generation.
`assets`:The owner's complete asset resolver used during capture. | — | `System.ArgumentException`:The target is not a component or system.
`System.InvalidOperationException`:The element is not live in a loaded scene. | -| [`public static Inno.Scene.GameComponent Inno.Scene.SceneElementSerialization.RestoreComponent(Inno.Scene.GameObject owner, Inno.Extensibility.Types.TypeRef type, System.Guid persistentId, int componentIndex, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L166) | Recreates one component without invoking Reset and restores its persistent properties.
返回:The recreated component, or a preserved missing placeholder when its logical type is unavailable.
`owner`:The live GameObject that will own the component.
`type`:The reload-safe identity of the current component implementation.
`persistentId`:The component instance identity to preserve.
`componentIndex`:The requested attachment index.
`stateData`:Complete neutral element bytes produced by CaptureState, including missing-state metadata.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The resolver used to materialize canonical asset references in restored properties. | — | `System.ArgumentNullException`:Thrown when owner is null.
`System.IO.InvalidDataException`:Thrown when the payload is invalid or the stable type resolves to an incompatible component.
`System.InvalidOperationException`:Thrown when the identity or component multiplicity conflicts, property restoration is incomplete, or a failed restoration cannot remove its partially created component. | -| [`public static void Inno.Scene.SceneElementSerialization.RestoreState(Inno.Scene.EngineObject target, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L101) | Restores a captured element state into the same logical type, including neutral state on a missing placeholder.
`target`:The attached element receiving the state.
`stateData`:Bytes produced by Inno.Scene.SceneElementSerialization.CaptureState(Inno.Scene.EngineObject,Inno.Core.Serialization.SerializationRegistry,Inno.Assets.IAssetReferenceResolver).
`serialization`:The owner's current serializer generation.
`assets`:The owner's complete asset resolver. | — | `System.IO.InvalidDataException`:The payload is invalid or represents a different logical type.
`System.InvalidOperationException`:Property or reference restoration is incomplete. | -| [`public static Inno.Scene.GameSystem Inno.Scene.SceneElementSerialization.RestoreSystem(Inno.Scene.GameScene scene, Inno.Extensibility.Types.TypeRef type, System.Guid persistentId, int systemIndex, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L239) | Recreates one scene system without invoking Reset and restores its persistent properties.
返回:The recreated system, or a preserved missing placeholder when its logical type is unavailable.
`scene`:The live loaded scene that will own the system.
`type`:The reload-safe identity of the current system implementation.
`persistentId`:The system instance identity to preserve.
`systemIndex`:The requested display and serialization index.
`stateData`:Complete neutral element bytes produced by CaptureState, including missing-state metadata.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The resolver used to materialize canonical asset references in restored properties. | — | `System.ArgumentNullException`:Thrown when scene is null.
`System.IO.InvalidDataException`:Thrown when the payload is invalid or the stable type resolves to an incompatible system.
`System.InvalidOperationException`:Thrown when the identity or system multiplicity conflicts, property restoration is incomplete, or a failed restoration cannot remove its partially created system. | -| [`public static class Inno.Scene.SceneElementSerialization`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L16) | Recreates individual scene components and systems from stable type and object identities. | — | — | - -### `Inno.Scene.SceneManager` - + + + + + + + + + + + + + + + + + + + + + @@ -937,108 +557,6 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static void Inno.Scene.SceneManager.FixedUpdate(float fixedDeltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L157) | Advances fixed-step scene lifecycle callbacks in the current runtime session.
`fixedDeltaTime`:The fixed simulation interval in seconds. | — | — | -| [`public static int Inno.Scene.SceneManager.GetSceneIndex(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L39) | Gets the hierarchy index of a scene loaded by the current runtime session.
返回:The zero-based hierarchy index.
`scene`:The loaded scene to locate. | — | — | -| [`public static void Inno.Scene.SceneManager.LateUpdate(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L173) | Advances late scene lifecycle callbacks in the current runtime session.
`deltaTime`:The elapsed frame time in seconds. | — | — | -| [`public static Inno.Scene.GameScene Inno.Scene.SceneManager.LoadNewScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/SceneManager.cs#L87) | Creates and loads a new active scene in the current runtime session.
返回:The newly created and loaded scene.
`name`:The initial scene display name. | — | — | -| [`public static Inno.Scene.GameScene Inno.Scene.SceneManager.LoadNewSceneAdditive(string name = "Untitled Scene", bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L101) | Creates and additively loads a new scene in the current runtime session.
返回:The newly created and loaded scene.
`name`:The initial scene display name.
`makeActive`:Whether the new scene becomes active. | — | — | -| [`public static void Inno.Scene.SceneManager.LoadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L61) | Replaces the current session scene set with one active scene.
`scene`:The scene to load. | — | — | -| [`public static void Inno.Scene.SceneManager.LoadSceneAdditive(Inno.Scene.GameScene scene, bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L72) | Loads a scene alongside the current session scene set.
`scene`:The scene to load additively.
`makeActive`:Whether the loaded scene becomes active. | — | — | -| [`public static void Inno.Scene.SceneManager.MoveGameObjectToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L124) | Moves a live object subtree into another scene owned by the current runtime session.
`gameObject`:The live root object to move.
`destination`:The loaded destination scene. | — | — | -| [`public static void Inno.Scene.SceneManager.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L113) | Makes a loaded scene active in the current runtime session.
`scene`:The loaded scene to activate. | — | — | -| [`public static void Inno.Scene.SceneManager.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L50) | Moves a loaded scene to a hierarchy index without changing the active scene.
`scene`:The loaded scene to move.
`sceneIndex`:The requested zero-based hierarchy index. | — | — | -| [`public static void Inno.Scene.SceneManager.UnloadActiveScene()`](../../src/content/scene/Inno.Scene/SceneManager.cs#L133) | Unloads the active scene in the current runtime session when one exists. | — | — | -| [`public static void Inno.Scene.SceneManager.UnloadAllScenes()`](../../src/content/scene/Inno.Scene/SceneManager.cs#L149) | Unloads every scene from the current runtime session. | — | — | -| [`public static bool Inno.Scene.SceneManager.UnloadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L144) | Unloads one scene from the current runtime session.
返回:true when the scene was loaded; otherwise, false.
`scene`:The scene to unload. | — | — | -| [`public static void Inno.Scene.SceneManager.Update(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L165) | Advances variable-step scene lifecycle callbacks in the current runtime session.
`deltaTime`:The elapsed frame time in seconds. | — | — | -| [`public static Inno.Scene.GameScene? Inno.Scene.SceneManager.activeScene { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L18) | Gets the active scene in the current runtime session. | — | — | -| [`public static bool Inno.Scene.SceneManager.hasActiveScene { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L23) | Gets whether the current runtime session has an active scene. | — | — | -| [`public static System.Collections.Generic.IReadOnlyList Inno.Scene.SceneManager.loadedScenes { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L28) | Gets an immutable snapshot of scenes loaded by the current runtime session. | — | — | -| [`public static class Inno.Scene.SceneManager`](../../src/content/scene/Inno.Scene/SceneManager.cs#L13) | Provides Unity-style scene operations by resolving the world in the current runtime execution context. | This façade owns no mutable scene state. Engine internals should depend on a concrete Inno.Scene.SceneWorld and only script-facing code should use this type. | — | - -### `Inno.Scene.ScenePropertySerialization` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static byte[] Inno.Scene.ScenePropertySerialization.CaptureProperties(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L82) | Captures all persistent properties without serializing the complete scene.
返回:Neutral bytes containing independently encoded properties.
`target`:The live scene object whose state should be captured.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The resolver used to preserve canonical asset references in captured values. | — | `System.ArgumentNullException`:Thrown when target is null.
`System.InvalidOperationException`:Thrown when the target is destroyed or not owned by a loaded scene. | -| [`public static byte[] Inno.Scene.ScenePropertySerialization.CaptureProperty(Inno.Scene.EngineObject target, string propertyName, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L43) | Captures one persistent property without serializing the complete scene.
返回:Neutral bytes that can be restored into the same logical object identity.
`target`:The live scene object containing the property.
`propertyName`:The exact serialized member key.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The resolver used to preserve canonical asset references in captured values. | — | `System.ArgumentNullException`:Thrown when target is null.
`System.ArgumentException`:Thrown when propertyName is empty or unknown.
`System.InvalidOperationException`:Thrown when the target is destroyed or not owned by a loaded scene. | -| [`public static System.Collections.Generic.IReadOnlyList Inno.Scene.ScenePropertySerialization.CapturePropertySnapshots(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L113) | Captures each persistent property independently while preserving scene-reference identities.
返回:Stable ordered property snapshots suitable for compact delta comparison.
`target`:Live scene object whose properties should be captured.
`serialization`:Registry owning the active converter generation.
`assets`:Resolver used to preserve canonical asset references. | — | — | -| [`public static Inno.Core.Serialization.SerializationPropertyRestoreResult Inno.Scene.ScenePropertySerialization.RestoreProperties(Inno.Scene.EngineObject target, System.ReadOnlySpan data, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Core.Serialization.SerializationPropertyRestoreMode mode = Inno.Core.Serialization.SerializationPropertyRestoreMode.Strict)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L156) | Restores independently captured properties into a live scene object.
返回:A summary of restored, ignored, and incompatible properties.
`target`:The current object generation receiving the values.
`data`:Bytes produced by Inno.Scene.ScenePropertySerialization.CaptureProperty(Inno.Scene.EngineObject,System.String,Inno.Core.Serialization.SerializationRegistry,Inno.Assets.IAssetReferenceResolver) or Inno.Scene.ScenePropertySerialization.CaptureProperties(Inno.Scene.EngineObject,Inno.Core.Serialization.SerializationRegistry,Inno.Assets.IAssetReferenceResolver).
`mode`:The compatibility policy applied to matching current properties.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The resolver used to materialize canonical asset references in restored values. | — | `System.ArgumentNullException`:Thrown when target is null.
`System.InvalidOperationException`:Thrown when strict restoration fails or the target is not in a loaded scene. | -| [`public static class Inno.Scene.ScenePropertySerialization`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L14) | Captures and restores individual scene-object properties while preserving scene reference identities. | — | — | - -### `Inno.Scene.SceneReloadDiagnostic` - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public string Inno.Scene.SceneReloadDiagnostic.code { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L34) | Gets the stable diagnostic code. | — | — | -| [`public string Inno.Scene.SceneReloadDiagnostic.currentPropertyType { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L69) | Gets the current declared property type name. | — | — | -| [`public string Inno.Scene.SceneReloadDiagnostic.message { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L44) | Gets the human-readable diagnostic message. | — | — | -| [`public System.Guid Inno.Scene.SceneReloadDiagnostic.objectPersistentId { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L54) | Gets the persistent identity of the affected component or system. | — | — | -| [`public string Inno.Scene.SceneReloadDiagnostic.previousPropertyType { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L64) | Gets the previous declared property type name. | — | — | -| [`public string Inno.Scene.SceneReloadDiagnostic.propertyName { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L59) | Gets the incompatible serialized property name. | — | — | -| [`public System.Guid Inno.Scene.SceneReloadDiagnostic.scenePersistentId { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L49) | Gets the persistent identity of the affected scene. | — | — | -| [`public Inno.Core.Diagnostics.DiagnosticSeverity Inno.Scene.SceneReloadDiagnostic.severity { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L39) | Gets the diagnostic severity. | — | — | -| [`public sealed class Inno.Scene.SceneReloadDiagnostic`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L9) | Describes a non-fatal issue encountered while migrating scene state to a new assembly generation. | — | — | - -### `Inno.Scene.SceneReloadService` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.SceneReloadService.SceneReloadService(Inno.Scene.SceneWorld world, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L34) | Creates a scene reload service bound to one serialization generation owner.
`serialization`:The serialization registry used to capture and restore scene state.
`world`:The isolated scene world whose active generation is captured.
`assets`:The asset reference generation used throughout capture, activation, and rollback. | — | `System.ArgumentNullException`:Thrown when world, serialization, or assets is null. | -| [`public Inno.Scene.ISceneReloadStateTransfer Inno.Scene.SceneReloadService.Capture(Inno.Extensibility.Types.TypeCacheReloadContext context)`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L53) | Captures all loaded scene objects affected by a prepared type-cache reload.
返回:A staged state transfer that has not modified live objects.
`context`:The prepared type-cache reload context. | — | — | -| [`public sealed class Inno.Scene.SceneReloadService`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L12) | Creates scene state-transfer transactions for assembly generation changes. | — | — | - -### `Inno.Scene.SceneSubtreeSerialization` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static byte[] Inno.Scene.SceneSubtreeSerialization.Capture(Inno.Scene.GameObject root, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L37) | Captures one live GameObject and all descendants without serializing unrelated scene objects.
返回:Neutral bytes containing the subtree structure, components, properties, and identity references.
`root`:The live subtree root.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The asset-reference resolver used to encode referenced asset identities. | — | `System.ArgumentNullException`:Thrown when root is null.
`System.InvalidOperationException`:Thrown when the root is destroyed or detached. | -| [`public static Inno.Scene.GameObject Inno.Scene.SceneSubtreeSerialization.Restore(Inno.Scene.GameScene scene, System.ReadOnlySpan data, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Scene.Components.Transform? parent, int siblingIndex)`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L81) | Restores a previously captured subtree into a loaded scene with its original persistent identities.
返回:The restored subtree root.
`scene`:The loaded destination scene.
`data`:Bytes created by Inno.Scene.SceneSubtreeSerialization.Capture(Inno.Scene.GameObject,Inno.Core.Serialization.SerializationRegistry,Inno.Assets.IAssetReferenceResolver).
`parent`:The optional external parent restored after the subtree is created.
`siblingIndex`:The requested sibling index under parent or at the scene root.
`serialization`:The serialization registry that owns the active converter generation.
`assets`:The asset-reference resolver used to restore referenced asset identities. | — | `System.ArgumentNullException`:Thrown when scene is null.
`System.InvalidOperationException`:Thrown when the scene is unavailable or an identity conflicts. | -| [`public static class Inno.Scene.SceneSubtreeSerialization`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L14) | Captures and restores one GameObject subtree with its persistent object and component identities. | — | — | - -### `Inno.Scene.SceneTypeResolutionException` - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scene.SceneTypeResolutionException.SceneTypeResolutionException(System.Guid stableTypeId, string elementKind)`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L23) | Creates an exception for one unresolved serialized scene type.
`stableTypeId`:The stable identity stored by the serialized scene element.
`elementKind`:The human-readable scene element kind, such as component or system. | — | `System.ArgumentException`:Thrown when stableTypeId is empty or elementKind is empty. | -| [`public string Inno.Scene.SceneTypeResolutionException.elementKind { get; }`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L44) | Gets the scene element kind that expected the missing type. | — | — | -| [`public System.Guid Inno.Scene.SceneTypeResolutionException.stableTypeId { get; }`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L39) | Gets the unresolved stable type identity. | — | — | -| [`public sealed class Inno.Scene.SceneTypeResolutionException : System.Exception`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L9) | Reports that a serialized scene element cannot be created because its stable type is not present in the active type catalog. | — | — | - -### `Inno.Scene.SceneWorld` - @@ -1062,45 +580,351 @@ History 通过 SceneEdits 捕获这些 bytes,不直接使用上面的 Host 工 + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public Inno.Scene.SceneWorld.SceneWorld(Inno.Core.Identity.IdentityAllocator identities, Inno.Extensibility.Types.TypeCatalog types)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L41) | Creates an empty scene world bound to one session identity allocator.
`identities`:The allocator that owns identities for scenes and their elements.
`types`:The host type catalog used to derive scene component and system generations. | — | `System.ArgumentNullException`:Thrown when identities is null. | -| [`public void Inno.Scene.SceneWorld.ConfigurePrefabInstantiation(Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L73) | Binds the session-owned serialization and asset generations used by scene prefab instances.
`serialization`:The active serialization registry for this session.
`assets`:The asset resolver that owns prefab dependencies. | — | `System.InvalidOperationException`:The binding was already configured. | -| [`public void Inno.Scene.SceneWorld.Dispose()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L439) | Unloads every scene and permanently releases this world. | — | — | -| [`public System.IDisposable Inno.Scene.SceneWorld.EnterScope()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L144) | Binds this world and its identity allocator to the current asynchronous execution context.
返回:A strict last-in-first-out execution scope owned by the caller. | — | `System.ObjectDisposedException`:Thrown when this world has been disposed. | -| [`public TObject? Inno.Scene.SceneWorld.Find(System.Guid persistentId) where TObject : Inno.Core.Identity.IdentityObject`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L131) | Resolves a live scene object by its persistent identity within this world.
返回:The live compatible object, or null when this world does not own one.
`persistentId`:The persistent identity to resolve. | — | — | -| [`public void Inno.Scene.SceneWorld.FixedUpdate(float fixedDeltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L400) | Advances fixed-step lifecycle callbacks for every loaded scene.
`fixedDeltaTime`:The fixed simulation interval in seconds. | — | — | -| [`public int Inno.Scene.SceneWorld.GetSceneIndex(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L163) | Gets the hierarchy index of a loaded scene.
返回:The zero-based hierarchy index.
`scene`:The loaded scene to locate. | — | `System.InvalidOperationException`:Thrown when the scene is not loaded by this world. | -| [`public void Inno.Scene.SceneWorld.LateUpdate(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L428) | Advances late lifecycle callbacks for every loaded scene.
`deltaTime`:The elapsed frame time in seconds. | — | — | -| [`public Inno.Scene.GameScene Inno.Scene.SceneWorld.LoadNewScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L255) | Creates and loads a new active scene.
返回:The newly created and loaded scene.
`name`:The initial scene display name. | — | — | -| [`public Inno.Scene.GameScene Inno.Scene.SceneWorld.LoadNewSceneAdditive(string name = "Untitled Scene", bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L275) | Creates and additively loads a new scene.
返回:The newly created and loaded scene.
`name`:The initial scene display name.
`makeActive`:Whether the new scene becomes active. | — | — | -| [`public void Inno.Scene.SceneWorld.LoadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L208) | Replaces the loaded set with one scene and makes it active.
`scene`:The scene to load. | — | — | -| [`public void Inno.Scene.SceneWorld.LoadSceneAdditive(Inno.Scene.GameScene scene, bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L230) | Loads a scene alongside the existing scene set.
`scene`:The scene to load additively.
`makeActive`:Whether the loaded scene becomes active. | — | — | -| [`public void Inno.Scene.SceneWorld.MoveGameObjectToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L315) | Moves a live object subtree between two scenes loaded by this world.
`gameObject`:The live root object to move.
`destination`:The loaded destination scene. | — | `System.InvalidOperationException`:Thrown when either scene is not loaded or the object is not live. | -| [`public void Inno.Scene.SceneWorld.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L294) | Makes one loaded scene active without changing the loaded set.
`scene`:The loaded scene to activate. | — | `System.InvalidOperationException`:Thrown when the scene is not loaded by this world. | -| [`public void Inno.Scene.SceneWorld.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L185) | Moves a loaded scene to a hierarchy index without changing the active scene.
`scene`:The loaded scene to move.
`sceneIndex`:The requested zero-based hierarchy index. | — | `System.InvalidOperationException`:Thrown when the scene is not loaded by this world. | -| [`public void Inno.Scene.SceneWorld.UnloadActiveScene()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L334) | Unloads the active scene when one exists. | — | — | -| [`public void Inno.Scene.SceneWorld.UnloadAllScenes()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L372) | Unloads every scene while preserving the first lifecycle failure. | — | — | -| [`public bool Inno.Scene.SceneWorld.UnloadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L350) | Unloads one scene and selects another loaded scene when necessary.
返回:true when the scene was loaded by this world; otherwise, false.
`scene`:The scene to unload. | — | — | -| [`public void Inno.Scene.SceneWorld.Update(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L414) | Advances variable-step lifecycle callbacks for every loaded scene.
`deltaTime`:The elapsed frame time in seconds. | — | — | +| [`public sealed class Inno.Scene.EngineAssetContent`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L11) | Carries immutable scene or prefab runtime content across the runtime-to-authoring asset boundary. | — | — | +| [`public Inno.Scene.EngineAssetContent.EngineAssetContent(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L28) | Creates a detached engine asset content snapshot.
`payload`: The complete serialized runtime graph.
`dependencies`: The direct persistent asset dependencies of the graph. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public byte[] Inno.Scene.EngineAssetContent.GetPayload()`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L45) | Gets a detached copy of the serialized runtime graph. | A payload copy owned by the caller. | — | +| [`public Inno.Assets.AssetDependency[] Inno.Scene.EngineAssetContent.GetDependencies()`](../../src/content/scene/Inno.Scene/Assets/EngineAssetContent.cs#L53) | Gets a detached copy of direct persistent asset dependencies. | A dependency array owned by the caller. | — | +| [`public sealed class Inno.Scene.PrefabAsset : Inno.Assets.AssetObject`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L18) | Stores a persistent game object subtree that can be instantiated repeatedly. | — | — | +| [`public static Inno.Scene.PrefabAsset Inno.Scene.PrefabAsset.Capture(Inno.Scene.GameObject root, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L40) | Captures a game object subtree into a new unsaved prefab asset.
`root`: Prefab source root.
`serialization`: The serialization registry that owns the active prefab converter generation.
`assets`: The runtime asset generation used to resolve persistent references in the prefab graph. | A prefab asset ready to save. | — | +| [`public Inno.Scene.GameObject Inno.Scene.PrefabAsset.Instantiate(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Scene.Components.Transform? parent = null)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L77) | Instantiates this prefab into a scene using newly generated object identities.
`scene`: Target scene.
`serialization`: The serialization registry that owns the active prefab converter generation.
`assets`: The runtime asset generation used to resolve persistent references in the prefab graph.
`parent`: Optional target parent. | The instantiated root game object. | — | +| [`public static Inno.Scene.PrefabAsset Inno.Scene.PrefabAsset.CreateImported(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L112) | Creates an imported prefab asset from validated runtime content produced by the prefab asset pipeline.
`payload`: The complete serialized runtime object subtree.
`dependencies`: The direct persistent asset dependencies captured with the subtree. | A prefab asset ready for an asset import writer to commit. | — | +| [`public Inno.Scene.EngineAssetContent Inno.Scene.PrefabAsset.CaptureContent()`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L133) | Captures the immutable runtime payload and dependency descriptors required by the prefab asset pipeline. | A detached authoring-content snapshot owned by the caller. | — | +| [`public Inno.Scene.PrefabAsset.PrefabAsset()`](../../src/content/scene/Inno.Scene/Assets/PrefabAsset.cs#L18) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.SceneAsset : Inno.Assets.AssetObject`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L16) | Stores imported scene source that can create independent runtime scenes. | — | — | +| [`public static Inno.Scene.SceneAsset Inno.Scene.SceneAsset.Capture(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L38) | Captures a runtime scene into a new unsaved scene asset.
`scene`: Scene to capture.
`serialization`: The serialization registry that owns the active scene converter generation.
`assets`: The asset database generation used to resolve references while collecting dependencies. | A detached scene asset that an authoring asset database can save. | — | +| [`public void Inno.Scene.SceneAsset.CaptureFrom(Inno.Scene.GameScene scene, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L61) | Replaces the pending source content with a fresh capture while preserving this asset identity.
`scene`: Scene to capture.
`serialization`: The serialization registry that owns the active scene converter generation.
`assets`: The asset database generation used to resolve references while collecting dependencies. | — | — | +| [`public Inno.Scene.GameScene Inno.Scene.SceneAsset.Instantiate(Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L88) | Creates an unloaded runtime scene and acquires its hard asset dependencies.
`serialization`: The serialization registry that owns the active scene converter generation.
`assets`: The runtime asset generation used to resolve persistent references in the scene graph. | A newly allocated scene. | — | +| [`public static Inno.Scene.SceneAsset Inno.Scene.SceneAsset.CreateImported(System.ReadOnlySpan payload, System.Collections.Generic.IReadOnlyList dependencies)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L115) | Creates an imported scene asset from validated runtime content produced by the scene asset pipeline.
`payload`: The complete serialized runtime scene graph.
`dependencies`: The direct persistent asset dependencies captured with the scene graph. | A scene asset ready for an asset import writer to commit. | — | +| [`public Inno.Scene.EngineAssetContent Inno.Scene.SceneAsset.CaptureContent()`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L136) | Captures the immutable runtime payload and dependency descriptors required by the scene asset pipeline. | A detached authoring-content snapshot owned by the caller. | — | +| [`protected override void Inno.Scene.SceneAsset.OnRuntimePayloadChanged(System.ReadOnlyMemory previousPayload, System.ReadOnlyMemory currentPayload)`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L156) | Rebuilds runtime-derived state after the serialized asset payload changes.
`previousPayload`: The previous payload consumed by on runtime payload changed; ownership remains with the caller unless explicitly stated otherwise.
`currentPayload`: The current payload consumed by on runtime payload changed; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public Inno.Scene.SceneAsset.SceneAsset()`](../../src/content/scene/Inno.Scene/Assets/SceneAsset.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.AllowMultipleComponentAttribute : System.Attribute`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleComponentAttribute.cs#L9) | Allows multiple instances of a concrete component type on one game object. | — | — | +| [`public Inno.Scene.AllowMultipleComponentAttribute.AllowMultipleComponentAttribute()`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleComponentAttribute.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.AllowMultipleSystemAttribute : System.Attribute`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleSystemAttribute.cs#L9) | Allows multiple instances of a concrete type in one scene. | — | — | +| [`public Inno.Scene.AllowMultipleSystemAttribute.AllowMultipleSystemAttribute()`](../../src/content/scene/Inno.Scene/Attributes/AllowMultipleSystemAttribute.cs#L9) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.MissingGameComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L19) | Preserves the identity, order, and serialized state of a component whose managed type is unavailable. | — | — | +| [`public Inno.Extensibility.Types.TypeRef Inno.Scene.MissingGameComponent.missingType { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L45) | Gets the logical identity of the unavailable component type. | — | — | +| [`public string Inno.Scene.MissingGameComponent.missingTypeName { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameComponent.cs#L50) | Gets the last known managed type name for diagnostics and editor presentation. | — | — | +| [`public sealed class Inno.Scene.MissingGameSystem : Inno.Scene.GameSystem`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L19) | Preserves the identity, order, and serialized state of a scene system whose managed type is unavailable. | — | — | +| [`public Inno.Extensibility.Types.TypeRef Inno.Scene.MissingGameSystem.missingType { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L46) | Gets the logical identity of the unavailable system type. | — | — | +| [`public string Inno.Scene.MissingGameSystem.missingTypeName { get; }`](../../src/content/scene/Inno.Scene/Concrete/MissingGameSystem.cs#L51) | Gets the last known managed type name for diagnostics and editor presentation. | — | — | +| [`public sealed class Inno.Scene.Components.Transform : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L15) | Stores local transform data and exposes the scene hierarchy relationship. | — | — | +| [`public Inno.Scene.Components.Transform.Transform()`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L31) | Creates a transform with identity local values. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.localPosition { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L40) | Gets or sets the local position relative to the parent. | — | — | +| [`public Inno.Core.Mathematics.Quaternion Inno.Scene.Components.Transform.localRotation { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L54) | Gets or sets the local rotation relative to the parent. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.localScale { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L68) | Gets or sets the local scale relative to the parent. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.worldPosition { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L81) | Gets or sets the world-space position. | — | — | +| [`public Inno.Core.Mathematics.Quaternion Inno.Scene.Components.Transform.worldRotation { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L90) | Gets or sets the world-space rotation. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.worldScale { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L99) | Gets or sets the world-space scale. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Scene.Components.Transform.localToWorldMatrix { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L108) | Gets the exact local-to-world matrix, including the complete parent hierarchy. | — | — | +| [`public Inno.Core.Mathematics.Matrix Inno.Scene.Components.Transform.worldToLocalMatrix { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L116) | Gets the inverse of the exact local-to-world matrix. | — | `System.InvalidOperationException`: Thrown when a zero-scale hierarchy is not invertible. | +| [`public Inno.Scene.Components.Transform? Inno.Scene.Components.Transform.parent { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L128) | Gets the parent transform, or for a scene-level object. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.Components.Transform.children { get; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L133) | Gets child transforms in sibling order. | — | — | +| [`public int Inno.Scene.Components.Transform.siblingIndex { get; set; }`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L138) | Gets or sets this transform's index among its siblings. | — | — | +| [`public void Inno.Scene.Components.Transform.SetParent(Inno.Scene.Components.Transform? parent)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L153) | Sets the parent while preserving this transform's world-space values.
`parent`: New parent, or to move to scene level. | — | `System.InvalidOperationException`: Thrown for hierarchy cycles, cross-scene parents, or a non-invertible parent hierarchy. | +| [`public void Inno.Scene.Components.Transform.SetSiblingIndex(int siblingIndex)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L161) | Moves this transform within its current sibling collection.
`siblingIndex`: Requested zero-based sibling index. | — | — | +| [`public void Inno.Scene.Components.Transform.SetWorldTransform(Inno.Core.Mathematics.Vector3 position, Inno.Core.Mathematics.Quaternion rotation, Inno.Core.Mathematics.Vector3 scale)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L175) | Atomically applies world-space translation, rotation, and scale.
`position`: Requested world-space translation.
`rotation`: Requested world-space rotation.
`scale`: Requested world-space scale. | — | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.TransformPoint(Inno.Core.Mathematics.Vector3 point)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L190) | Transforms a local-space point through the complete parent hierarchy.
`point`: Point expressed in this transform's local space. | The corresponding world-space point. | — | +| [`public Inno.Core.Mathematics.Vector3 Inno.Scene.Components.Transform.InverseTransformPoint(Inno.Core.Mathematics.Vector3 point)`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L201) | Transforms a world-space point into this transform's local space.
`point`: Point expressed in world space. | The corresponding local-space point. | — | +| [`protected override void Inno.Scene.Components.Transform.Reset()`](../../src/content/scene/Inno.Scene/Concrete/Transform.cs#L206) | Restores this instance to its initial reusable state. | — | — | +| [`public static class Inno.Scene.SceneContentSource`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L11) | Projects loaded scenes into the shared content protocol without depending on a rendering or audio model. | — | — | +| [`public static Inno.References.ContentReadScope Inno.Scene.SceneContentSource.CreateScope(Inno.Scene.SceneWorld world)`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L22) | Captures the ordered roots and primary identity of one scene world.
`world`: The owner whose current scene set should be visible. | A caller-owned, operation-scoped identity view. | — | +| [`public static Inno.References.ContentReadScope Inno.Scene.SceneContentSource.CreateScope(System.Collections.Generic.IEnumerable scenes, Inno.Scene.GameScene? activeScene)`](../../src/content/scene/Inno.Scene/Content/SceneContentSource.cs#L40) | Captures an explicit coherent scene presentation selected by a host.
`scenes`: The ordered live scene roots.
`activeScene`: The optional primary root, which must be in the collection. | A caller-owned scope that retains no scene instances. | — | +| [`public abstract class Inno.Scene.EngineObject : Inno.Core.Identity.IdentityObject`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L11) | Provides identity and destruction state shared by all managed scene objects. | — | — | +| [`public bool Inno.Scene.EngineObject.isDestroyed { get; }`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L18) | Gets whether the engine has destroyed this object. | — | — | +| [`public static bool Inno.Scene.EngineObject.operator ==(Inno.Scene.EngineObject? left, Inno.Scene.EngineObject? right)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L32) | Compares engine objects using reference identity and destroyed-object null semantics.
`left`: Left operand.
`right`: Right operand. | when both operands refer to the same live object or both behave as null. | — | +| [`public static bool Inno.Scene.EngineObject.operator !=(Inno.Scene.EngineObject? left, Inno.Scene.EngineObject? right)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L57) | Compares engine objects using reference identity and destroyed-object null semantics.
`left`: Left operand.
`right`: Right operand. | when the operands do not represent the same object. | — | +| [`public override bool Inno.Scene.EngineObject.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L71) | Compares this instance to another object using reference identity.
`obj`: The object to compare with this instance. | when is this instance. | — | +| [`public override int Inno.Scene.EngineObject.GetHashCode()`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L79) | Returns a stable runtime reference hash code. | The reference hash code. | — | +| [`protected Inno.Scene.EngineObject.EngineObject()`](../../src/content/scene/Inno.Scene/Core/EngineObject.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Scene.GameBehavior : Inno.Scene.GameComponent, Inno.Scene.ISceneLifecycleObject`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L10) | Base component for independently enabled scene functionality with frame lifecycle callbacks. | — | — | +| [`public bool Inno.Scene.GameBehavior.enabled { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L22) | Gets or sets whether this component participates in scene lifecycle and frame updates. | — | — | +| [`public bool Inno.Scene.GameBehavior.isActiveAndEnabled { get; }`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L39) | Gets whether this component is enabled and active in its owning hierarchy. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.Awake()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L44) | Called once before this behavior first becomes active. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.Start()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L51) | Called once immediately before the first update of this behavior. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.OnEnable()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L58) | Called when this component becomes active and enabled. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.OnDisable()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L65) | Called when this component stops being active and enabled. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.Update()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L72) | Called during the variable-rate update stage. Use Time.deltaTime for frame timing. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.FixedUpdate()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L79) | Called during the fixed-rate update stage. Use Time.fixedDeltaTime for step timing. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.LateUpdate()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L86) | Called during the late update stage. Use Time.deltaTime for frame timing. | — | — | +| [`protected virtual void Inno.Scene.GameBehavior.OnDestroy()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L93) | Called immediately before this behavior is detached and destroyed. | — | — | +| [`protected Inno.Scene.GameBehavior.GameBehavior()`](../../src/content/scene/Inno.Scene/Core/GameBehavior.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public abstract class Inno.Scene.GameComponent : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L11) | Base type for data and behavior objects attached to a . | — | — | +| [`public Inno.Scene.GameObject Inno.Scene.GameComponent.gameObject { get; }`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L21) | Gets the owning game object. | — | `System.InvalidOperationException`: Thrown when the component is detached or destroyed. | +| [`public Inno.Scene.Components.Transform Inno.Scene.GameComponent.transform { get; }`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L32) | Gets the owning game object's transform. | — | `System.InvalidOperationException`: Thrown when the component is detached or destroyed. | +| [`protected virtual void Inno.Scene.GameComponent.Reset()`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L37) | Restores this component to its default state when it is added or explicitly reset. | — | — | +| [`protected Inno.Scene.GameComponent.GameComponent()`](../../src/content/scene/Inno.Scene/Core/GameComponent.cs#L11) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.GameObject : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L15) | Represents a scene-owned object and exposes its component-oriented API. | — | — | +| [`public const string Inno.Scene.GameObject.defaultTag = "Untagged"`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L20) | Defines the tag assigned to newly created game objects. | — | — | +| [`public bool Inno.Scene.GameObject.isRuntimeValid { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L43) | Gets whether this object is live in its owning scene. | — | — | +| [`public Inno.Scene.GameScene Inno.Scene.GameObject.scene { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L51) | Gets the owning scene. | — | `System.InvalidOperationException`: Thrown when this object is destroyed or detached. | +| [`public string Inno.Scene.GameObject.name { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L56) | Gets or sets the display name stored in the owning scene. | — | — | +| [`public string Inno.Scene.GameObject.tag { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L84) | Gets or sets the ordinal tag used to categorize and query this game object. | — | `System.ArgumentException`: Thrown when the assigned tag is empty or contains only white-space characters. | +| [`public Inno.Scene.Layers.GameLayer Inno.Scene.GameObject.layer { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L109) | Gets or sets the single runtime layer used to filter this game object. | — | — | +| [`public Inno.Scene.Components.Transform Inno.Scene.GameObject.transform { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L132) | Gets the mandatory transform component. | — | `System.InvalidOperationException`: Thrown when this object is destroyed or incompletely initialized. | +| [`public bool Inno.Scene.GameObject.activeSelf { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L144) | Gets whether this object is explicitly active. | — | — | +| [`public bool Inno.Scene.GameObject.activeInHierarchy { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L156) | Gets whether this object is active after parent hierarchy state is applied. | — | — | +| [`public bool Inno.Scene.GameObject.isPartOfPrefabInstance { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L161) | Gets whether this object retains a prefab source connection. | — | — | +| [`public Inno.Scene.GameObject? Inno.Scene.GameObject.prefabInstanceRoot { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L166) | Gets the root of this object's prefab instance connection. | — | — | +| [`public Inno.Scene.PrefabInstanceInfo? Inno.Scene.GameObject.prefabInstance { get; }`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L171) | Gets read-only information about this object's prefab source connection. | — | — | +| [`public void Inno.Scene.GameObject.SetActive(bool value)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L179) | Changes this object's explicit active state and updates its hierarchy subtree.
`value`: The concrete value read or transformed by this operation. | — | — | +| [`public TComponent Inno.Scene.GameObject.AddComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L190) | Creates and attaches a component of the requested type.
`TComponent`: Concrete component type. | The attached component. | — | +| [`public Inno.Scene.GameComponent Inno.Scene.GameObject.AddComponent(System.Type componentType)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L201) | Creates and attaches a component of the requested runtime type.
`componentType`: Concrete component type. | The attached component. | — | +| [`public TComponent Inno.Scene.GameObject.GetComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L216) | Gets the first attached component assignable to the requested type.
`TComponent`: Requested component type. | The first matching component. | `System.InvalidOperationException`: Thrown when no matching component exists. | +| [`public Inno.Scene.GameComponent Inno.Scene.GameObject.GetComponent(System.Type componentType)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L235) | Gets the first attached component assignable to a runtime type.
`componentType`: Requested component type. | The first matching component. | `System.InvalidOperationException`: Thrown when no matching component exists. | +| [`public bool Inno.Scene.GameObject.TryGetComponent(out TComponent? component) where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L255) | Tries to get the first attached component assignable to the requested type.
`component`: Matching component when found.
`TComponent`: Requested component type. | when a matching component exists. | — | +| [`public bool Inno.Scene.GameObject.HasComponent() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L275) | Gets whether a matching component is attached.
`TComponent`: Requested component type. | when a matching component exists. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameObject.GetComponents() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L286) | Gets all attached components assignable to the requested type in attachment order.
`TComponent`: Requested component type. | A stable component snapshot. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameObject.GetComponents()`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L295) | Gets all attached components in attachment order. | A stable component snapshot. | — | +| [`public int Inno.Scene.GameObject.GetComponentIndex(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L306) | Gets the attachment index of a component on this object.
`component`: Attached component to locate. | The zero-based attachment index. | — | +| [`public void Inno.Scene.GameObject.SetComponentIndex(Inno.Scene.GameComponent component, int componentIndex)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L322) | Moves an attached component to a requested attachment index. The mandatory Transform participates in ordering but remains non-removable.
`component`: Attached component to move.
`componentIndex`: Requested zero-based attachment index. | — | — | +| [`public void Inno.Scene.GameObject.ResetComponent(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L342) | Restores an attached component to the defaults defined by its optional Reset message.
`component`: GameComponent instance to reset. | — | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the object is invalid or the component is not attached to it. | +| [`public bool Inno.Scene.GameObject.RemoveComponent(Inno.Scene.GameComponent component)`](../../src/content/scene/Inno.Scene/Core/GameObject.cs#L360) | Removes and destroys a specific attached component.
`component`: GameComponent instance to remove. | when the component was attached and removed. | `System.InvalidOperationException`: Thrown when attempting to remove the mandatory transform. | +| [`public sealed class Inno.Scene.GameScene : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L17) | Owns a runtime scene, including its objects, components, hierarchy, and systems. | — | — | +| [`public Inno.Scene.GameScene.GameScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L36) | Creates an empty scene.
`name`: Initial scene name. | — | — | +| [`public string Inno.Scene.GameScene.name { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L57) | Gets or sets the scene display name. | — | — | +| [`public bool Inno.Scene.GameScene.isLoaded { get; }`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L70) | Gets whether this scene is currently owned by . | — | — | +| [`public ulong Inno.Scene.GameScene.hierarchyRevision { get; }`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L79) | Gets the owner-local change counter for object membership, parentage and sibling ordering. | — | — | +| [`public Inno.Scene.GameObject Inno.Scene.GameScene.CreateObject(string name = "GameObject")`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L93) | Creates a game object with a mandatory transform component.
`name`: Initial game object name. | The created game object. | — | +| [`public Inno.Scene.GameObject Inno.Scene.GameScene.InstantiatePrefab(Inno.Scene.PrefabAsset prefab, Inno.Scene.Components.Transform? parent = null)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L114) | Creates a connected instance of an imported prefab in this loaded scene.
`prefab`: The prefab asset that supplies the object subtree.
`parent`: Optional parent for the new instance. | The newly instantiated prefab root. | `System.ArgumentNullException`: The prefab is null.
`System.InvalidOperationException`: The scene is not loaded in its owning world or prefab services are unavailable. | +| [`public bool Inno.Scene.GameScene.DestroyObject(Inno.Scene.GameObject gameObject)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L130) | Recursively destroys a game object and its complete child subtree.
`gameObject`: Root object to destroy. | when the object belonged to this scene and was destroyed. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.GetObjects()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L185) | Gets all committed live game objects in scene storage order. | A stable object snapshot. | — | +| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObject(string name)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L200) | Finds the first game object with an ordinally matching name.
`name`: Name to find. | The first match, or when no object matches. | — | +| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObjectWithTag(string tag)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L219) | Finds the first game object with an ordinally matching tag in scene storage order.
`tag`: Tag to find. | The first match, or when no object matches. | `System.ArgumentException`: Thrown when is empty or contains only white-space characters. | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithTag(string tag)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L237) | Finds every game object with an ordinally matching tag in scene storage order.
`tag`: Tag to find. | A stable snapshot containing every matching game object. | `System.ArgumentException`: Thrown when is empty or contains only white-space characters. | +| [`public Inno.Scene.GameObject? Inno.Scene.GameScene.FindObjectWithLayer(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L252) | Finds the first game object assigned to a layer in scene storage order.
`layer`: The layer slot to find. | The first matching game object, or when no object matches. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithLayer(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L267) | Finds every game object assigned to one layer in scene storage order.
`layer`: The layer slot to find. | A stable snapshot containing every matching game object. | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.FindObjectsWithLayers(Inno.Scene.Layers.GameLayerMask layers)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L282) | Finds every game object assigned to any layer contained in a mask.
`layers`: The set of accepted layer slots. | A stable snapshot in scene storage order. | — | +| [`public TSystem Inno.Scene.GameScene.AddSystem() where TSystem : Inno.Scene.GameSystem, new()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L297) | Creates and registers a parameterless game system.
`TSystem`: Concrete system type. | The registered system. | — | +| [`public Inno.Scene.GameSystem Inno.Scene.GameScene.AddSystem(System.Type systemType)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L312) | Creates and registers a concrete game system by runtime type.
`systemType`: The system type consumed by add system; ownership remains with the caller unless explicitly stated otherwise. | The validated game system that represents the completed operation. | — | +| [`public void Inno.Scene.GameScene.AddSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L330) | Registers a game system instance.
`system`: System to register. | — | — | +| [`public bool Inno.Scene.GameScene.RemoveSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L345) | Removes a registered game system.
`system`: System to remove. | when the system was registered. | — | +| [`public void Inno.Scene.GameScene.ResetSystem(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L358) | Explicitly restores a registered system to its default state.
`system`: The system consumed by reset system; ownership remains with the caller unless explicitly stated otherwise. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameScene.GetSystems()`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L372) | Gets registered systems in display and serialization order. Explicit values independently control lifecycle execution priority. | An immutable snapshot of the values selected by the operation. | — | +| [`public int Inno.Scene.GameScene.GetSystemIndex(Inno.Scene.GameSystem system)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L387) | Gets the display index of a registered system.
`system`: Registered system to locate. | The zero-based display index. | — | +| [`public void Inno.Scene.GameScene.SetSystemIndex(Inno.Scene.GameSystem system, int systemIndex)`](../../src/content/scene/Inno.Scene/Core/GameScene.cs#L404) | Moves a registered system to a requested display and serialization index. This operation does not change its explicit .
`system`: Registered system to move.
`systemIndex`: Requested zero-based display index. | — | — | +| [`public abstract class Inno.Scene.GameSystem : Inno.Scene.EngineObject, Inno.Core.Serialization.ISerializable, Inno.Scene.ISceneLifecycleObject`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L13) | Base type for serializable, ordered scene-level behaviors. | — | — | +| [`public bool Inno.Scene.GameSystem.enabled { get; set; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L22) | Gets or sets whether this system participates in scene lifecycle updates. | — | — | +| [`public bool Inno.Scene.GameSystem.isActiveAndEnabled { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L39) | Gets whether this system is registered, enabled, and dispatchable. | — | — | +| [`public virtual int Inno.Scene.GameSystem.order { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L44) | Gets the ascending execution order used by the owning scene. | — | — | +| [`protected Inno.Scene.GameScene Inno.Scene.GameSystem.scene { get; }`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L49) | Gets the owning scene after this system has been registered. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.Reset()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L56) | Restores this system to defaults when added or explicitly reset. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.Awake()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L63) | Called once before the system first becomes active. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.Start()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L70) | Called once immediately before the first update. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnEnable()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L77) | Called when the system becomes active and enabled. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnDisable()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L84) | Called when the system stops being active and enabled. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnDestroy()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L91) | Called before a system that entered runtime lifecycle is destroyed. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnFixedUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L98) | Called during the fixed-rate scene stage. Use Time.fixedDeltaTime for step timing. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L105) | Called during the variable-rate scene stage. Use Time.deltaTime for frame timing. | — | — | +| [`protected virtual void Inno.Scene.GameSystem.OnLateUpdate()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L112) | Called during the late scene stage. Use Time.deltaTime for frame timing. | — | — | +| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.GetComponents() where TComponent : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L125) | Gets all scene components assignable to a requested type.
`TComponent`: The component base or concrete type included in the result. | An immutable snapshot of the values selected by the operation. | — | +| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.GetObjects()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L133) | Gets all committed live game objects in deterministic scene storage order. | The immutable structure snapshot retained by the scene until its object or component structure changes. | — | +| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L144) | Queries game objects containing one required component type.
`T1`: The first component type required on every returned game object. | An immutable snapshot of the values selected by the operation. | — | +| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent where T2 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L158) | Queries game objects containing two required component types.
`T1`: The first component type required on every returned game object.
`T2`: The second component type required on every returned game object. | An immutable snapshot of the values selected by the operation. | — | +| [`protected System.Collections.Generic.IReadOnlyList Inno.Scene.GameSystem.Query() where T1 : Inno.Scene.GameComponent where T2 : Inno.Scene.GameComponent where T3 : Inno.Scene.GameComponent`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L178) | Queries game objects containing three required component types.
`T1`: The first component type required on every returned game object.
`T2`: The second component type required on every returned game object.
`T3`: The third component type required on every returned game object. | An immutable snapshot of the values selected by the operation. | — | +| [`protected Inno.Scene.GameSystem.GameSystem()`](../../src/content/scene/Inno.Scene/Core/GameSystem.cs#L13) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scene.GameTagCatalog : Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L16) | Stores the project-wide tag definitions used to author and validate scene object assignments. | — | — | +| [`public const string Inno.Scene.GameTagCatalog.settingProtocolId = "inno.scene.tags"`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L21) | Gets the immutable project-setting protocol value used by discovery metadata. | — | — | +| [`public static Inno.Core.Settings.ProjectSettingId Inno.Scene.GameTagCatalog.settingId { get; }`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L29) | Gets the stable project setting protocol for the project-wide tag catalog. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.GameTagCatalog.GetTags()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L37) | Gets the defined tags with the immutable default tag first. | An independently owned deterministic tag snapshot. | — | +| [`public Inno.Core.Settings.ProjectLocalId Inno.Scene.GameTagCatalog.GetLocalId(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L52) | Gets the stable project-independent identity of a defined tag.
`tag`: The configured tag name. | The deterministic local identity. | — | +| [`public Inno.Core.Settings.ProjectScopedId Inno.Scene.GameTagCatalog.GetId(Inno.Core.Settings.ProjectId projectId, string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L72) | Gets the complete project-scoped identity of a defined tag.
`projectId`: The current project namespace.
`tag`: The configured tag name. | The canonical projectId.name identity. | — | +| [`public bool Inno.Scene.GameTagCatalog.IsDefined(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L89) | Determines whether an ordinal tag is defined by the project.
`tag`: The tag to resolve. | when the normalized tag is defined. | `System.ArgumentException`: Thrown when is empty or invalid. | +| [`public bool Inno.Scene.GameTagCatalog.Add(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L108) | Adds one normalized project tag definition.
`tag`: The unique tag to define. | when a definition was added. | `System.ArgumentException`: Thrown when is empty or invalid. | +| [`public bool Inno.Scene.GameTagCatalog.Remove(string tag)`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L134) | Removes one custom project tag definition without rewriting scene assignments.
`tag`: The custom tag to remove. | when an existing definition was removed. | `System.ArgumentException`: Thrown when is empty or invalid. | +| [`public Inno.Scene.GameTagCatalog Inno.Scene.GameTagCatalog.Clone()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L155) | Creates a detached mutable copy of this catalog. | An independently owned catalog with the same definitions. | — | +| [`public Inno.Scene.GameTagCatalog.GameTagCatalog()`](../../src/content/scene/Inno.Scene/Core/GameTagCatalog.cs#L16) | Inherited or compiler-provided contract. | — | — | +| [`public readonly struct Inno.Scene.Layers.GameLayer : System.IEquatable, System.IComparable`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L8) | Identifies one of the thirty-two runtime layers available to scene objects. | — | — | +| [`public const int Inno.Scene.Layers.GameLayer.C_MAX_COUNT = 32`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L13) | Defines the number of layer slots supported by the runtime bit-mask representation. | — | — | +| [`public static Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayer.defaultLayer { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L18) | Gets the built-in default layer stored in slot zero. | — | — | +| [`public Inno.Scene.Layers.GameLayer.GameLayer(int index)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L29) | Creates a layer identifier from a zero-based layer index.
`index`: The layer index in the inclusive range from zero through thirty-one. | — | `System.ArgumentOutOfRangeException`: Thrown when is outside the supported range. | +| [`public int Inno.Scene.Layers.GameLayer.index { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L39) | Gets the zero-based layer index. | — | — | +| [`public int Inno.Scene.Layers.GameLayer.CompareTo(Inno.Scene.Layers.GameLayer other)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L50) | Compares this layer with another layer by index.
`other`: The value to compare with this instance. | A signed value describing the relative index ordering. | — | +| [`public bool Inno.Scene.Layers.GameLayer.Equals(Inno.Scene.Layers.GameLayer other)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L61) | Determines whether another layer identifies the same index.
`other`: The layer to compare with this value. | when both layers identify the same index. | — | +| [`public override bool Inno.Scene.Layers.GameLayer.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L72) | Determines whether this instance and the supplied value represent the same logical state.
`obj`: The object to compare with this instance. | when both values represent the same logical state; otherwise, . | — | +| [`public override int Inno.Scene.Layers.GameLayer.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L80) | Computes a hash code from the fields that participate in logical equality. | A hash code consistent with the implemented equality contract. | — | +| [`public override string Inno.Scene.Layers.GameLayer.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L88) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static bool Inno.Scene.Layers.GameLayer.operator ==(Inno.Scene.Layers.GameLayer left, Inno.Scene.Layers.GameLayer right)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L102) | Determines whether two layer identifiers contain the same index.
`left`: The first layer identifier.
`right`: The second layer identifier. | when the identifiers are equal. | — | +| [`public static bool Inno.Scene.Layers.GameLayer.operator !=(Inno.Scene.Layers.GameLayer left, Inno.Scene.Layers.GameLayer right)`](../../src/content/scene/Inno.Scene/Layers/GameLayer.cs#L119) | Determines whether two layer identifiers contain different indices.
`left`: The first layer identifier.
`right`: The second layer identifier. | when the identifiers are different. | — | +| [`public sealed class Inno.Scene.Layers.GameLayerCatalog : Inno.Core.Serialization.ISerializable`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L18) | Stores the named layer catalog and symmetric layer-interaction matrix used by a project. | — | — | +| [`public const string Inno.Scene.Layers.GameLayerCatalog.settingProtocolId = "inno.scene.layers"`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L23) | Gets the immutable project-setting protocol value used by discovery metadata. | — | — | +| [`public static Inno.Core.Settings.ProjectSettingId Inno.Scene.Layers.GameLayerCatalog.settingId { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L39) | Gets the stable project setting protocol for the project-wide layer catalog. | — | — | +| [`public Inno.Scene.Layers.GameLayerCatalog.GameLayerCatalog()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L44) | Creates a layer stack containing the immutable default layer. | — | — | +| [`public int Inno.Scene.Layers.GameLayerCatalog.count { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L56) | Gets the number of currently named layer slots. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scene.Layers.GameLayerCatalog.GetDefinitions()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L71) | Gets an immutable snapshot of every named layer ordered by slot index. | The ordered layer-definition snapshot. | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.IsDefined(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L97) | Determines whether a layer slot is defined.
`layer`: The layer slot to test. | when the slot is defined. | — | +| [`public string? Inno.Scene.Layers.GameLayerCatalog.GetName(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L112) | Gets the display name assigned to a layer slot.
`layer`: The layer slot to resolve. | The configured name, or . | — | +| [`public Inno.Core.Settings.ProjectLocalId? Inno.Scene.Layers.GameLayerCatalog.GetLocalId(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L127) | Gets the stable project-independent identity assigned to a layer slot.
`layer`: The layer slot to resolve. | The local identity, or . | — | +| [`public Inno.Scene.Layers.GameLayerId? Inno.Scene.Layers.GameLayerCatalog.GetId(Inno.Core.Settings.ProjectId projectId, Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L146) | Gets the complete identity assigned to a layer slot.
`projectId`: The current project namespace.
`layer`: The layer slot to resolve. | The qualified identity, or . | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(Inno.Core.Settings.ProjectLocalId localId, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L166) | Tries to resolve a local identity to its compact runtime slot.
`localId`: The stable project-local identity.
`layer`: The resolved runtime slot. | when the identity is defined. | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(Inno.Core.Settings.ProjectId projectId, Inno.Scene.Layers.GameLayerId id, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L199) | Tries to resolve a qualified identity to its compact runtime slot.
`projectId`: The current project namespace.
`id`: The complete layer identity.
`layer`: The resolved runtime slot. | when the identity belongs to this project and is defined. | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.TryGetLayer(string name, out Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L233) | Tries to resolve an ordinal layer name to its compact slot.
`name`: The layer name to find.
`layer`: The resolved layer. | when a matching layer exists. | — | +| [`public Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayerCatalog.GetLayer(string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L259) | Resolves an ordinal layer name to its compact slot.
`name`: The configured layer name. | The matching runtime layer. | — | +| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerCatalog.GetMask(System.Collections.Generic.IEnumerable names)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L275) | Creates a mask from configured ordinal layer names.
`names`: The configured names to include. | A mask containing every resolved layer. | — | +| [`public void Inno.Scene.Layers.GameLayerCatalog.Define(Inno.Scene.Layers.GameLayer layer, string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L293) | Defines or renames a project layer without accepting an authored ID.
`layer`: The compact runtime slot.
`name`: The unique display name. | — | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.Remove(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L347) | Removes a custom layer definition while retaining its numeric slot and interactions.
`layer`: The custom layer slot to undefine. | when a definition was removed. | — | +| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerCatalog.GetInteractionMask(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L368) | Gets the interaction mask assigned to one layer.
`layer`: The source layer. | The configured interaction mask. | — | +| [`public bool Inno.Scene.Layers.GameLayerCatalog.CanInteract(Inno.Scene.Layers.GameLayer first, Inno.Scene.Layers.GameLayer second)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L386) | Determines whether two layer slots may interact.
`first`: The first layer.
`second`: The second layer. | when interaction is enabled. | — | +| [`public void Inno.Scene.Layers.GameLayerCatalog.SetInteraction(Inno.Scene.Layers.GameLayer first, Inno.Scene.Layers.GameLayer second, bool canInteract)`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L406) | Sets a symmetric interaction pair.
`first`: The first layer.
`second`: The second layer.
`canInteract`: Whether interaction is enabled. | — | — | +| [`public Inno.Scene.Layers.GameLayerCatalog Inno.Scene.Layers.GameLayerCatalog.Clone()`](../../src/content/scene/Inno.Scene/Layers/GameLayerCatalog.cs#L432) | Creates a detached copy of this stack. | An independent layer stack. | — | +| [`public sealed class Inno.Scene.Layers.GameLayerDefinition`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L10) | Describes one named layer slot in a snapshot. | — | — | +| [`public Inno.Scene.Layers.GameLayerDefinition.GameLayerDefinition(Inno.Core.Settings.ProjectLocalId localId, Inno.Scene.Layers.GameLayer layer, string name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L24) | Creates an immutable layer definition.
`localId`: The stable project-independent identity.
`layer`: The layer slot represented by the definition.
`name`: The non-empty display and lookup name. | — | — | +| [`public Inno.Core.Settings.ProjectLocalId Inno.Scene.Layers.GameLayerDefinition.localId { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L39) | Gets the stable project-independent identity. | — | — | +| [`public Inno.Scene.Layers.GameLayer Inno.Scene.Layers.GameLayerDefinition.layer { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L44) | Gets the layer slot represented by this definition. | — | — | +| [`public string Inno.Scene.Layers.GameLayerDefinition.name { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L49) | Gets the unique ordinal layer name. | — | — | +| [`public Inno.Scene.Layers.GameLayerId Inno.Scene.Layers.GameLayerDefinition.GetId(Inno.Core.Settings.ProjectId projectId)`](../../src/content/scene/Inno.Scene/Layers/GameLayerDefinition.cs#L60) | Resolves the complete identity under a project namespace.
`projectId`: The current project namespace. | The canonical project-scoped layer identity. | — | +| [`public readonly record struct Inno.Scene.Layers.GameLayerId : System.IEquatable`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Identifies one project-scoped game layer independently from its compact runtime slot. | — | — | +| [`public Inno.Scene.Layers.GameLayerId.GameLayerId(Inno.Core.Settings.ProjectScopedId value)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L18) | Creates a project-scoped layer identity.
`value`: The canonical qualified identity. | — | — | +| [`public Inno.Scene.Layers.GameLayerId.GameLayerId(Inno.Core.Settings.ProjectId projectId, Inno.Core.Settings.ProjectLocalId name)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L34) | Creates a project-scoped layer identity from its project and local parts.
`projectId`: The current project identity.
`name`: The stable project-local layer identity. | — | — | +| [`public string Inno.Scene.Layers.GameLayerId.value { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L63) | Gets the canonical projectId.name identity string. | — | — | +| [`public bool Inno.Scene.Layers.GameLayerId.isValid { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L68) | Gets whether this value contains a usable identity. | — | — | +| [`public override string Inno.Scene.Layers.GameLayerId.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L76) | Formats this value as a human-readable representation. | The qualified identity. | — | +| [`public static bool Inno.Scene.Layers.GameLayerId.operator !=(Inno.Scene.Layers.GameLayerId left, Inno.Scene.Layers.GameLayerId right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Scene.Layers.GameLayerId.operator ==(Inno.Scene.Layers.GameLayerId left, Inno.Scene.Layers.GameLayerId right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Scene.Layers.GameLayerId.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Scene.Layers.GameLayerId.Equals(object obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Scene.Layers.GameLayerId.Equals(Inno.Scene.Layers.GameLayerId other)`](../../src/content/scene/Inno.Scene/Layers/GameLayerId.cs#L10) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public readonly struct Inno.Scene.Layers.GameLayerMask : System.IEquatable`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L9) | Stores a compact set of scene layers for rendering, physics, and query filtering. | — | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.none { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L14) | Gets a mask containing no layers. | — | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.everything { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L19) | Gets a mask containing every supported layer. | — | — | +| [`public Inno.Scene.Layers.GameLayerMask.GameLayerMask(uint value)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L27) | Creates a mask from its raw thirty-two-bit representation.
`value`: The raw mask bits. | — | — | +| [`public uint Inno.Scene.Layers.GameLayerMask.value { get; }`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L35) | Gets the raw thirty-two-bit mask value. | — | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.FromLayers(System.Collections.Generic.IEnumerable layers)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L49) | Creates a mask containing the supplied layers.
`layers`: The layers whose bits should be enabled. | A mask containing every supplied layer. | `System.ArgumentNullException`: Thrown when is . | +| [`public bool Inno.Scene.Layers.GameLayerMask.Contains(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L67) | Determines whether the supplied layer is contained in this mask.
`layer`: The layer consumed by contains; ownership remains with the caller unless explicitly stated otherwise. | when the layer bit is enabled. | — | +| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.With(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L78) | Returns a mask with the supplied layer enabled.
`layer`: The layer consumed by with; ownership remains with the caller unless explicitly stated otherwise. | The updated immutable mask. | — | +| [`public Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.Without(Inno.Scene.Layers.GameLayer layer)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L89) | Returns a mask with the supplied layer disabled.
`layer`: The layer consumed by without; ownership remains with the caller unless explicitly stated otherwise. | The updated immutable mask. | — | +| [`public bool Inno.Scene.Layers.GameLayerMask.Equals(Inno.Scene.Layers.GameLayerMask other)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L100) | Determines whether another mask contains the same layer bits.
`other`: The mask to compare with this value. | when both masks contain the same bits. | — | +| [`public override bool Inno.Scene.Layers.GameLayerMask.Equals(object? obj)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L111) | Determines whether this instance and the supplied value represent the same logical state.
`obj`: The object to compare with this instance. | when both values represent the same logical state; otherwise, . | — | +| [`public override int Inno.Scene.Layers.GameLayerMask.GetHashCode()`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L119) | Computes a hash code from the fields that participate in logical equality. | A hash code consistent with the implemented equality contract. | — | +| [`public override string Inno.Scene.Layers.GameLayerMask.ToString()`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L127) | Formats this value as a human-readable representation. | The human-readable representation of this value. | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator \|(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L141) | Combines the enabled bits from two masks.
`left`: The first mask.
`right`: The second mask. | The bitwise union of both masks. | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator &(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L158) | Retains only layer bits enabled in both masks.
`left`: The first mask.
`right`: The second mask. | The bitwise intersection of both masks. | — | +| [`public static Inno.Scene.Layers.GameLayerMask Inno.Scene.Layers.GameLayerMask.operator ~(Inno.Scene.Layers.GameLayerMask mask)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L172) | Inverts every layer bit in a mask.
`mask`: The mask to invert. | The complement of the supplied mask. | — | +| [`public static bool Inno.Scene.Layers.GameLayerMask.operator ==(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L186) | Determines whether two masks contain the same bits.
`left`: The first mask.
`right`: The second mask. | when both masks are equal. | — | +| [`public static bool Inno.Scene.Layers.GameLayerMask.operator !=(Inno.Scene.Layers.GameLayerMask left, Inno.Scene.Layers.GameLayerMask right)`](../../src/content/scene/Inno.Scene/Layers/GameLayerMask.cs#L203) | Determines whether two masks contain different bits.
`left`: The first mask.
`right`: The second mask. | when both masks differ. | — | +| [`public sealed class Inno.Scene.PrefabInstanceInfo`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L10) | Describes the read-only source connection retained by one prefab instance object. | — | — | +| [`public System.Guid Inno.Scene.PrefabInstanceInfo.sourceAssetId { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L37) | Gets the persistent identity of the source prefab asset. | — | — | +| [`public System.Guid Inno.Scene.PrefabInstanceInfo.sourceObjectId { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L42) | Gets the source-local identity represented by this object. | — | — | +| [`public Inno.Scene.GameObject Inno.Scene.PrefabInstanceInfo.instanceRoot { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L47) | Gets the root object of this prefab instance connection. | — | — | +| [`public bool Inno.Scene.PrefabInstanceInfo.isRoot { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L52) | Gets whether this object is the instance connection root. | — | — | +| [`public bool Inno.Scene.PrefabInstanceInfo.isVariant { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L57) | Gets whether the connection originates from a prefab variant. | — | — | +| [`public bool Inno.Scene.PrefabInstanceInfo.isMissing { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L62) | Gets whether the source prefab was unavailable during restoration. | — | — | +| [`public int Inno.Scene.PrefabInstanceInfo.overrideCount { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L67) | Gets the number of retained overrides for this instance connection. | — | — | +| [`public int Inno.Scene.PrefabInstanceInfo.orphanedOverrideCount { get; }`](../../src/content/scene/Inno.Scene/Prefabs/PrefabInstanceInfo.cs#L72) | Gets the number of retained overrides that no longer match the current source. | — | — | +| [`public interface Inno.Scene.ISceneReloadStateTransfer`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L9) | Represents a staged scene object state transfer associated with one assembly reload transaction. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.retiredObjects { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L14) | Gets old scene objects whose runtime types belong to the retiring assembly generation. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.diagnostics { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L19) | Gets non-fatal state transfer decisions produced while replacing reloadable scene objects. | — | — | +| [`System.Collections.Generic.IReadOnlyList Inno.Scene.ISceneReloadStateTransfer.recoveryChanges { get; }`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L24) | Gets the shared candidate resolutions for actual scene element and asset dependency slots after application. | — | — | +| [`void Inno.Scene.ISceneReloadStateTransfer.PrepareForActivation()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L29) | Disables active retiring lifecycle objects before the new assembly generation becomes active. | — | — | +| [`void Inno.Scene.ISceneReloadStateTransfer.Apply()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L34) | Creates replacement instances and restores their serialized state. | — | — | +| [`void Inno.Scene.ISceneReloadStateTransfer.RollbackStructure()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L39) | Restores the previous scene structure after a failed state transfer. | — | — | +| [`void Inno.Scene.ISceneReloadStateTransfer.RestorePreviousState()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L44) | Restores lifecycle state on the previous scene instances after rollback. | — | — | +| [`void Inno.Scene.ISceneReloadStateTransfer.Complete()`](../../src/content/scene/Inno.Scene/Reloading/ISceneReloadStateTransfer.cs#L49) | Finalizes replacement instances after the assembly reload is committed. | — | — | +| [`public sealed class Inno.Scene.SceneReloadDiagnostic`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L9) | Describes a non-fatal issue encountered while migrating scene state to a new assembly generation. | — | — | +| [`public string Inno.Scene.SceneReloadDiagnostic.code { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L34) | Gets the stable diagnostic code. | — | — | +| [`public Inno.Core.Diagnostics.DiagnosticSeverity Inno.Scene.SceneReloadDiagnostic.severity { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L39) | Gets the diagnostic severity. | — | — | +| [`public string Inno.Scene.SceneReloadDiagnostic.message { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L44) | Gets the human-readable diagnostic message. | — | — | +| [`public System.Guid Inno.Scene.SceneReloadDiagnostic.scenePersistentId { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L49) | Gets the persistent identity of the affected scene. | — | — | +| [`public System.Guid Inno.Scene.SceneReloadDiagnostic.objectPersistentId { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L54) | Gets the persistent identity of the affected component or system. | — | — | +| [`public string Inno.Scene.SceneReloadDiagnostic.propertyName { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L59) | Gets the incompatible serialized property name. | — | — | +| [`public string Inno.Scene.SceneReloadDiagnostic.previousPropertyType { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L64) | Gets the previous declared property type name. | — | — | +| [`public string Inno.Scene.SceneReloadDiagnostic.currentPropertyType { get; }`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadDiagnostic.cs#L69) | Gets the current declared property type name. | — | — | +| [`public sealed class Inno.Scene.SceneReloadService`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L12) | Creates scene state-transfer transactions for assembly generation changes. | — | — | +| [`public Inno.Scene.SceneReloadService.SceneReloadService(Inno.Scene.SceneWorld world, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L34) | Creates a scene reload service bound to one serialization generation owner.
`serialization`: The serialization registry used to capture and restore scene state.
`world`: The isolated scene world whose active generation is captured.
`assets`: The asset reference generation used throughout capture, activation, and rollback. | — | `System.ArgumentNullException`: Thrown when , , or is null. | +| [`public Inno.Scene.ISceneReloadStateTransfer Inno.Scene.SceneReloadService.Capture(Inno.Extensibility.Types.TypeCacheReloadContext context)`](../../src/content/scene/Inno.Scene/Reloading/SceneReloadService.cs#L53) | Captures all loaded scene objects affected by a prepared type-cache reload.
`context`: The prepared type-cache reload context. | A staged state transfer that has not modified live objects. | — | +| [`public static class Inno.Scene.SceneManager`](../../src/content/scene/Inno.Scene/SceneManager.cs#L13) | Provides Unity-style scene operations by resolving the world in the current runtime execution context. | — | — | +| [`public static Inno.Scene.GameScene? Inno.Scene.SceneManager.activeScene { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L18) | Gets the active scene in the current runtime session. | — | — | +| [`public static bool Inno.Scene.SceneManager.hasActiveScene { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L23) | Gets whether the current runtime session has an active scene. | — | — | +| [`public static System.Collections.Generic.IReadOnlyList Inno.Scene.SceneManager.loadedScenes { get; }`](../../src/content/scene/Inno.Scene/SceneManager.cs#L28) | Gets an immutable snapshot of scenes loaded by the current runtime session. | — | — | +| [`public static int Inno.Scene.SceneManager.GetSceneIndex(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L39) | Gets the hierarchy index of a scene loaded by the current runtime session.
`scene`: The loaded scene to locate. | The zero-based hierarchy index. | — | +| [`public static void Inno.Scene.SceneManager.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L50) | Moves a loaded scene to a hierarchy index without changing the active scene.
`scene`: The loaded scene to move.
`sceneIndex`: The requested zero-based hierarchy index. | — | — | +| [`public static void Inno.Scene.SceneManager.LoadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L61) | Replaces the current session scene set with one active scene.
`scene`: The scene to load. | — | — | +| [`public static void Inno.Scene.SceneManager.LoadSceneAdditive(Inno.Scene.GameScene scene, bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L72) | Loads a scene alongside the current session scene set.
`scene`: The scene to load additively.
`makeActive`: Whether the loaded scene becomes active. | — | — | +| [`public static Inno.Scene.GameScene Inno.Scene.SceneManager.LoadNewScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/SceneManager.cs#L87) | Creates and loads a new active scene in the current runtime session.
`name`: The initial scene display name. | The newly created and loaded scene. | — | +| [`public static Inno.Scene.GameScene Inno.Scene.SceneManager.LoadNewSceneAdditive(string name = "Untitled Scene", bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L101) | Creates and additively loads a new scene in the current runtime session.
`name`: The initial scene display name.
`makeActive`: Whether the new scene becomes active. | The newly created and loaded scene. | — | +| [`public static void Inno.Scene.SceneManager.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L113) | Makes a loaded scene active in the current runtime session.
`scene`: The loaded scene to activate. | — | — | +| [`public static void Inno.Scene.SceneManager.MoveGameObjectToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L124) | Moves a live object subtree into another scene owned by the current runtime session.
`gameObject`: The live root object to move.
`destination`: The loaded destination scene. | — | — | +| [`public static void Inno.Scene.SceneManager.UnloadActiveScene()`](../../src/content/scene/Inno.Scene/SceneManager.cs#L133) | Unloads the active scene in the current runtime session when one exists. | — | — | +| [`public static bool Inno.Scene.SceneManager.UnloadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L144) | Unloads one scene from the current runtime session.
`scene`: The scene to unload. | when the scene was loaded; otherwise, . | — | +| [`public static void Inno.Scene.SceneManager.UnloadAllScenes()`](../../src/content/scene/Inno.Scene/SceneManager.cs#L149) | Unloads every scene from the current runtime session. | — | — | +| [`public static void Inno.Scene.SceneManager.FixedUpdate(float fixedDeltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L157) | Advances fixed-step scene lifecycle callbacks in the current runtime session.
`fixedDeltaTime`: The fixed simulation interval in seconds. | — | — | +| [`public static void Inno.Scene.SceneManager.Update(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L165) | Advances variable-step scene lifecycle callbacks in the current runtime session.
`deltaTime`: The elapsed frame time in seconds. | — | — | +| [`public static void Inno.Scene.SceneManager.LateUpdate(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneManager.cs#L173) | Advances late scene lifecycle callbacks in the current runtime session.
`deltaTime`: The elapsed frame time in seconds. | — | — | +| [`public sealed class Inno.Scene.SceneWorld : System.IDisposable`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L16) | Owns the loaded scene set and lifecycle state for one isolated runtime session. | — | — | +| [`public Inno.Scene.SceneWorld.SceneWorld(Inno.Core.Identity.IdentityAllocator identities, Inno.Extensibility.Types.TypeCatalog types)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L41) | Creates an empty scene world bound to one session identity allocator.
`identities`: The allocator that owns identities for scenes and their elements.
`types`: The host type catalog used to derive scene component and system generations. | — | `System.ArgumentNullException`: Thrown when is null. | +| [`public void Inno.Scene.SceneWorld.ConfigurePrefabInstantiation(Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L73) | Binds the session-owned serialization and asset generations used by scene prefab instances.
`serialization`: The active serialization registry for this session.
`assets`: The asset resolver that owns prefab dependencies. | — | `System.InvalidOperationException`: The binding was already configured. | | [`public Inno.Scene.GameScene? Inno.Scene.SceneWorld.activeScene { get; }`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L107) | Gets the scene currently selected for unqualified scene operations. | — | — | | [`public bool Inno.Scene.SceneWorld.hasActiveScene { get; }`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L112) | Gets whether this world contains an active scene. | — | — | | [`public System.Collections.Generic.IReadOnlyList Inno.Scene.SceneWorld.loadedScenes { get; }`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L117) | Gets an immutable snapshot of loaded scenes in hierarchy order. | — | — | -| [`public sealed class Inno.Scene.SceneWorld : System.IDisposable`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L16) | Owns the loaded scene set and lifecycle state for one isolated runtime session. | — | — | +| [`public TObject? Inno.Scene.SceneWorld.Find(System.Guid persistentId) where TObject : Inno.Core.Identity.IdentityObject`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L131) | Resolves a live scene object by its persistent identity within this world.
`persistentId`: The persistent identity to resolve.
`TObject`: The required scene object contract. | The live compatible object, or when this world does not own one. | — | +| [`public System.IDisposable Inno.Scene.SceneWorld.EnterScope()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L144) | Binds this world and its identity allocator to the current asynchronous execution context. | A strict last-in-first-out execution scope owned by the caller. | `System.ObjectDisposedException`: Thrown when this world has been disposed. | +| [`public int Inno.Scene.SceneWorld.GetSceneIndex(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L163) | Gets the hierarchy index of a loaded scene.
`scene`: The loaded scene to locate. | The zero-based hierarchy index. | `System.InvalidOperationException`: Thrown when the scene is not loaded by this world. | +| [`public void Inno.Scene.SceneWorld.SetSceneIndex(Inno.Scene.GameScene scene, int sceneIndex)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L185) | Moves a loaded scene to a hierarchy index without changing the active scene.
`scene`: The loaded scene to move.
`sceneIndex`: The requested zero-based hierarchy index. | — | `System.InvalidOperationException`: Thrown when the scene is not loaded by this world. | +| [`public void Inno.Scene.SceneWorld.LoadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L208) | Replaces the loaded set with one scene and makes it active.
`scene`: The scene to load. | — | — | +| [`public void Inno.Scene.SceneWorld.LoadSceneAdditive(Inno.Scene.GameScene scene, bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L230) | Loads a scene alongside the existing scene set.
`scene`: The scene to load additively.
`makeActive`: Whether the loaded scene becomes active. | — | — | +| [`public Inno.Scene.GameScene Inno.Scene.SceneWorld.LoadNewScene(string name = "Untitled Scene")`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L255) | Creates and loads a new active scene.
`name`: The initial scene display name. | The newly created and loaded scene. | — | +| [`public Inno.Scene.GameScene Inno.Scene.SceneWorld.LoadNewSceneAdditive(string name = "Untitled Scene", bool makeActive = true)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L275) | Creates and additively loads a new scene.
`name`: The initial scene display name.
`makeActive`: Whether the new scene becomes active. | The newly created and loaded scene. | — | +| [`public void Inno.Scene.SceneWorld.SetActiveScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L294) | Makes one loaded scene active without changing the loaded set.
`scene`: The loaded scene to activate. | — | `System.InvalidOperationException`: Thrown when the scene is not loaded by this world. | +| [`public void Inno.Scene.SceneWorld.MoveGameObjectToScene(Inno.Scene.GameObject gameObject, Inno.Scene.GameScene destination)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L315) | Moves a live object subtree between two scenes loaded by this world.
`gameObject`: The live root object to move.
`destination`: The loaded destination scene. | — | `System.InvalidOperationException`: Thrown when either scene is not loaded or the object is not live. | +| [`public void Inno.Scene.SceneWorld.UnloadActiveScene()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L334) | Unloads the active scene when one exists. | — | — | +| [`public bool Inno.Scene.SceneWorld.UnloadScene(Inno.Scene.GameScene scene)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L350) | Unloads one scene and selects another loaded scene when necessary.
`scene`: The scene to unload. | when the scene was loaded by this world; otherwise, . | — | +| [`public void Inno.Scene.SceneWorld.UnloadAllScenes()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L372) | Unloads every scene while preserving the first lifecycle failure. | — | — | +| [`public void Inno.Scene.SceneWorld.FixedUpdate(float fixedDeltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L400) | Advances fixed-step lifecycle callbacks for every loaded scene.
`fixedDeltaTime`: The fixed simulation interval in seconds. | — | — | +| [`public void Inno.Scene.SceneWorld.Update(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L414) | Advances variable-step lifecycle callbacks for every loaded scene.
`deltaTime`: The elapsed frame time in seconds. | — | — | +| [`public void Inno.Scene.SceneWorld.LateUpdate(float deltaTime)`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L428) | Advances late lifecycle callbacks for every loaded scene.
`deltaTime`: The elapsed frame time in seconds. | — | — | +| [`public void Inno.Scene.SceneWorld.Dispose()`](../../src/content/scene/Inno.Scene/SceneWorld.cs#L439) | Unloads every scene and permanently releases this world. | — | — | +| [`public static class Inno.Scene.SceneElementSerialization`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L16) | Recreates individual scene components and systems from stable type and object identities. | — | — | +| [`public static byte[] Inno.Scene.SceneElementSerialization.CaptureState(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L39) | Captures one element's logical type, neutral properties, asset dependencies and scene-reference aliases.
`target`: The live component or system, including a missing placeholder.
`serialization`: The owner's current serializer generation.
`assets`: The owner's complete asset resolver used during capture. | Neutral element bytes suitable for History and restoration without retaining the current managed type. | `System.ArgumentException`: The target is not a component or system.
`System.InvalidOperationException`: The element is not live in a loaded scene. | +| [`public static void Inno.Scene.SceneElementSerialization.RestoreState(Inno.Scene.EngineObject target, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L101) | Restores a captured element state into the same logical type, including neutral state on a missing placeholder.
`target`: The attached element receiving the state.
`stateData`: Bytes produced by .
`serialization`: The owner's current serializer generation.
`assets`: The owner's complete asset resolver. | — | `System.IO.InvalidDataException`: The payload is invalid or represents a different logical type.
`System.InvalidOperationException`: Property or reference restoration is incomplete. | +| [`public static Inno.Scene.GameComponent Inno.Scene.SceneElementSerialization.RestoreComponent(Inno.Scene.GameObject owner, Inno.Extensibility.Types.TypeRef type, System.Guid persistentId, int componentIndex, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L166) | Recreates one component without invoking Reset and restores its persistent properties.
`owner`: The live GameObject that will own the component.
`type`: The reload-safe identity of the current component implementation.
`persistentId`: The component instance identity to preserve.
`componentIndex`: The requested attachment index.
`stateData`: Complete neutral element bytes produced by CaptureState, including missing-state metadata.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The resolver used to materialize canonical asset references in restored properties. | The recreated component, or a preserved missing placeholder when its logical type is unavailable. | `System.ArgumentNullException`: Thrown when is .
`System.IO.InvalidDataException`: Thrown when the payload is invalid or the stable type resolves to an incompatible component.
`System.InvalidOperationException`: Thrown when the identity or component multiplicity conflicts, property restoration is incomplete, or a failed restoration cannot remove its partially created component. | +| [`public static Inno.Scene.GameSystem Inno.Scene.SceneElementSerialization.RestoreSystem(Inno.Scene.GameScene scene, Inno.Extensibility.Types.TypeRef type, System.Guid persistentId, int systemIndex, System.ReadOnlySpan stateData, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneElementSerialization.cs#L239) | Recreates one scene system without invoking Reset and restores its persistent properties.
`scene`: The live loaded scene that will own the system.
`type`: The reload-safe identity of the current system implementation.
`persistentId`: The system instance identity to preserve.
`systemIndex`: The requested display and serialization index.
`stateData`: Complete neutral element bytes produced by CaptureState, including missing-state metadata.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The resolver used to materialize canonical asset references in restored properties. | The recreated system, or a preserved missing placeholder when its logical type is unavailable. | `System.ArgumentNullException`: Thrown when is .
`System.IO.InvalidDataException`: Thrown when the payload is invalid or the stable type resolves to an incompatible system.
`System.InvalidOperationException`: Thrown when the identity or system multiplicity conflicts, property restoration is incomplete, or a failed restoration cannot remove its partially created system. | +| [`public static class Inno.Scene.ScenePropertySerialization`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L14) | Captures and restores individual scene-object properties while preserving scene reference identities. | — | — | +| [`public static byte[] Inno.Scene.ScenePropertySerialization.CaptureProperty(Inno.Scene.EngineObject target, string propertyName, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L43) | Captures one persistent property without serializing the complete scene.
`target`: The live scene object containing the property.
`propertyName`: The exact serialized member key.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The resolver used to preserve canonical asset references in captured values. | Neutral bytes that can be restored into the same logical object identity. | `System.ArgumentNullException`: Thrown when is .
`System.ArgumentException`: Thrown when is empty or unknown.
`System.InvalidOperationException`: Thrown when the target is destroyed or not owned by a loaded scene. | +| [`public static byte[] Inno.Scene.ScenePropertySerialization.CaptureProperties(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L82) | Captures all persistent properties without serializing the complete scene.
`target`: The live scene object whose state should be captured.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The resolver used to preserve canonical asset references in captured values. | Neutral bytes containing independently encoded properties. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the target is destroyed or not owned by a loaded scene. | +| [`public static System.Collections.Generic.IReadOnlyList Inno.Scene.ScenePropertySerialization.CapturePropertySnapshots(Inno.Scene.EngineObject target, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L113) | Captures each persistent property independently while preserving scene-reference identities.
`target`: Live scene object whose properties should be captured.
`serialization`: Registry owning the active converter generation.
`assets`: Resolver used to preserve canonical asset references. | Stable ordered property snapshots suitable for compact delta comparison. | — | +| [`public static Inno.Core.Serialization.SerializationPropertyRestoreResult Inno.Scene.ScenePropertySerialization.RestoreProperties(Inno.Scene.EngineObject target, System.ReadOnlySpan data, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Core.Serialization.SerializationPropertyRestoreMode mode = Inno.Core.Serialization.SerializationPropertyRestoreMode.Strict)`](../../src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs#L156) | Restores independently captured properties into a live scene object.
`target`: The current object generation receiving the values.
`data`: Bytes produced by or .
`mode`: The compatibility policy applied to matching current properties.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The resolver used to materialize canonical asset references in restored values. | A summary of restored, ignored, and incompatible properties. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when strict restoration fails or the target is not in a loaded scene. | +| [`public static class Inno.Scene.SceneSubtreeSerialization`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L14) | Captures and restores one GameObject subtree with its persistent object and component identities. | — | — | +| [`public static byte[] Inno.Scene.SceneSubtreeSerialization.Capture(Inno.Scene.GameObject root, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets)`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L37) | Captures one live GameObject and all descendants without serializing unrelated scene objects.
`root`: The live subtree root.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The asset-reference resolver used to encode referenced asset identities. | Neutral bytes containing the subtree structure, components, properties, and identity references. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the root is destroyed or detached. | +| [`public static Inno.Scene.GameObject Inno.Scene.SceneSubtreeSerialization.Restore(Inno.Scene.GameScene scene, System.ReadOnlySpan data, Inno.Core.Serialization.SerializationRegistry serialization, Inno.Assets.IAssetReferenceResolver assets, Inno.Scene.Components.Transform? parent, int siblingIndex)`](../../src/content/scene/Inno.Scene/Serialization/SceneSubtreeSerialization.cs#L81) | Restores a previously captured subtree into a loaded scene with its original persistent identities.
`scene`: The loaded destination scene.
`data`: Bytes created by .
`parent`: The optional external parent restored after the subtree is created.
`siblingIndex`: The requested sibling index under or at the scene root.
`serialization`: The serialization registry that owns the active converter generation.
`assets`: The asset-reference resolver used to restore referenced asset identities. | The restored subtree root. | `System.ArgumentNullException`: Thrown when is .
`System.InvalidOperationException`: Thrown when the scene is unavailable or an identity conflicts. | +| [`public sealed class Inno.Scene.SceneTypeResolutionException : System.Exception`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L9) | Reports that a serialized scene element cannot be created because its stable type is not present in the active type catalog. | — | — | +| [`public Inno.Scene.SceneTypeResolutionException.SceneTypeResolutionException(System.Guid stableTypeId, string elementKind)`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L23) | Creates an exception for one unresolved serialized scene type.
`stableTypeId`: The stable identity stored by the serialized scene element.
`elementKind`: The human-readable scene element kind, such as component or system. | — | `System.ArgumentException`: Thrown when is empty or is empty. | +| [`public System.Guid Inno.Scene.SceneTypeResolutionException.stableTypeId { get; }`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L39) | Gets the unresolved stable type identity. | — | — | +| [`public string Inno.Scene.SceneTypeResolutionException.elementKind { get; }`](../../src/content/scene/Inno.Scene/Serialization/SceneTypeResolutionException.cs#L44) | Gets the scene element kind that expected the missing type. | — | — | ## 项目依赖 -- [Inno.Extensibility.Reload](../extensibility/Inno.Extensibility.Reload.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Execution](../core/Inno.Core.Execution.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Collections](../core/Inno.Core.Collections.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Scripting.Api](../scripting/Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Identity](../core/Inno.Core.Identity.md):公开引用边界由实际签名核对。 -- [Inno.Core.Mathematics](../core/Inno.Core.Mathematics.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Types](../extensibility/Inno.Extensibility.Types.md):公开引用边界由实际签名核对。 -- [Inno.Core.Serialization](../core/Inno.Core.Serialization.md):公开引用边界由实际签名核对。 -- [Inno.Core.Settings](../core/Inno.Core.Settings.md):公开引用边界由实际签名核对。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.References](../references/Inno.References.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Extensibility.Reload` | 实现依赖;不传递 compile surface | +| `Inno.Core.Execution` | 实现依赖;不传递 compile surface | +| `Inno.Core.Collections` | 实现依赖;不传递 compile surface | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Core.Identity` | 公开签名依赖 | +| `Inno.Core.Mathematics` | 公开签名依赖 | +| `Inno.Extensibility.Types` | 公开签名依赖 | +| `Inno.Core.Serialization` | 公开签名依赖 | +| `Inno.Core.Settings` | 公开签名依赖 | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.References` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/scripting/Inno.Scripting.Compiler.md b/docs/scripting/Inno.Scripting.Compiler.md index a071f7fac..dbc6fd1ab 100644 --- a/docs/scripting/Inno.Scripting.Compiler.md +++ b/docs/scripting/Inno.Scripting.Compiler.md @@ -1,5 +1,14 @@ # Inno.Scripting.Compiler +## SDK 输入与 IDE 一致性 + +`ScriptCompilerOptions.compilationEnvironment` 是 Host 必须提供的 `IScriptCompilationEnvironmentProvider`。一次编译或投影只捕获一次 `ScriptCompilationInputs`,其中保存选定 SDK 身份、实际 framework references 与 SDK 生成的预定义符号。集合复制后只读;编译器在进入 Roslyn 前读取并冻结引用内容,内容哈希参与产物身份。 + +编译器不选择已安装 reference pack 的最高版本,也不引用 Build、MSBuild 或平台工程。标准 Host 使用 Build 的 `SdkScriptCompilationEnvironment`,复用现有 SDK resolver、进程执行及 Core IO 临时 owner。求值失败或取消会明确失败,不发布脚本产物,不改写用户 SDK 配置。 + +IDE 投影显式引用同一组 framework assemblies,并使用同一组符号。SDK 的隐式 framework references 与隐式符号注入关闭;目标仍为正常的 `net10.0` SDK 工程。实际 IDE SDK 与捕获身份不一致时报告错误,要求在预期 SDK 选择下重新生成投影。 + + ## Authoring 标注与 Player 边界 编译器根据 `ScriptingApiScope.Authoring` 的导出清单,在语义绑定后、目标 Player references 绑定前移除纯创作标注使用、自定义派生标注类及相关 using。规则由清单与继承符号推导,不依赖标注的具体实现,也没有属性类型白名单。创作编译与 IDE 保留完整标注 API;Player IL 不保留纯创作标注引用。普通 `SerializableProperty` 不在此 scope,继续表达持久化契约。 @@ -87,41 +96,7 @@ static bool AdvanceSampleImport( ## 当前源码公开 API 清单 -按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 - -### `Inno.Scripting.Compiler.CSharpSampleSourceRewriter` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.CSharpSampleSourceRewriter.CSharpSampleSourceRewriter()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L17) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public void Inno.Scripting.Compiler.CSharpSampleSourceRewriter.Transform(Inno.Assets.Pipeline.AssetSampleTransformContext context)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L25) | Rewrites explicit stable type identities in cloned C# scripts and registers their asset mappings.
`context`:The private sample import stage and its identity map. | — | — | -| [`public sealed class Inno.Scripting.Compiler.CSharpSampleSourceRewriter : Inno.Assets.Pipeline.IAssetSampleSourceRewriter`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L17) | Gives imported C# sample scripts distinct type identities before their scene assets are remapped. | — | — | - -### `Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer` - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.compilationWideUsingDiagnosticId = "INNO2003"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L32) | Diagnostic identifier for a forbidden compilation-wide namespace import. | — | — | -| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.directImplementationNamespaceDiagnosticId = "INNO2001"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L22) | Diagnostic identifier for direct implementation namespace access. | — | — | -| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.missingLogicalNamespaceDiagnosticId = "INNO2002"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L27) | Diagnostic identifier for a missing logical namespace import. | — | — | -| [`public Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.LogicalScriptingApiAnalyzer()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L17) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public override void Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L73) | Initializes owned resources and establishes the instance's active lifetime.
`context`:The context that supplies state and services for this operation. | — | — | -| [`public override System.Collections.Immutable.ImmutableArray Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.SupportedDiagnostics { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L61) | Gets the complete compiler diagnostic set enforced by this analyzer. | — | — | -| [`public sealed class Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L17) | Enforces logical scripting namespaces and rejects direct implementation namespace access. | — | — | - -### `Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset` +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 @@ -132,61 +107,24 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.ScriptAssemblyDefinitionAsset()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L54) | Creates an empty definition asset for deserialization. | — | — | -| [`public Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.ScriptAssemblyDefinitionAsset(string assemblyName, Inno.Scripting.Compiler.ScriptAssemblyScope scope, string[]? references = null, string[]? defines = null, bool nullable = true, bool allowUnsafe = false)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L82) | Creates a configured script assembly definition for native asset export.
`assemblyName`:Stable managed assembly name.
`scope`:Runtime or Editor API scope.
`references`:Referenced script assembly names.
`defines`:Preprocessor symbols.
`nullable`:Whether nullable reference analysis is enabled.
`allowUnsafe`:Whether unsafe source is permitted. | — | `System.ArgumentException`:Thrown when assemblyName is empty. | -| [`public bool Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.allowUnsafe { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L49) | Gets whether unsafe source is permitted. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.assemblyName { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L19) | Gets the stable assembly name. | — | — | -| [`public string[] Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.defines { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L37) | Gets preprocessor symbols applied to this assembly. | — | — | -| [`public bool Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.nullable { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L43) | Gets whether nullable reference analysis is enabled. | — | — | -| [`public string[] Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.references { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L31) | Gets referenced script assembly names. | — | — | -| [`public Inno.Scripting.Compiler.ScriptAssemblyScope Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.scope { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L25) | Gets the assembly API scope. | — | — | -| [`public sealed class Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset : Inno.Assets.AssetObject`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L13) | Defines one project script assembly and its compilation policy. | — | — | - -### `Inno.Scripting.Compiler.ScriptAssemblyScope` - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`Inno.Scripting.Compiler.ScriptAssemblyScope.Editor = 1`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L16) | The assembly can use runtime and editor scripting APIs. | — | — | -| [`Inno.Scripting.Compiler.ScriptAssemblyScope.Runtime = 0`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L11) | The assembly can use runtime scripting APIs only. | — | — | -| [`public enum Inno.Scripting.Compiler.ScriptAssemblyScope : int`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L6) | Identifies whether a project script assembly can use editor-only APIs. | — | — | - -### `Inno.Scripting.Compiler.ScriptCompilationProgress` - - - - - - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptCompilationProgress.ScriptCompilationProgress(float fraction, string stage)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Describes monotonic progress through one script compilation.
`fraction`:The completed fraction in the inclusive range from zero to one.
`stage`:The current human-readable compiler stage. | — | — | -| [`public void Inno.Scripting.Compiler.ScriptCompilationProgress.Deconstruct(out float fraction, out string stage)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Scripting.Compiler.ScriptCompilationProgress.Equals(Inno.Scripting.Compiler.ScriptCompilationProgress other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Scripting.Compiler.ScriptCompilationProgress.Equals(object obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Scripting.Compiler.ScriptCompilationProgress.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptCompilationProgress.operator ==(Inno.Scripting.Compiler.ScriptCompilationProgress left, Inno.Scripting.Compiler.ScriptCompilationProgress right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptCompilationProgress.operator !=(Inno.Scripting.Compiler.ScriptCompilationProgress left, Inno.Scripting.Compiler.ScriptCompilationProgress right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Scripting.Compiler.ScriptCompilationProgress.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public float Inno.Scripting.Compiler.ScriptCompilationProgress.fraction { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L13) | The completed fraction in the inclusive range from zero to one. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptCompilationProgress.stage { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L14) | The current human-readable compiler stage. | — | — | -| [`public readonly record struct Inno.Scripting.Compiler.ScriptCompilationProgress : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Describes monotonic progress through one script compilation.
`fraction`:The completed fraction in the inclusive range from zero to one.
`stage`:The current human-readable compiler stage. | — | — | - -### `Inno.Scripting.Compiler.ScriptCompilationResult` - + + + + + + + + + + + + + + + @@ -197,22 +135,6 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public static Inno.Scripting.Compiler.ScriptCompilationResult Inno.Scripting.Compiler.ScriptCompilationResult.Failure(Inno.Scripting.Compiler.ScriptDiagnostic diagnostic)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L93) | Creates a failed result for an exception intercepted at an orchestration boundary.
返回:A failed immutable result with no output directory or module deployments.
`diagnostic`:The diagnostic describing why no candidate generation was produced. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.compiledAssemblyNames { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L77) | Gets assembly names compiled during this request instead of reused from the artifact cache. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.diagnostics { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L52) | Gets all diagnostics produced by the compilation. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.moduleDeployments { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L72) | Gets the immutable module artifacts and dependency topology produced by this compilation. | — | — | -| [`public string? Inno.Scripting.Compiler.ScriptCompilationResult.outputDirectory { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L57) | Gets the generation output directory when one was created. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.reusedAssemblyNames { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L82) | Gets assembly names reused from the deterministic artifact cache. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.runtimeAssemblyPaths { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L67) | Gets exact runtime-scope managed assemblies suitable for a deployed Player. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.stageTimings { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L62) | Gets completed compiler stage timings in execution order. | — | — | -| [`public bool Inno.Scripting.Compiler.ScriptCompilationResult.success { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L47) | Gets whether every discovered script assembly compiled or reused successfully. | — | — | -| [`public sealed class Inno.Scripting.Compiler.ScriptCompilationResult`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L13) | Reports the outputs and diagnostics of one authoring-generation or runtime-deployment script compilation. | — | — | - -### `Inno.Scripting.Compiler.ScriptCompilationStageTiming` - @@ -223,52 +145,6 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptCompilationStageTiming.ScriptCompilationStageTiming(string stage, System.TimeSpan elapsed)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L25) | Creates one immutable stage timing sample.
`stage`:The human-readable stage description.
`elapsed`:The wall time spent in the stage. | — | `System.ArgumentException`:Thrown when stage is empty or contains only whitespace.
`System.ArgumentOutOfRangeException`:Thrown when elapsed is negative. | -| [`public bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.Equals(Inno.Scripting.Compiler.ScriptCompilationStageTiming other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.Equals(object obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Indicates whether this instance and a specified object are equal.
返回:true if obj and this instance are the same type and represent the same value; otherwise, false.
`obj`:The object to compare with the current instance. | — | — | -| [`public override int Inno.Scripting.Compiler.ScriptCompilationStageTiming.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Returns the hash code for this instance.
返回:A 32-bit signed integer that is the hash code for this instance. | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.operator ==(Inno.Scripting.Compiler.ScriptCompilationStageTiming left, Inno.Scripting.Compiler.ScriptCompilationStageTiming right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.operator !=(Inno.Scripting.Compiler.ScriptCompilationStageTiming left, Inno.Scripting.Compiler.ScriptCompilationStageTiming right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Scripting.Compiler.ScriptCompilationStageTiming.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Returns the fully qualified type name of this instance.
返回:The fully qualified type name. | — | — | -| [`public System.TimeSpan Inno.Scripting.Compiler.ScriptCompilationStageTiming.elapsed { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L45) | Gets the wall time spent in the stage. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptCompilationStageTiming.stage { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L40) | Gets the human-readable stage description. | — | — | -| [`public readonly record struct Inno.Scripting.Compiler.ScriptCompilationStageTiming : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Describes the elapsed wall time of one completed script compilation stage. | — | — | - -### `Inno.Scripting.Compiler.ScriptCompiler` - - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptCompiler.ScriptCompiler(Inno.Scripting.Compiler.ScriptCompilerOptions options, Inno.Assets.Pipeline.AssetPipeline assets, Inno.Plugins.Authoring.PluginEnvironment plugins)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L40) | Creates a compiler over explicit authoring services owned by one host.
`options`:The project and derived-cache locations used by compilation.
`assets`:The authoring asset pipeline supplying committed source artifacts.
`plugins`:The Plugin environment supplying the candidate Plugin source generation. | — | `System.ArgumentNullException`:Thrown when an owner argument is null.
`System.ArgumentException`:Thrown when the project root is empty. | -| [`public int Inno.Scripting.Compiler.ScriptCompiler.CollectArtifacts(System.Collections.Generic.IEnumerable retainedDirectories)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L173) | Removes unreferenced compiler generations while retaining the supplied active directories.
返回:The number of obsolete generation directories removed from the compiler cache.
`retainedDirectories`:The generation directories that remain reachable from active runtime sessions. | — | — | -| [`public System.Threading.Tasks.ValueTask Inno.Scripting.Compiler.ScriptCompiler.CompileAuthoringGenerationAsync(System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.IAssetSourceSnapshot? sourceSnapshot = null)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L96) | Compiles a complete runtime and editor candidate generation without activating it.
返回:The immutable diagnostics and artifacts produced by the compilation attempt.
`progress`:Optional observer for monotonic compiler progress.
`cancellationToken`:The token that cancels compilation before an artifact generation is committed.
`sourceSnapshot`:Optional isolated authoring sources. Capture on its owner thread and keep its transaction alive until compilation drains; null selects the current Plugin candidate or active sources. | Source discovery captures live inputs on the owner thread. Reference generation, Roslyn and artifact writes run in the background; progress observers must accept callbacks from either thread. Retain the source generation until the returned operation drains, including after cancellation. | `System.OperationCanceledException`:Thrown when cancellationToken is canceled. | -| [`public System.Threading.Tasks.ValueTask Inno.Scripting.Compiler.ScriptCompiler.CompileRuntimeDeploymentAsync(string targetRuntimeDirectory, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L143) | Compiles only the runtime assembly closure required by a deployed Player and binds it to that Player runtime.
返回:The immutable diagnostics and runtime-only artifacts produced by the compilation attempt.
`targetRuntimeDirectory`:The verified Player Support Pack directory whose runtime assemblies will execute the emitted scripts.
`progress`:Optional observer for monotonic compiler progress.
`cancellationToken`:The token that cancels compilation before an artifact generation is committed. | — | `System.OperationCanceledException`:Thrown when cancellationToken is canceled.
`System.ArgumentException`:Thrown when targetRuntimeDirectory is empty.
`System.IO.DirectoryNotFoundException`:Thrown when targetRuntimeDirectory does not exist.
`System.IO.InvalidDataException`:Thrown when the target runtime has no valid Inno runtime assembly closure. | -| [`public void Inno.Scripting.Compiler.ScriptCompiler.GenerateProjectFiles()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L64) | Regenerates IDE project files from the current source graph and logical API references. | — | `System.InvalidOperationException`:Thrown when the authoring asset pipeline is not initialized. | -| [`public sealed class Inno.Scripting.Compiler.ScriptCompiler`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L16) | Produces deterministic runtime and editor script artifacts from one authoring snapshot. | — | — | - -### `Inno.Scripting.Compiler.ScriptCompilerOptions` - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptCompilerOptions.ScriptCompilerOptions()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L8) | C# 隐式构造函数;按类型声明执行字段初始化和基类构造。 | — | — | -| [`public required string Inno.Scripting.Compiler.ScriptCompilerOptions.projectRootDirectory { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L13) | Gets the project root containing Assets and Library. | — | — | -| [`public sealed class Inno.Scripting.Compiler.ScriptCompilerOptions`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L8) | Identifies the project and derived-cache locations used by one script compiler. | — | — | - -### `Inno.Scripting.Compiler.ScriptDiagnostic` - @@ -284,27 +160,6 @@ static bool AdvanceSampleImport( - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptDiagnostic.ScriptDiagnostic(string id, Inno.Core.Diagnostics.DiagnosticSeverity severity, string message, string? filePath, int line, int column)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Represents one source-oriented script compilation diagnostic.
`id`:The stable compiler, analyzer, or reload diagnostic identifier.
`severity`:The presentation severity of the diagnostic.
`message`:The human-readable diagnostic message.
`filePath`:The source path associated with the diagnostic, or null for a project-level diagnostic.
`line`:The one-based source line, or zero when no source location is available.
`column`:The one-based source column, or zero when no source location is available. | — | — | -| [`public void Inno.Scripting.Compiler.ScriptDiagnostic.Deconstruct(out string id, out Inno.Core.Diagnostics.DiagnosticSeverity severity, out string message, out string? filePath, out int line, out int column)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | C# record 生成的解构入口,按主构造声明顺序返回位置成员。 | — | — | -| [`public bool Inno.Scripting.Compiler.ScriptDiagnostic.Equals(Inno.Scripting.Compiler.ScriptDiagnostic? other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Indicates whether the current object is equal to another object of the same type.
返回:true if the current object is equal to the other parameter; otherwise, false.
`other`:An object to compare with this object. | — | — | -| [`public override bool Inno.Scripting.Compiler.ScriptDiagnostic.Equals(object? obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Determines whether the specified object is equal to the current object.
返回:true if the specified object is equal to the current object; otherwise, false.
`obj`:The object to compare with the current object. | — | — | -| [`public override int Inno.Scripting.Compiler.ScriptDiagnostic.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Serves as the default hash function.
返回:A hash code for the current object. | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptDiagnostic.operator ==(Inno.Scripting.Compiler.ScriptDiagnostic? left, Inno.Scripting.Compiler.ScriptDiagnostic? right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public static bool Inno.Scripting.Compiler.ScriptDiagnostic.operator !=(Inno.Scripting.Compiler.ScriptDiagnostic? left, Inno.Scripting.Compiler.ScriptDiagnostic? right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | C# record 生成的值相等性成员;比较当前 record 状态,并保持相等值的哈希一致。 | — | — | -| [`public override string Inno.Scripting.Compiler.ScriptDiagnostic.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Returns a string that represents the current object.
返回:A string that represents the current object. | — | — | -| [`public int Inno.Scripting.Compiler.ScriptDiagnostic.column { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L31) | The one-based source column, or zero when no source location is available. | — | — | -| [`public string? Inno.Scripting.Compiler.ScriptDiagnostic.filePath { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L29) | The source path associated with the diagnostic, or null for a project-level diagnostic. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptDiagnostic.id { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L26) | The stable compiler, analyzer, or reload diagnostic identifier. | — | — | -| [`public int Inno.Scripting.Compiler.ScriptDiagnostic.line { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L30) | The one-based source line, or zero when no source location is available. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptDiagnostic.message { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L28) | The human-readable diagnostic message. | — | — | -| [`public Inno.Core.Diagnostics.DiagnosticSeverity Inno.Scripting.Compiler.ScriptDiagnostic.severity { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L27) | The presentation severity of the diagnostic. | — | — | -| [`public sealed record Inno.Scripting.Compiler.ScriptDiagnostic : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Represents one source-oriented script compilation diagnostic.
`id`:The stable compiler, analyzer, or reload diagnostic identifier.
`severity`:The presentation severity of the diagnostic.
`message`:The human-readable diagnostic message.
`filePath`:The source path associated with the diagnostic, or null for a project-level diagnostic.
`line`:The one-based source line, or zero when no source location is available.
`column`:The one-based source column, or zero when no source location is available. | — | — | - -### `Inno.Scripting.Compiler.ScriptModuleDeployment` - @@ -313,42 +168,146 @@ static bool AdvanceSampleImport( + + + + + + + + + + + + + + + + + + + + + + + + + + + + -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | -| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Scripting.Compiler.ScriptModuleDeployment.assemblyScopes { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L70) | Gets explicit scopes keyed by assembly simple name, with case-insensitive lookup. | — | — | -| [`public Inno.Extensibility.Modules.AssemblyDomain Inno.Scripting.Compiler.ScriptModuleDeployment.domain { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L50) | Gets the ownership domain of this module's assemblies. | — | — | -| [`public string Inno.Scripting.Compiler.ScriptModuleDeployment.mainAssemblyPath { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L45) | Gets the absolute path of the module's primary assembly artifact. | — | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset : Inno.Assets.AssetObject`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L13) | Defines one project script assembly and its compilation policy. | — | — | +| [`public string Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.assemblyName { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L19) | Gets the stable assembly name. | — | — | +| [`public Inno.Scripting.Compiler.ScriptAssemblyScope Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.scope { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L25) | Gets the assembly API scope. | — | — | +| [`public string[] Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.references { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L31) | Gets referenced script assembly names. | — | — | +| [`public string[] Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.defines { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L37) | Gets preprocessor symbols applied to this assembly. | — | — | +| [`public bool Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.nullable { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L43) | Gets whether nullable reference analysis is enabled. | — | — | +| [`public bool Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.allowUnsafe { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L49) | Gets whether unsafe source is permitted. | — | — | +| [`public Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.ScriptAssemblyDefinitionAsset()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L54) | Creates an empty definition asset for deserialization. | — | — | +| [`public Inno.Scripting.Compiler.ScriptAssemblyDefinitionAsset.ScriptAssemblyDefinitionAsset(string assemblyName, Inno.Scripting.Compiler.ScriptAssemblyScope scope, string[]? references = null, string[]? defines = null, bool nullable = true, bool allowUnsafe = false)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyDefinitionAsset.cs#L82) | Creates a configured script assembly definition for native asset export.
`assemblyName`: Stable managed assembly name.
`scope`: Runtime or Editor API scope.
`references`: Referenced script assembly names.
`defines`: Preprocessor symbols.
`nullable`: Whether nullable reference analysis is enabled.
`allowUnsafe`: Whether unsafe source is permitted. | — | `System.ArgumentException`: Thrown when is empty. | +| [`public enum Inno.Scripting.Compiler.ScriptAssemblyScope : int`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L6) | Identifies whether a project script assembly can use editor-only APIs. | — | — | +| [`Inno.Scripting.Compiler.ScriptAssemblyScope.Runtime = 0`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L11) | The assembly can use runtime scripting APIs only. | — | — | +| [`Inno.Scripting.Compiler.ScriptAssemblyScope.Editor = 1`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptAssemblyScope.cs#L16) | The assembly can use runtime and editor scripting APIs. | — | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptSourceAsset : Inno.Assets.AssetObject`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L11) | Describes an imported C# source snapshot and its parse diagnostics. | — | — | +| [`public Inno.Scripting.Compiler.ScriptAssemblyScope Inno.Scripting.Compiler.ScriptSourceAsset.scope { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L17) | Gets the default assembly scope inferred for the source. | — | — | +| [`public string[] Inno.Scripting.Compiler.ScriptSourceAsset.declaredTypeNames { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L23) | Gets syntax-level type names declared by the source. | — | — | +| [`public string[] Inno.Scripting.Compiler.ScriptSourceAsset.parseDiagnostics { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L29) | Gets parse diagnostics associated with the source snapshot. | — | — | +| [`public Inno.Scripting.Compiler.ScriptSourceAsset.ScriptSourceAsset()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L34) | Creates an empty script source asset for deserialization. | — | — | +| [`public sealed class Inno.Scripting.Compiler.CSharpSampleSourceRewriter : Inno.Assets.Pipeline.IAssetSampleSourceRewriter`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L17) | Gives imported C# sample scripts distinct type identities before their scene assets are remapped. | — | — | +| [`public void Inno.Scripting.Compiler.CSharpSampleSourceRewriter.Transform(Inno.Assets.Pipeline.AssetSampleTransformContext context)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L25) | Rewrites explicit stable type identities in cloned C# scripts and registers their asset mappings.
`context`: The private sample import stage and its identity map. | — | — | +| [`public Inno.Scripting.Compiler.CSharpSampleSourceRewriter.CSharpSampleSourceRewriter()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/CSharpSampleSourceRewriter.cs#L17) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L19) | Enforces logical scripting namespaces and rejects direct implementation namespace access. | — | — | +| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.directImplementationNamespaceDiagnosticId = "INNO2001"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L24) | Diagnostic identifier for direct implementation namespace access. | — | — | +| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.missingLogicalNamespaceDiagnosticId = "INNO2002"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L29) | Diagnostic identifier for a missing logical namespace import. | — | — | +| [`public const string Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.compilationWideUsingDiagnosticId = "INNO2003"`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L34) | Diagnostic identifier for a forbidden compilation-wide namespace import. | — | — | +| [`public override System.Collections.Immutable.ImmutableArray Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.SupportedDiagnostics { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L63) | Gets the complete compiler diagnostic set enforced by this analyzer. | — | — | +| [`public override void Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L75) | Initializes owned resources and establishes the instance's active lifetime.
`context`: The context that supplies state and services for this operation. | — | — | +| [`public Inno.Scripting.Compiler.LogicalScriptingApiAnalyzer.LogicalScriptingApiAnalyzer()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/LogicalScriptingApiAnalyzer.cs#L19) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptCompilationResult`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L13) | Reports the outputs and diagnostics of one authoring-generation or runtime-deployment script compilation. | — | — | +| [`public bool Inno.Scripting.Compiler.ScriptCompilationResult.success { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L47) | Gets whether every discovered script assembly compiled or reused successfully. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.diagnostics { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L52) | Gets all diagnostics produced by the compilation. | — | — | +| [`public string? Inno.Scripting.Compiler.ScriptCompilationResult.outputDirectory { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L57) | Gets the generation output directory when one was created. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.stageTimings { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L62) | Gets completed compiler stage timings in execution order. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.runtimeAssemblyPaths { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L67) | Gets exact runtime-scope managed assemblies suitable for a deployed Player. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.moduleDeployments { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L72) | Gets the immutable module artifacts and dependency topology produced by this compilation. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.compiledAssemblyNames { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L77) | Gets assembly names compiled during this request instead of reused from the artifact cache. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationResult.reusedAssemblyNames { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L82) | Gets assembly names reused from the deterministic artifact cache. | — | — | +| [`public static Inno.Scripting.Compiler.ScriptCompilationResult Inno.Scripting.Compiler.ScriptCompilationResult.Failure(Inno.Scripting.Compiler.ScriptDiagnostic diagnostic)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationResult.cs#L93) | Creates a failed result for an exception intercepted at an orchestration boundary.
`diagnostic`: The diagnostic describing why no candidate generation was produced. | A failed immutable result with no output directory or module deployments. | — | +| [`public readonly record struct Inno.Scripting.Compiler.ScriptCompilationStageTiming : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Describes the elapsed wall time of one completed script compilation stage. | — | — | +| [`public Inno.Scripting.Compiler.ScriptCompilationStageTiming.ScriptCompilationStageTiming(string stage, System.TimeSpan elapsed)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L25) | Creates one immutable stage timing sample.
`stage`: The human-readable stage description.
`elapsed`: The wall time spent in the stage. | — | `System.ArgumentException`: Thrown when is empty or contains only whitespace.
`System.ArgumentOutOfRangeException`: Thrown when is negative. | +| [`public string Inno.Scripting.Compiler.ScriptCompilationStageTiming.stage { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L40) | Gets the human-readable stage description. | — | — | +| [`public System.TimeSpan Inno.Scripting.Compiler.ScriptCompilationStageTiming.elapsed { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L45) | Gets the wall time spent in the stage. | — | — | +| [`public override string Inno.Scripting.Compiler.ScriptCompilationStageTiming.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.operator !=(Inno.Scripting.Compiler.ScriptCompilationStageTiming left, Inno.Scripting.Compiler.ScriptCompilationStageTiming right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.operator ==(Inno.Scripting.Compiler.ScriptCompilationStageTiming left, Inno.Scripting.Compiler.ScriptCompilationStageTiming right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Scripting.Compiler.ScriptCompilationStageTiming.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.Equals(object obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Scripting.Compiler.ScriptCompilationStageTiming.Equals(Inno.Scripting.Compiler.ScriptCompilationStageTiming other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationStageTiming.cs#L8) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public sealed record Inno.Scripting.Compiler.ScriptDiagnostic : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Represents one source-oriented script compilation diagnostic.
`id`: The stable compiler, analyzer, or reload diagnostic identifier.
`severity`: The presentation severity of the diagnostic.
`message`: The human-readable diagnostic message.
`filePath`: The source path associated with the diagnostic, or for a project-level diagnostic.
`line`: The one-based source line, or zero when no source location is available.
`column`: The one-based source column, or zero when no source location is available. | — | — | +| [`public Inno.Scripting.Compiler.ScriptDiagnostic.ScriptDiagnostic(string id, Inno.Core.Diagnostics.DiagnosticSeverity severity, string message, string? filePath, int line, int column)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Represents one source-oriented script compilation diagnostic.
`id`: The stable compiler, analyzer, or reload diagnostic identifier.
`severity`: The presentation severity of the diagnostic.
`message`: The human-readable diagnostic message.
`filePath`: The source path associated with the diagnostic, or for a project-level diagnostic.
`line`: The one-based source line, or zero when no source location is available.
`column`: The one-based source column, or zero when no source location is available. | — | — | +| [`public string Inno.Scripting.Compiler.ScriptDiagnostic.id { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L26) | The stable compiler, analyzer, or reload diagnostic identifier. | — | — | +| [`public Inno.Core.Diagnostics.DiagnosticSeverity Inno.Scripting.Compiler.ScriptDiagnostic.severity { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L27) | The presentation severity of the diagnostic. | — | — | +| [`public string Inno.Scripting.Compiler.ScriptDiagnostic.message { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L28) | The human-readable diagnostic message. | — | — | +| [`public string? Inno.Scripting.Compiler.ScriptDiagnostic.filePath { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L29) | The source path associated with the diagnostic, or for a project-level diagnostic. | — | — | +| [`public int Inno.Scripting.Compiler.ScriptDiagnostic.line { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L30) | The one-based source line, or zero when no source location is available. | — | — | +| [`public int Inno.Scripting.Compiler.ScriptDiagnostic.column { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L31) | The one-based source column, or zero when no source location is available. | — | — | +| [`public override string Inno.Scripting.Compiler.ScriptDiagnostic.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Returns a string that represents the current object. | A string that represents the current object. | — | +| [`public static bool Inno.Scripting.Compiler.ScriptDiagnostic.operator !=(Inno.Scripting.Compiler.ScriptDiagnostic? left, Inno.Scripting.Compiler.ScriptDiagnostic? right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Scripting.Compiler.ScriptDiagnostic.operator ==(Inno.Scripting.Compiler.ScriptDiagnostic? left, Inno.Scripting.Compiler.ScriptDiagnostic? right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Scripting.Compiler.ScriptDiagnostic.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Serves as the default hash function. | A hash code for the current object. | — | +| [`public override bool Inno.Scripting.Compiler.ScriptDiagnostic.Equals(object? obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Determines whether the specified object is equal to the current object.
`obj`: The object to compare with the current object. | if the specified object is equal to the current object; otherwise, . | — | +| [`public bool Inno.Scripting.Compiler.ScriptDiagnostic.Equals(Inno.Scripting.Compiler.ScriptDiagnostic? other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Scripting.Compiler.ScriptDiagnostic.Deconstruct(out string id, out Inno.Core.Diagnostics.DiagnosticSeverity severity, out string message, out string? filePath, out int line, out int column)`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptDiagnostic.cs#L25) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptModuleDeployment`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L14) | Describes the immutable assembly artifacts and dependency topology of one compiled script module. The receiving host chooses how these artifacts enter its module catalog. | — | — | | [`public string Inno.Scripting.Compiler.ScriptModuleDeployment.moduleName { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L40) | Gets the logical module name used to resolve dependencies within this compiled generation. | — | — | -| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptModuleDeployment.preloadAssemblyPaths { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L60) | Gets the additional assembly artifacts that belong to this same module generation. | — | — | +| [`public string Inno.Scripting.Compiler.ScriptModuleDeployment.mainAssemblyPath { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L45) | Gets the absolute path of the module's primary assembly artifact. | — | — | +| [`public Inno.Extensibility.Modules.AssemblyDomain Inno.Scripting.Compiler.ScriptModuleDeployment.domain { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L50) | Gets the ownership domain of this module's assemblies. | — | — | | [`public Inno.Extensibility.Modules.AssemblyScope Inno.Scripting.Compiler.ScriptModuleDeployment.scope { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L55) | Gets the dependency scope used for assemblies without an explicit scope override. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptModuleDeployment.preloadAssemblyPaths { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L60) | Gets the additional assembly artifacts that belong to this same module generation. | — | — | | [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptModuleDeployment.upstreamModuleNames { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L65) | Gets the logical names of modules that supply managed dependencies to this module. | — | — | -| [`public sealed class Inno.Scripting.Compiler.ScriptModuleDeployment`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L14) | Describes the immutable assembly artifacts and dependency topology of one compiled script module. The receiving host chooses how these artifacts enter its module catalog. | — | — | - -### `Inno.Scripting.Compiler.ScriptSourceAsset` - - - - - - - -| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | -| --- | --- | --- | --- | -| [`public Inno.Scripting.Compiler.ScriptSourceAsset.ScriptSourceAsset()`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L34) | Creates an empty script source asset for deserialization. | — | — | -| [`public string[] Inno.Scripting.Compiler.ScriptSourceAsset.declaredTypeNames { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L23) | Gets syntax-level type names declared by the source. | — | — | -| [`public string[] Inno.Scripting.Compiler.ScriptSourceAsset.parseDiagnostics { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L29) | Gets parse diagnostics associated with the source snapshot. | — | — | -| [`public Inno.Scripting.Compiler.ScriptAssemblyScope Inno.Scripting.Compiler.ScriptSourceAsset.scope { get; private set; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L17) | Gets the default assembly scope inferred for the source. | — | — | -| [`public sealed class Inno.Scripting.Compiler.ScriptSourceAsset : Inno.Assets.AssetObject`](../../src/runtime/scripting/Inno.Scripting.Compiler/Assets/ScriptSourceAsset.cs#L11) | Describes an imported C# source snapshot and its parse diagnostics. | — | — | +| [`public System.Collections.Generic.IReadOnlyDictionary Inno.Scripting.Compiler.ScriptModuleDeployment.assemblyScopes { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptModuleDeployment.cs#L70) | Gets explicit scopes keyed by assembly simple name, with case-insensitive lookup. | — | — | +| [`public interface Inno.Scripting.Compiler.IScriptCompilationEnvironmentProvider`](../../src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs#L8) | Supplies the host-selected framework contract for one compilation or IDE projection operation. | — | — | +| [`Inno.Scripting.Compiler.ScriptCompilationInputs Inno.Scripting.Compiler.IScriptCompilationEnvironmentProvider.Capture(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs#L19) | Captures current SDK inputs without choosing a framework inside the scripting compiler.
`cancellationToken`: Cancels input preparation before any script artifact is published. | Complete immutable inputs; missing or inconsistent SDK inputs must fail explicitly. | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptCompilationInputs`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs#L11) | Describes framework references and predefined symbols selected by the authoring host's SDK. | — | — | +| [`public Inno.Scripting.Compiler.ScriptCompilationInputs.ScriptCompilationInputs(string sdkIdentity, System.Collections.Generic.IEnumerable frameworkReferences, System.Collections.Generic.IEnumerable predefinedSymbols)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs#L31) | Freezes a complete C# 14 and .NET 10 reference contract without retaining SDK objects.
`sdkIdentity`: The exact SDK identity that evaluated this contract.
`frameworkReferences`: Absolute framework assembly paths selected by SDK reference resolution.
`predefinedSymbols`: Framework and configuration symbols produced by SDK target execution. | — | `System.ArgumentException`: The identity, reference set or a reference path is invalid.
`System.ArgumentNullException`: A collection is null. | +| [`public string Inno.Scripting.Compiler.ScriptCompilationInputs.sdkIdentity { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs#L50) | Gets the exact SDK identity used for IDE consistency checks and artifact identity. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationInputs.frameworkReferences { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs#L55) | Gets the SDK-selected framework paths, whose content is frozen before Roslyn compilation. | — | — | +| [`public System.Collections.Generic.IReadOnlyList Inno.Scripting.Compiler.ScriptCompilationInputs.predefinedSymbols { get; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs#L60) | Gets SDK-produced symbols shared by runtime compilation and IDE projects. | — | — | +| [`public readonly record struct Inno.Scripting.Compiler.ScriptCompilationProgress : System.IEquatable`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Describes monotonic progress through one script compilation.
`fraction`: The completed fraction in the inclusive range from zero to one.
`stage`: The current human-readable compiler stage. | — | — | +| [`public Inno.Scripting.Compiler.ScriptCompilationProgress.ScriptCompilationProgress(float fraction, string stage)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Describes monotonic progress through one script compilation.
`fraction`: The completed fraction in the inclusive range from zero to one.
`stage`: The current human-readable compiler stage. | — | — | +| [`public float Inno.Scripting.Compiler.ScriptCompilationProgress.fraction { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L13) | The completed fraction in the inclusive range from zero to one. | — | — | +| [`public string Inno.Scripting.Compiler.ScriptCompilationProgress.stage { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L14) | The current human-readable compiler stage. | — | — | +| [`public override string Inno.Scripting.Compiler.ScriptCompilationProgress.ToString()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Returns the fully qualified type name of this instance. | The fully qualified type name. | — | +| [`public static bool Inno.Scripting.Compiler.ScriptCompilationProgress.operator !=(Inno.Scripting.Compiler.ScriptCompilationProgress left, Inno.Scripting.Compiler.ScriptCompilationProgress right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public static bool Inno.Scripting.Compiler.ScriptCompilationProgress.operator ==(Inno.Scripting.Compiler.ScriptCompilationProgress left, Inno.Scripting.Compiler.ScriptCompilationProgress right)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public override int Inno.Scripting.Compiler.ScriptCompilationProgress.GetHashCode()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | +| [`public override bool Inno.Scripting.Compiler.ScriptCompilationProgress.Equals(object obj)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | +| [`public bool Inno.Scripting.Compiler.ScriptCompilationProgress.Equals(Inno.Scripting.Compiler.ScriptCompilationProgress other)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | +| [`public void Inno.Scripting.Compiler.ScriptCompilationProgress.Deconstruct(out float fraction, out string stage)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationProgress.cs#L12) | Inherited or compiler-provided contract. | — | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptCompiler`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L16) | Produces deterministic runtime and editor script artifacts from one authoring snapshot. | — | — | +| [`public Inno.Scripting.Compiler.ScriptCompiler.ScriptCompiler(Inno.Scripting.Compiler.ScriptCompilerOptions options, Inno.Assets.Pipeline.AssetPipeline assets, Inno.Plugins.Authoring.PluginEnvironment plugins)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L40) | Creates a compiler over explicit authoring services owned by one host.
`options`: The project and derived-cache locations used by compilation.
`assets`: The authoring asset pipeline supplying committed source artifacts.
`plugins`: The Plugin environment supplying the candidate Plugin source generation. | — | `System.ArgumentNullException`: Thrown when an owner argument is .
`System.ArgumentException`: Thrown when the project root is empty. | +| [`public void Inno.Scripting.Compiler.ScriptCompiler.GenerateProjectFiles()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L66) | Regenerates IDE project files from the current source graph and logical API references. | — | `System.InvalidOperationException`: Thrown when the authoring asset pipeline is not initialized. | +| [`public System.Threading.Tasks.ValueTask Inno.Scripting.Compiler.ScriptCompiler.CompileAuthoringGenerationAsync(System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken), Inno.Assets.Pipeline.IAssetSourceSnapshot? sourceSnapshot = null)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L98) | Compiles a complete runtime and editor candidate generation without activating it.
`progress`: Optional observer for monotonic compiler progress.
`cancellationToken`: The token that cancels compilation before an artifact generation is committed.
`sourceSnapshot`: Optional isolated authoring sources. Capture on its owner thread and keep its transaction alive until compilation drains; null selects the current Plugin candidate or active sources. | The immutable diagnostics and artifacts produced by the compilation attempt. | `System.OperationCanceledException`: Thrown when is canceled. | +| [`public System.Threading.Tasks.ValueTask Inno.Scripting.Compiler.ScriptCompiler.CompileRuntimeDeploymentAsync(string targetRuntimeDirectory, System.IProgress? progress = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L145) | Compiles only the runtime assembly closure required by a deployed Player and binds it to that Player runtime.
`targetRuntimeDirectory`: The verified Player Support Pack directory whose runtime assemblies will execute the emitted scripts.
`progress`: Optional observer for monotonic compiler progress.
`cancellationToken`: The token that cancels compilation before an artifact generation is committed. | The immutable diagnostics and runtime-only artifacts produced by the compilation attempt. | `System.OperationCanceledException`: Thrown when is canceled.
`System.ArgumentException`: Thrown when is empty.
`System.IO.DirectoryNotFoundException`: Thrown when does not exist.
`System.IO.InvalidDataException`: Thrown when the target runtime has no valid Inno runtime assembly closure. | +| [`public int Inno.Scripting.Compiler.ScriptCompiler.CollectArtifacts(System.Collections.Generic.IEnumerable retainedDirectories)`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs#L175) | Removes unreferenced compiler generations while retaining the supplied active directories.
`retainedDirectories`: The generation directories that remain reachable from active runtime sessions. | The number of obsolete generation directories removed from the compiler cache. | — | +| [`public sealed class Inno.Scripting.Compiler.ScriptCompilerOptions`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L8) | Identifies the project and derived-cache locations used by one script compiler. | — | — | +| [`public required string Inno.Scripting.Compiler.ScriptCompilerOptions.projectRootDirectory { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L13) | Gets the project root containing Assets and Library. | — | — | +| [`public required Inno.Scripting.Compiler.IScriptCompilationEnvironmentProvider Inno.Scripting.Compiler.ScriptCompilerOptions.compilationEnvironment { get; init; }`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L18) | Gets the host-owned source of SDK framework inputs for each operation. | — | — | +| [`public Inno.Scripting.Compiler.ScriptCompilerOptions.ScriptCompilerOptions()`](../../src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs#L8) | Inherited or compiler-provided contract. | — | — | ## 项目依赖 -- [Inno.Core.Collections](../core/Inno.Core.Collections.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Extensibility.Modules](../extensibility/Inno.Extensibility.Modules.md):公开引用边界由实际签名核对。 -- [Inno.Scripting.Api](Inno.Scripting.Api.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Assets](../assets/Inno.Assets.md):公开引用边界由实际签名核对。 -- [Inno.Assets.Pipeline](../assets/Inno.Assets.Pipeline.md):公开引用边界由实际签名核对。 -- [Inno.Plugins.Authoring](../plugins/Inno.Plugins.Authoring.md):公开引用边界由实际签名核对。 -- [Inno.Core.Diagnostics](../core/Inno.Core.Diagnostics.md):公开引用边界由实际签名核对。 -- [Inno.Runtime.Generators](../runtime/Inno.Runtime.Generators.md):实现依赖,PrivateAssets="compile"。 -- [Inno.Core.Serialization.Generators](../core/Inno.Core.Serialization.Generators.md):实现依赖,PrivateAssets="compile"。 +| 工程 | 边界 | +| --- | --- | +| `Inno.Core.Collections` | 实现依赖;不传递 compile surface | +| `Inno.Extensibility.Modules` | 公开签名依赖 | +| `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | +| `Inno.Assets` | 公开签名依赖 | +| `Inno.Assets.Pipeline` | 公开签名依赖 | +| `Inno.Plugins.Authoring` | 公开签名依赖 | +| `Inno.Core.Diagnostics` | 公开签名依赖 | +| `Inno.Runtime.Generators` | 实现依赖;不传递 compile surface | +| `Inno.Core.Serialization.Generators` | 实现依赖;不传递 compile surface | diff --git a/docs/tooling/Inno.Tooling.Architecture.md b/docs/tooling/Inno.Tooling.Architecture.md index e66bc43c9..1f9f7d6d1 100644 --- a/docs/tooling/Inno.Tooling.Architecture.md +++ b/docs/tooling/Inno.Tooling.Architecture.md @@ -1,5 +1,12 @@ # Inno.Tooling.Architecture +## 独立输出目录的检查 + +使用 `--dotnet ` 和 `--artifacts-path ` 检查独立 SDK 输出。求值图的 `TargetPath` 决定公开程序集的 owner,Wiki 的 `ReferencePath`、`Compile`、语言和符号也使用同一输出选择。不要把独立输出复制回默认 bin 来满足检查。 + +`--configuration` 支持 `Debug`、`SourceDebug` 和 `Release`。`--artifacts-path` 要求显式 `--dotnet`;SDK 失败、缺失引用或取消均不能当作通过。 + + [Tooling 索引](README.md) 退休 catch 的 AST 规则拒绝直接捕获 `RetirementPendingException` / `RetirementTimeoutException`(包括全限定名)。 @@ -78,24 +85,7 @@ ProjectReference、Compile、程序集身份、产品与 Native target。不会 BGCS/Cpp2C target profile 必须显式声明 Native owner 内的输出目录,与宿主输出不能相同或互相包含。 此规则覆盖 managed binding 和 C++ bridge,避免任一生成器的原子目录替换删除另一目标的产物。 -## 当前源码公开 API 清单 - -当前声明由源码语义清单核对,包含 public 和可供外部派生实现使用的 protected 成员。构建契约不作为游戏脚本导出;线程、失败与所有权要求见前文及每项 XML。 - -### `Inno.Tooling.Architecture.ArchitectureValidator` - - - -| 声明 | 行为、参数与结果 | -| --- | --- | -| [`public static int Inno.Tooling.Architecture.ArchitectureValidator.Execute(string[] arguments, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../tools/Inno.Tooling.Architecture/ArchitectureValidator.cs#L61) | Executes repository validation or an explicitly requested documentation maintenance operation.
`arguments`:The optional repository path, configuration, XML maintenance flag, explicit SDK, evaluated graph output, and Wiki inventories with explicitly selected runtime and editor script reference assemblies. --wiki-target limits product compilation to that explicit target; shared Wiki checks remain complete.
`cancellationToken`:Cancels owned SDK evaluation processes and drains them before returning.
返回:Zero when validation succeeds, or one when any invariant is violated.
T:System.ArgumentException:An option is unknown or more than one repository path is supplied.
T:System.IO.DirectoryNotFoundException:The requested path is not inside an engine checkout.
T:System.OperationCanceledException:The caller cancels evaluated SDK project inspection.
T:System.IO.InvalidDataException:A selected Wiki script reference is not the declared logical API assembly. | -| [`public static class Inno.Tooling.Architecture.ArchitectureValidator`](../../tools/Inno.Tooling.Architecture/ArchitectureValidator.cs#L16) | Checks repository dependency, API documentation and source ownership invariants. | - -## 项目依赖 - -- [Inno.Extensibility.Catalogs](../extensibility/Inno.Extensibility.Catalogs.md):公开引用边界由实际签名核对。 -- [Inno.Build.Toolchains](../build/Inno.Build.Toolchains.md):实现依赖,PrivateAssets="compile"。 ## 缺失目标契约的诊断 @@ -108,3 +98,22 @@ BGCS/Cpp2C target profile 必须显式声明 Native owner 内的输出目录, ## 开发入口项目边界 项目归属检查要求 entry 仅包含两个普通可执行项目:Editor 以宿主条件项目引用选择真实产品,Build 直接引用共享 CLI。共同生产层不得反向依赖 entry;backend、平台基础模块与 integration 的原有边界继续检查。 + +## 当前源码公开 API 清单 + +此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。 + + + + +| API 与源码 | 契约与参数 | 返回值 | 重要异常 | +| --- | --- | --- | --- | +| [`public static class Inno.Tooling.Architecture.ArchitectureValidator`](../../tools/Inno.Tooling.Architecture/ArchitectureValidator.cs#L16) | Checks repository dependency, API documentation and source ownership invariants. | — | — | +| [`public static int Inno.Tooling.Architecture.ArchitectureValidator.Execute(string[] arguments, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../tools/Inno.Tooling.Architecture/ArchitectureValidator.cs#L61) | Executes repository validation or an explicitly requested documentation maintenance operation.
`arguments`: The optional repository path, configuration, XML maintenance flag, explicit SDK, evaluated graph output, and Wiki inventories with explicitly selected runtime and editor script reference assemblies. --wiki-target limits product compilation to that explicit target; shared Wiki checks remain complete.
`cancellationToken`: Cancels owned SDK evaluation processes and drains them before returning. | Zero when validation succeeds, or one when any invariant is violated. | `System.ArgumentException`: An option is unknown or more than one repository path is supplied.
`System.IO.DirectoryNotFoundException`: The requested path is not inside an engine checkout.
`System.OperationCanceledException`: The caller cancels evaluated SDK project inspection.
`System.IO.InvalidDataException`: A selected Wiki script reference is not the declared logical API assembly. | + +## 项目依赖 + +| 工程 | 边界 | +| --- | --- | +| `Inno.Extensibility.Catalogs` | 公开签名依赖 | +| `Inno.Build.Toolchains` | 实现依赖;不传递 compile surface | diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index 46a8193df..d7ce47a29 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -2,7 +2,7 @@ [Original-plan review](acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md) · [Parent issue #27](https://github.com/Wolfiora/InnoEngine/issues/27) · [PR #26](https://github.com/Wolfiora/InnoEngine/pull/26) -Baseline: `30d18489fbf68763229cd8ed35a7005f543019ad`. Evidence owner: `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`. The original desktop plan has SHA-256 `a55f25b76db09befd6cdd2db06a607d0e2a70d660dca6ca5429c64f3d5623646`. +Baseline: `30d18489fbf68763229cd8ed35a7005f543019ad`. Evidence owner: `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`. The original UI plan's hash recorded by the review is `a55f25b76db09befd6cdd2db06a607d0e2a70d660dca6ca5429c64f3d5623646`. The desktop file now contains the approved issue-driven closure plan, SHA-256 `cbb7d50a26a08e4a0d50c7e0f9aabe6c8538ae3fbe229957d2094a4b60325d9e`; these are different documents. Every work item below is a real GitHub issue created before implementation. Issues remain open after branch verification. Only completely verified items receive an individual `Fixes #N` in PR #26; they close on merge. The broader #18 uses `Refs #18`. No automatic merge is authorized. @@ -10,21 +10,23 @@ Every work item below is a real GitHub issue created before implementation. Issu | Work | Issue | Owner | State | | --- | --- | --- | --- | -| E01 scopes and failures | [#28](https://github.com/Wolfiora/InnoEngine/issues/28) | ImGui adapter and concrete presentation | In progress | -| E02 command indexes | [#29](https://github.com/Wolfiora/InnoEngine/issues/29) | Interactions generation catalog | Not started | -| E03 styles | [#30](https://github.com/Wolfiora/InnoEngine/issues/30) | Shared and domain presentation | Not started | -| E04 keyboard and Console | [#31](https://github.com/Wolfiora/InnoEngine/issues/31) | Selection, controls and Diagnostics views | Not started | -| E05 graph | [#32](https://github.com/Wolfiora/InnoEngine/issues/32) | Graph/Graph.ImGui and Shader consumer | Not started | -| E06 statistics | [#33](https://github.com/Wolfiora/InnoEngine/issues/33) | EditorStatistics and Stats Panel | Not started | -| E07 large views | [#34](https://github.com/Wolfiora/InnoEngine/issues/34) | Browser/tree/Console/graph presentation | Not started | -| E08 workflows | [#35](https://github.com/Wolfiora/InnoEngine/issues/35) | Existing domain features and History | Not started | -| Actual restore/load scope | [#16](https://github.com/Wolfiora/InnoEngine/issues/16) | Build public SDK tests | Not started | -| Frozen framework inputs | [#19](https://github.com/Wolfiora/InnoEngine/issues/19) | Host/Build capture and Compiler | Not started | -| E09 scale/input | [#36](https://github.com/Wolfiora/InnoEngine/issues/36) | Native public-input fixtures | Not started | -| E10 retirement/reload | [#37](https://github.com/Wolfiora/InnoEngine/issues/37) | Host and generation owners | Not started | -| E11 performance | [#38](https://github.com/Wolfiora/InnoEngine/issues/38) | Deterministic public-host workloads | Baseline preparation | -| E12 documentation/review | [#39](https://github.com/Wolfiora/InnoEngine/issues/39) | Each changed API and Wiki owner | Not started | +| E01 scopes and failures | [#28](https://github.com/Wolfiora/InnoEngine/issues/28) | ImGui adapter and concrete presentation | Focused native scopes passed; final graph/lifecycle coverage pending | +| E02 command indexes | [#29](https://github.com/Wolfiora/InnoEngine/issues/29) | Interactions generation catalog | Implemented; focused contracts passed; final retirement/performance pending | +| E03 styles | [#30](https://github.com/Wolfiora/InnoEngine/issues/30) | Shared and domain presentation | Domain styles moved to their actual owners; same-frame style deferral and foreign-context contracts passed; complete physical visual matrix pending | +| E04 keyboard and Console | [#31](https://github.com/Wolfiora/InnoEngine/issues/31) | Selection, controls and Diagnostics views | Focused actual Panel workflows passed; full grid/scale/lifecycle matrix pending | +| E05 graph | [#32](https://github.com/Wolfiora/InnoEngine/issues/32) | Graph/Graph.ImGui and Shader consumer | Neutral nodes, layout and pointer intents implemented; 13 contracts and one native failure-scope test passed; complete workflow/performance pending | +| E06 statistics | [#33](https://github.com/Wolfiora/InnoEngine/issues/33) | EditorStatistics and Stats Panel | Core snapshot and three real native sampling contracts passed; complete lifecycle/performance pending | +| E07 large views | [#34](https://github.com/Wolfiora/InnoEngine/issues/34) | Browser/tree/Console/graph presentation | 10k tree/list and bounded Console presentation implemented; fixed-input paired measurements pending | +| E08 workflows | [#35](https://github.com/Wolfiora/InnoEngine/issues/35) | Existing domain features and History | Focused workflows plus actual FlappyBird Inspector edit/Undo/Redo and Play/stop executed; complete journey matrix pending | +| Actual restore/load scope | [#16](https://github.com/Wolfiora/InnoEngine/issues/16) | Build public SDK tests | Four real SDK/load cases passed, including isolated mismatch rejection and strict collectible retirement; final suite/CI pending | +| Frozen framework inputs | [#19](https://github.com/Wolfiora/InnoEngine/issues/19) | Host/Build capture and Compiler | SDK-owned provider replaces reference-pack selection; 11 frozen SDK/C# 14 contracts passed; final companion projections and CI pending | +| E09 scale/input | [#36](https://github.com/Wolfiora/InnoEngine/issues/36) | Native public-input fixtures | 56 native simulated-metric combinations and selected real FlappyBird input/screenshots executed; final comprehensive matrix pending | +| E10 retirement/reload | [#37](https://github.com/Wolfiora/InnoEngine/issues/37) | Host and generation owners | Actual nine-panel 100-cycle product fixture, saved Scene and Shader cycles, and focused generation tests executed; complete convergence matrix pending | +| E11 performance | [#38](https://github.com/Wolfiora/InnoEngine/issues/38) | Deterministic public-host workloads | Original/current deterministic harnesses prepared; identical current FlappyBird plugin input cannot compile against the original API; full comparison blocked | +| E12 documentation/review | [#39](https://github.com/Wolfiora/InnoEngine/issues/39) | Each changed API and Wiki owner | Design standard added; SDK-based architecture/Wiki gate passed for 241 projects before the final style cleanup; final synchronization pending | +| Current SDK-owned validation | [#41](https://github.com/Wolfiora/InnoEngine/issues/41) | Tooling SDK capture and current public source models | SDK output owner and framework references implemented; 92 focused regressions passed; full policy/documentation audit pending | | Existing History rejection fix | [#13](https://github.com/Wolfiora/InnoEngine/issues/13) | History | Previously verified; final rerun required | +| Required build regression | [#40](https://github.com/Wolfiora/InnoEngine/issues/40) | SDK/compiler-tool graph and load ownership | Compiler-tool graph fix committed; four SDK contracts passed; full normal build/load matrix pending | ## Original requirement traceability @@ -79,3 +81,82 @@ Each row must ultimately record a final commit, executable command and evidence ## Delivery constraints .NET SDK 10.0.402, C# 14 and BGCS 4.1.2 remain fixed. No BGCS/vendor/generated modifications, global/implicit imports, IVT, private reflection, test backdoors, compatibility readers or parallel infrastructure. Author and committer are Aaron Liao , with no co-author. Preserve exact source ownership and clean only proven inactive owned outputs; retain current products and evidence. + +## Current evidence, not final acceptance + +- Baseline inventory is in `baseline.json` and `device.json` under the evidence owner. All five repositories were initially clean, including BGCS. +- `65d222ae` implements the first scope-safety corrections. `e01/scopes.trx` has 55 passes and `e01/console-scopes-verified.trx` has 5 passes. The initial Console failure fixture omitted its required DiagnosticHub scope; the correction and original log remain recorded. +- `88df3c79` implements generation-owned command indexes and fresh contextual resolution. `e02/commands-statistics.trx` has 129 passes, including new menu/shortcut and Core statistics contracts in the working candidate. This is not the final committed matrix. +- Preliminary focused samples in `baseline/command-statistics.json` and `e02/focused-comparison.json` show menu allocation about 8,680 to 4,240 bytes/op and unchanged-statistics path about 16,792 to 184 bytes/op, including test assertions. Concurrent build activity prevents controlled whole-editor CPU claims. +- `e03/styles.trx` has 22 passes. Full scale, lifetime and plugin acceptance is pending. +- Ordinary Debug and the explicitly owned D: SDK output both encountered a generator CS2012 write conflict. Issue #40 is required to restore reliable repeated validation; no IDE process or global package was changed. Read-only Restart Manager inspection found no persistent current lock; the file has no extra hardlink. Binlog diagnosis remains pending. + +- `6a656b67` normalizes SDK-negotiated compiler-tool references across analyzer/runtime/transitive consumers. `e40/compiler-tool-scope-final.trx`: 4 passed; final native/task suites remain required. +- `b7a8924d` commits snapshot reuse and its three Core contracts. `e04/controls-verified.trx`: 40 native control tests passed. +- `e04/console-statistics-verified.trx`: 8 passed, 2 failed. The two Console fixtures used different log call sites while expecting collapse; grouping intentionally includes location. The fixture is corrected to repeat one source call; actual search verification is rerunning. Stats native sampling/window/capacity cases passed. +- The first architecture audit found 27 new XML layout violations and a stale-default-bin public-contract classification. XML layout is corrected; #41 tracks the SDK-owned validation correction before implementation. + +- `e04/keyboard-panels-mode.trx`: 14 passed. Actual Hierarchy, Browser list/tree, Console search/copy and Stats Panels use public SDK/native contexts and public domain operations. The tree regression includes non-leaf nodes and verifies that Ctrl+Space does not expand them; an independently focused native overlay button retains keyboard activation. The Console fixture installs an owned clipboard transport through public ImGui PlatformIO callbacks, without modifying the user's OS clipboard. +- Early embedded-Panel fixtures omitted the standard Workbench setting contributions and the Edit-session identity domain. These were corrected to match the explicit host composition; production validation was not relaxed. Asset entry references are re-resolved after initial source reconciliation. All earlier failure logs remain local evidence. +- Iterative native runs used standard SDK test discovery/build plus VSTest against the already prepared native deployment. The final candidate still requires a complete normal SDK build/product run; these iterations are not the final revision matrix. +- `cache-maintenance-preview.log` and `cache-maintenance.log` record the standard cache maintenance entry. It reported 10,071,041,837 logical bytes reclaimed; C: free space increased from about 95.14 to 97.12 GiB. No unknown owner, SDK/NuGet global cache or user content was removed. The large owned MSBuild diagnostic was hash-verified and compressed locally; its private archive is not a publication artifact. + + +## SDK and presentation implementation progress (2026-10-28) + +- PR head `b7a8924d` completed all six remote checks successfully, including actual Windows and macOS native jobs. Uncommitted candidate changes are not covered by that result. +- `e05/panels-current.trx`: 60 passing native cases, including 56 simulated DPI/Zoom/window-size combinations, non-Shader pointer/History and Console search. This is real ImGui Context geometry/input acceptance, not physical-display or GPU visual proof. +- `e05/graph-lifecycle-current.trx`: 18 passing graph contracts, including 100 close/reopen cycles with weak payload collection and 10 consecutive successful or rejected assembly publications through the strict generation barrier. +- `e19/sdk-inputs-current.trx`: seven passing cases after source-owner SDK selection. `e19/apphost-sdk-corrected.trx` verifies a real direct apphost with no DOTNET_HOST_PATH and an unrelated PATH. A later runtime-installation-shape guard still requires the final rerun. +- `source-current.log`: complete SourceDebug solution build passed with zero warnings/errors. Later candidate refinements require the current second build and final rerun. +- The unified `Invoke-EditorUiAcceptance.ps1` uses existing VSTest and isolated execution, rejects empty selected projects, records SDK/candidate state and creates fresh evidence operations. Its first Lifecycle run passed three cases; the expanded Shader lifecycle profile must rerun. + +## Root ordering and visible-view work (2026-10-28) + +[#42](https://github.com/Wolfiora/InnoEngine/issues/42) was created before the additional Scene owner fix. A real sampling stack identified repeated List.IndexOf inside sibling sorting. Twenty-two public Scene/Transform regressions passed; the root-position index reduced the 10000-root median from approximately 3.7 seconds to 156 ms. A public read-only hierarchyRevision is now justified by the actual flattened-view consumer and never enters persistence. + +`e07/large-views-after.json` records five first-round medians around 140 ms for 10000 asset directories and 3.7 seconds for hierarchy. `large-hierarchy-indexed.json` records the next 156 ms result. These are intermediate results with separate fixture identities, not the completed paired E11 matrix. Flat row submission, neutral-ID view reuse and further measurements remain under #34/#38. + +Current source implements cached ID/depth rows for Hierarchy and File Browser, iterative traversal, SDK-owned hierarchy invalidation and native clipper submission. Asset table/grid snapshots contain IDs and neutral display values, never cached live AssetFileEntry objects. Fifty-two widget/viewport regressions and six focused native keyboard/Graph workflows passed before the final Browser cache changes; those changes require the next replay. + +- `e19/sdk-inputs.trx`: seven passing C# 14, cross-plugin IDE, content invalidation, SDK failure/cancellation and immutable-input cases. A subsequent refinement selects the SDK from the source-owner directory before creating its private evaluation project; the final suite must rerun it. +- Compiler owns `IScriptCompilationEnvironmentProvider` and immutable `ScriptCompilationInputs`; the Build provider maps the existing SDK resolver and Core IO process-owned directories into that boundary. Compiler does not reference Build, MSBuild or platforms. Framework pack scanning was removed from its resolver. IDE projects use the same explicit references and symbols and reject a different SDK. +- `e41/tooling-regressions-corrected.trx`: 92 passing related symbol/Wiki/SDK output-owner cases. Explicit `--artifacts-path` is forwarded to SDK graph and Wiki evaluation; public contract inspection consumes evaluated TargetPath. SDK Wiki framework references no longer come from the validator runtime. Non-SDK inspection policy and the final repository-wide result remain under review. +- `e05/pointer.trx`: 13 neutral Graph geometry, pointer, Missing-port, read-only and cancellation contracts passed. `e05/native-nodes.trx`: a real non-Shader node consumer restores its native clip after an extension exception and culls offscreen callbacks across three frames. Shader now uses the same neutral geometry, hit, selection and pointer mechanisms; its semantics and History application remain in the Shader owner. Full document/clipboard/reload journeys remain pending. +- `e07/widgets-clipping.trx`: 45 related real-native Widget and renderer-capability cases passed. This includes 10000 tree rows, explicit retained clipped rows, clipper callback failure cleanup and both renderer-capability values. Full Panel/scale/retirement checks must still rerun the candidate. + +## Large-view baseline and confirmed failure + +The first unbounded workload aborted the native host with CRT exit code 3. A diagnostic-only startup hook under the evidence owner redirected CRT assertion reporting to stderr without removing assertions or changing engine/vendor/package code. `e07-crash/hierarchy-assertion.log` records Dear ImGui's 16-bit vertex limit assertion. The diagnostic setup follows [Microsoft's CRT error-output contract](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/set-error-mode?view=msvc-170). + +Two distinct responsibilities were identified: clipped ordinary tree rows still submitted custom geometry, and the existing SDL/BGFX draw consumers applied command vertex offsets without the context advertising that capability. The selected renderer now reports it through its existing SPI. Headless performance hosts explicitly validate every indexed draw command before claiming that capability; they do not represent GPU or physical-display acceptance. + +`e11-baseline/large-views-before.trx` passed both synthetic workloads with five repetitions of ten measured frames, after five warmup frames. The raw samples are in `large-views-before.json`. Median samples were approximately 5.5 seconds/frame and 263 MB/frame for 10000 asset-directory rows, and 3.2 seconds/frame and 58 MB/frame for 10000 hierarchy rows. These are pre-clipping observations, not acceptable final performance. The original pre-refactor comparison, controlled paired runs, other workloads and final product measurements are still required. + +The exact pre-clipping native test runtime was copied (not hardlinked) to `e11-baseline/runtime-before`; `runtime-before-manifest.json` verifies all 275 files (70,661,631 bytes) by SHA-256. The measured fixture names/counts are deterministic, but persistent IDs were generated by each authoring owner; a stricter frozen-input paired replay remains required before E11 can close. + +Current deep review: display caches retain neutral IDs, geometry and strings; frame callbacks are not cached. Selection and drag sources continue to resolve through Core Identity; History remains authoritative in existing feature controllers. SDK execution reuses the existing process runner and Core IO ownership instead of adding a second resolver or temporary-directory protocol. No BGCS, package, generated binding or third-party source was changed. These findings do not substitute for the outstanding full acceptance matrix. + +## Additional verified evidence and corrections (2026-10-28) + +- `273c42ad` fixes [#43](https://github.com/Wolfiora/InnoEngine/issues/43) in the AssetFileSystem owner: an unchanged refresh preserves existing Core identities, preparation failure preserves the previous index, and only removed entries retire. The real FlappyBird Browser-empty-after-Play failure and two native regressions exposed the defect. Asset Pipeline has 121 passes; six related native Browser/Scene cases passed. Final real-product replay is still required. +- `e16/loaded-package-shared.trx` has three passes for default output, isolated output without default assets, and isolated output beside deliberately stale default assets. `e16/loaded-package-isolated.trx` has one fresh-host pass. Content-mismatched test copies with the same assembly name/MVID are actually loaded and rejected; full GC/finalizer/full GC confirms their ALC retirement. Original packages remain unchanged. +- `e01/window-geometry.trx` has 49 passes. Copied window/clip bounds and draw-list construction require an active native frame. Native getters retain their write-access side effects; no private native structure is dereferenced before that guard. +- `e19/sdk-contract-final.trx` has 11 passes, including C# 14 Runtime/IDE agreement and cross-plugin extensions. IDE input guards also check implicit configuration defines, because the SDK can otherwise append DEBUG after imports. +- `e05/graph-final.trx` has 26 passes. Shift-box selection preserves its original selection; native generic-node and Shader workflows remain separately tracked. +- Console popup reporting was corrected: requested capacity 1 is normalized to 16. The first attempted eviction test therefore did not evict anything. The corrected test proves the original occurrence is absent before checking popup retirement; both clear and real eviction pass against the existing implementation (`e07/console-retired-real-before.trx`). The speculative production change was withdrawn. Retained-window diagnostics now report the actual record count. +- The original `b303d9f1` production source builds unchanged. Test-only harness adaptations supply equivalent public fixtures; `e11/preflight-original-hierarchy-3.trx` and `preflight-original-assets.trx` pass with shared frozen input. Their concurrent-build timings are preparation evidence, not controlled final performance results. +- `e41/current-wiki-refresh.log` reports SDK ownership for 241/241 projects and a passing architecture/Wiki gate. API pages were then refreshed from that inventory; the final changed state still requires its gate. +- `source-final.log/binlog` records a SourceDebug solution build with zero compile errors before the remaining style ownership cleanup. `contracts-interrupted.json` explicitly invalidates an incomplete no-build Contracts run: native deployment had not been prepared, and a subsequent overlapping discovery rebuild encountered loaded-output locks. Both owned operations were stopped. The unified build now includes native test preparation and stages test temporary directories under its result owner; this does not change production file-lock protocols. + +No item above closes its issue by itself. The PR remains Draft until the complete applicable matrix passes on the final candidate. + + +## Candidate verification update (2026-10-28 06:22 +08:00) + +- Unified entry: Workflows 297 passed, Visual 135 passed, Lifecycle 5 passed. These profiles report selected automated contracts, not the entire issue matrix. +- The final D3D12 FlappyBird acceptance copy verified actual Play/stop, the recovered asset Browser after refresh, Settings edit/cancel/reopen, and normal resource retirement (exit 0). Screenshots: `e09/flappybird-final-play-stop-browser.png` and `e09/flappybird-final-settings-cancel-reopen.png`. +- `e03/all-native-frame-contracts.trx`: all 72 ImGui contracts passed. Production presentation now has an explicit pre-frame callback and owned failure retirement; Session metrics commit before frame creation. +- `e16/loaded-package-shared.trx`: 3 passed; the isolated loaded-package mismatch contract also passed. Negative content belongs only to test-owned copies, never global NuGet or BGCS files. +- The first full Contracts attempt was interrupted and is invalid: a test output was missing its native deployment and an overlapping test compilation encountered the live testhost's file handle. The unified builder now supplies the actual test product selection. The diagnostic dump was retired after preserving its text analysis; no production assertion, timeout or file-lock protocol was relaxed. +- The Console eviction premise was corrected to its actual minimum capacity. Both real clear/evict cases pass against the existing focus logic; a speculative production focus change was withdrawn. diff --git a/src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs b/src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs index be26568bf..85680d068 100644 --- a/src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs +++ b/src/adapters/presentation/Inno.Adapter.Presentation/IPresentationContext.cs @@ -65,7 +65,20 @@ bool TryCaptureLayout( /// /// Callback that emits presentation commands for the current frame. /// - void RenderFrame(Action drawFrame); + /// + /// Optional synchronous preparation after the owned context and display metrics are selected, + /// before frame creation. Failure prevents drawing and no partial frame is published. + /// + /// + /// The draw callback is null. + /// + /// + /// Another frame operation is active on this context. + /// + void RenderFrame( + Action drawFrame, + Action? prepareFrame = null + ); /// /// Registers a persistent render texture for presentation drawing. diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs index 4579a1559..ce91feaeb 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteraction.cs @@ -3,8 +3,14 @@ namespace Inno.Editor.Interactions; /// -/// Provides a lightweight fluent handle for one interaction area and optional target. +/// Provides a fluent handle that borrows an optional target for a synchronous interaction request. /// +/// +/// A target-bearing handle retains that target while it is in use. Do not cache it across frames, +/// asynchronous work, or extension generations. Resolve live identity objects through Core Identity +/// before creating a fresh handle. Focus and deferred routing capture their own identity or weak reference. +/// Target-free handles can be reused within their owning interaction runtime. +/// public readonly struct EditorInteraction { private readonly EditorInteractions m_interactions; @@ -24,7 +30,7 @@ internal EditorInteraction( /// public string area { get; } /// - /// Gets the optional target represented by this handle. + /// Gets the borrowed target without extending its validity or resolving a replacement generation. /// public object? target { get; } /// diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs index 98da7c301..5cd3b36d6 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Runtime/EditorInteractions.cs @@ -62,6 +62,11 @@ Logger log /// public EditorSelectionState selection { get; } + /// + /// Gets the immutable product keyboard policy used by commands and selection controls. + /// + public EditorKeyboardPolicy keyboard => m_editor.keyboard; + object? IEditorSelectionCoordinator.selectedTarget => selection.selectedTarget; /// /// Gets the transactional Undo and Redo history owned by this editor runtime. @@ -233,7 +238,7 @@ public void ToggleSelection(IdentityObject target) /// Selects the inclusive visible range between the current anchor and the clicked target. /// /// - /// Targets in the current visible sort and filter order. + /// Runtime identities in the current visible sort and filter order, resolved through Core Identity only when selected. /// /// /// The clicked range endpoint, which becomes primary. @@ -247,8 +252,11 @@ public void ToggleSelection(IdentityObject target) /// /// The endpoint is not in the visible target order. /// + /// + /// A range identity is unavailable; the current selection remains unchanged. + /// public void SelectRange( - IReadOnlyList orderedTargets, + IReadOnlyList orderedTargets, IdentityObject target, bool additive = false ) { @@ -259,9 +267,9 @@ public void SelectRange( int last = -1; for (int i = 0; i < orderedTargets.Count; i++) { - if (ReferenceEquals(orderedTargets[i], anchor)) + if (orderedTargets[i] == anchor.identity.runtimeIdentity) first = i; - if (ReferenceEquals(orderedTargets[i], target)) + if (orderedTargets[i] == target.identity.runtimeIdentity) last = i; } @@ -276,10 +284,12 @@ public void SelectRange( var values = new List(); if (additive) _ = selection.CopyTargets(values); + var included = new HashSet(values, ReferenceEqualityComparer.Instance); for (int i = Math.Min(first, last); i <= Math.Max(first, last); i++) { - IdentityObject value = orderedTargets[i]; - if (!ReferenceEquals(value, target) && !values.Contains(value)) + if (!TryResolveIdentity(orderedTargets[i], out IdentityObject? value) || value is null) + throw new InvalidOperationException("The visible selection range contains an unavailable identity."); + if (!ReferenceEquals(value, target) && included.Add(value)) values.Add(value); } diff --git a/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs index 3f67cbbe8..93647e5cb 100644 --- a/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs +++ b/src/composition/editor/framework/Inno.Editor.Interactions/Selection/EditorSelectionState.cs @@ -11,6 +11,7 @@ public sealed class EditorSelectionState { private readonly EditorInteractions m_interactions; private EditorTargetReference[] m_targets = []; + private HashSet m_identityMembership = []; private EditorTargetReference? m_anchor; private EditorSelectionSnapshot? m_snapshot; internal EditorSelectionState(EditorInteractions interactions) @@ -48,6 +49,8 @@ public bool Contains(object? target) { if (target is null) return false; + if (target is IdentityObject identity) + return identity.identity.runtimeIdentity is RuntimeIdentity runtime && m_identityMembership.Contains(runtime); foreach (EditorTargetReference entry in m_targets) { if (entry.Matches(m_interactions, target)) @@ -157,7 +160,7 @@ internal void Replace( return; m_targets = entries.ToArray(); m_anchor = nextAnchor; - Changed(); + Changed(seen); } internal void Clear() @@ -199,8 +202,16 @@ internal void Rebind() Changed(); } - private void Changed() + private void Changed(HashSet? identities = null) { + if (identities is null) + { + identities = new HashSet(m_targets.Length); + foreach (EditorTargetReference target in m_targets) + if (target.runtimeIdentity is RuntimeIdentity identity) + identities.Add(identity); + } + m_identityMembership = identities; m_snapshot = null; version++; } diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorAuthoringServices.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorAuthoringServices.cs index 3c0fafae3..bfad6ab29 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorAuthoringServices.cs +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorAuthoringServices.cs @@ -1,4 +1,5 @@ using Inno.Core.IO; +using Inno.Build; using System; using System.IO; using System.Linq; @@ -47,7 +48,8 @@ ScriptCompiler compiler internal static EditorAuthoringServices Start( string projectDirectory, - EngineHost engineHost + EngineHost engineHost, + string dotnetHost ) { ArgumentException.ThrowIfNullOrWhiteSpace(projectDirectory); ArgumentNullException.ThrowIfNull(engineHost); @@ -114,7 +116,8 @@ .. PluginSourceService.GetActivatableMounts(pluginScan) var compiler = new ScriptCompiler( new ScriptCompilerOptions { - projectRootDirectory = projectRoot + projectRootDirectory = projectRoot, + compilationEnvironment = new SdkScriptCompilationEnvironment(dotnetHost, projectRoot) }, assets, plugins); diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs index 37fdbb303..c5ebc26bf 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs @@ -297,7 +297,7 @@ private void Initialize() m_resources.Register(() => engineHost.logs.UnregisterSink(hostLog)); } EditorAuthoringServices activeAuthoring = m_resources.Acquire( - () => EditorAuthoringServices.Start(projectDirectory, engineHost), + () => EditorAuthoringServices.Start(projectDirectory, engineHost, m_launch.buildContext.dotnetHost), static services => services.Dispose()); m_authoring = activeAuthoring; _ = m_resources.Acquire( diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs index b5c5804f8..e850923d7 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorLayer.cs @@ -22,6 +22,8 @@ internal sealed class EditorLayer : Layer private readonly EditorProjectDiagnosticPublisher m_diagnostics = new(); private readonly EditorPlayModeLoop m_playModeLoop = new(); private readonly ImGuiEditorRuntime m_runtime; + private readonly Action m_drawFrame; + private readonly Action m_prepareFrame; private readonly Logger m_log; private readonly LifetimeScope m_resources = new(); private bool m_isShutdownPrepared; @@ -39,6 +41,8 @@ internal EditorLayer( m_log = logs.CreateLogger(); m_resources.Own(m_diagnostics); m_runtime = m_resources.Own(new ImGuiEditorRuntime(context, types, logs, CreateHostServices(hostServices, m_playModeLoop))); + m_drawFrame = m_runtime.Draw; + m_prepareFrame = m_runtime.PrepareFrame; } internal int panelCount => m_runtime.panelCount; @@ -85,7 +89,7 @@ public override void OnDetach() public override void OnLateUpdate(float deltaTime) { m_runtime.Update(new EditorFrame(deltaTime, m_totalTime, isFocused)); - m_presentation.RenderFrame(m_runtime.Draw); + m_presentation.RenderFrame(m_drawFrame, m_prepareFrame); SaveLayoutIfChanged(); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs index 4fe8acd30..af616c7e4 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserData.cs @@ -3,9 +3,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Diagnostics.CodeAnalysis; +using Inno.Core.Identity; using Inno.Assets; using Inno.Assets.Pipeline; -using Inno.Editor.Core; namespace Inno.Editor.Panel.FileBrowser; internal enum FileBrowserEntryTypeFilter @@ -22,18 +23,27 @@ internal enum FileBrowserEntryScopeFilter } internal readonly record struct FileBrowserDisplayEntry( - AssetFileEntry entry, + RuntimeIdentity identity, + AssetPath path, string displayName, + string extension, + bool isDirectory, bool isPluginRoot = false ); internal sealed class FileBrowserData(AssetBrowserModule assets) { + private ViewInput? m_input; + private IReadOnlyList m_entries = Array.Empty(); + internal IReadOnlyList visibleSelection { get; private set; } = Array.Empty(); + internal IReadOnlyList CollectVisibleEntries( - EditorContext context, FileBrowserEntryTypeFilter typeFilter, FileBrowserEntryScopeFilter scopeFilter, string searchFilter ) { + var input = new ViewInput(assets.edits.pipeline.revision, assets.browser.root, assets.browser.currentDirectory, typeFilter, scopeFilter, searchFilter); + if (m_input == input) + return m_entries; List entries = []; if (assets.browser.root == AssetBrowserRoot.Plugins && string.IsNullOrEmpty(assets.browser.currentDirectory)) { @@ -43,7 +53,7 @@ string searchFilter { IReadOnlyList children = GetVisibleChildren(assets.browser.currentDirectory); for (int i = 0; i < children.Count; i++) - entries.Add(new FileBrowserDisplayEntry(children[i], children[i].name)); + entries.Add(Describe(children[i], children[i].name)); } else CollectEntriesRecursive(assets.browser.currentDirectory, entries); @@ -57,12 +67,36 @@ string searchFilter int byName = string.Compare(left.displayName, right.displayName, StringComparison.OrdinalIgnoreCase); if (byName != 0) return byName; - int byExtension = string.Compare(left.entry.extension, right.entry.extension, StringComparison.OrdinalIgnoreCase); - return byExtension != 0 ? byExtension : string.CompareOrdinal(left.entry.assetPath.ToString(), right.entry.assetPath.ToString()); + int byExtension = string.Compare(left.extension, right.extension, StringComparison.OrdinalIgnoreCase); + return byExtension != 0 ? byExtension : string.CompareOrdinal(left.path.ToString(), right.path.ToString()); }); - return entries; + visibleSelection = Array.AsReadOnly(entries.Select(static entry => entry.identity).ToArray()); + m_entries = entries.AsReadOnly(); + m_input = input; + return m_entries; + } + + internal bool TryResolve( + FileBrowserDisplayEntry item, + [NotNullWhen(true)] out AssetFileEntry? entry + ) { + if (assets.edits.interactions.TryResolveIdentity(item.identity, out IdentityObject? target) + && target is AssetFileEntry resolved && resolved.assetPath == item.path) + { + entry = resolved; + return true; + } + entry = null; + return false; } + private static FileBrowserDisplayEntry Describe( + AssetFileEntry entry, + string label, + bool pluginRoot = false + ) => new(entry.identity.runtimeIdentity ?? throw new InvalidOperationException("A visible asset requires a registered identity."), + entry.assetPath, label, entry.extension, entry.isDirectory, pluginRoot); + private static void ApplyTypeFilter( List entries, FileBrowserEntryTypeFilter typeFilter @@ -70,10 +104,10 @@ FileBrowserEntryTypeFilter typeFilter switch (typeFilter) { case FileBrowserEntryTypeFilter.FoldersOnly: - entries.RemoveAll(static item => !item.entry.isDirectory); + entries.RemoveAll(static item => !item.isDirectory); break; case FileBrowserEntryTypeFilter.FilesOnly: - entries.RemoveAll(static item => item.entry.isDirectory); + entries.RemoveAll(static item => item.isDirectory); break; } } @@ -103,7 +137,7 @@ FileBrowserEntryScopeFilter scopeFilter var rootPath = new AssetPath(mount.id, string.Empty); if (!assets.edits.pipeline.TryGetFileSystemEntry(rootPath, out AssetFileEntry root)) continue; - entries.Add(new FileBrowserDisplayEntry(root, mount.id.value, isPluginRoot: true)); + entries.Add(Describe(root, mount.id.value, pluginRoot: true)); if (scopeFilter == FileBrowserEntryScopeFilter.Recursive) CollectEntriesRecursive(root.assetPath.ToString(), entries); } @@ -113,13 +147,16 @@ private void CollectEntriesRecursive( string directory, List entries ) { - IReadOnlyList children = GetVisibleChildren(directory); - for (int i = 0; i < children.Count; i++) + var pending = new Stack(); + pending.Push(directory); + while (pending.TryPop(out string? current)) { - AssetFileEntry child = children[i]; - entries.Add(new FileBrowserDisplayEntry(child, child.name)); - if (child.isDirectory) - CollectEntriesRecursive(child.assetPath.ToString(), entries); + foreach (AssetFileEntry child in GetVisibleChildren(current)) + { + entries.Add(Describe(child, child.name)); + if (child.isDirectory) + pending.Push(child.assetPath.ToString()); + } } } @@ -153,4 +190,13 @@ internal IReadOnlyList GetVisibleChildren(string relativePath) return isolated; } + + private readonly record struct ViewInput( + long revision, + AssetBrowserRoot root, + string directory, + FileBrowserEntryTypeFilter type, + FileBrowserEntryScopeFilter scope, + string search + ); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs index c64592a05..917a7cb7f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Grid.cs @@ -1,8 +1,6 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.Assets; using Inno.Editor.Assets.ImGui; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; @@ -23,13 +21,17 @@ private void DrawGridRegion( EditorContext context, IReadOnlyList entries ) { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float sliderHeight = Ui.GetFrameHeight() + style.windowPadding.Y * 2f + style.itemSpacing.Y; bool entriesVisible = Ui.BeginChild("##EntriesScroll", new Vector2(0f, -sliderHeight), ImGuiChildFlags.None); try { if (entriesVisible) { + float cellSize = GetGridCellSize(); + int columns = Math.Max(1, (int)(Ui.GetContentRegionAvailable().X / cellSize)); + int rows = Math.Max(1, (int)(Ui.GetContentRegionAvailable().Y / cellSize)); + HandleEntryKeyboardNavigation(columns * rows); DrawGrid(context, entries); HandleBackgroundSelection(context); m_contextMenu.DrawBackground(context, "##asset_grid_background_context", FileBrowserPresentation.Grid); @@ -55,7 +57,7 @@ private void HandleBackgroundSelection(EditorContext context) private void DrawGridScaleSlider() { - m_gridScale = Math.Clamp(m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); + m_gridScale = Math.Clamp(m_gridScale, CurrentBrowserStyle.gridMinimumScale, CurrentBrowserStyle.gridMaximumScale); DrawFooterTopSplitter(); Vector2 cursor = Ui.GetCursorPosition(); float labelOffsetY = MathF.Max(0f, (Ui.GetFrameHeight() - Ui.GetTextLineHeight()) * 0.5f); @@ -64,8 +66,8 @@ private void DrawGridScaleSlider() Ui.SameLine(0f, -1f); Ui.SetCursorPosY(cursor.Y); Ui.SetNextItemWidth(-1f); - _ = Ui.SliderFloat("##GridScale", ref m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); - m_gridScale = Math.Clamp(m_gridScale, EditorUI.current.widgets.style.assetGridMinimumScale, EditorUI.current.widgets.style.assetGridMaximumScale); + _ = Ui.SliderFloat("##GridScale", ref m_gridScale, CurrentBrowserStyle.gridMinimumScale, CurrentBrowserStyle.gridMaximumScale); + m_gridScale = Math.Clamp(m_gridScale, CurrentBrowserStyle.gridMinimumScale, CurrentBrowserStyle.gridMaximumScale); } private void DrawGrid( @@ -86,11 +88,28 @@ IReadOnlyList entries return; try { - for (int i = 0; i < entries.Count; i++) + int rowCount = (entries.Count + columns - 1) / columns; + Span preserved = stackalloc int[3]; + int preservedCount = CollectPreservedEntryRows(entries, preserved, columns); + m_ui.widgets.ClippedRows(rowCount, -1f, (panel: this, context, entries, columns), static ( + state, + start, + end + ) => { - Ui.TableNextColumn(); - DrawGridItem(context, entries[i]); - } + for (int row = start; row < end; row++) + { + Ui.TableNextRow(); + for (int column = 0; column < state.columns; column++) + { + int index = row * state.columns + column; + if (index >= state.entries.Count) + break; + _ = Ui.TableSetColumnIndex(column); + state.panel.DrawGridItem(state.context, state.entries[index]); + } + } + }, preserved[..preservedCount]); } finally { @@ -102,13 +121,17 @@ private void DrawGridItem( EditorContext context, FileBrowserDisplayEntry item ) { - AssetFileEntry entry = item.entry; float cellSize = GetGridCellSize(); + if (!m_data.TryResolve(item, out AssetFileEntry? entry)) + { + Ui.Dummy(new(cellSize, cellSize)); + return; + } string icon = m_assets.GetIcon(entry); string name = item.isPluginRoot ? item.displayName : entry.name; bool selected = m_assets.browser.IsSelected(context, entry); bool editing = m_rename.IsEditing(context, entry.assetPath.ToString(), FileBrowserPresentation.Grid); - Vector2 itemSize = new(cellSize - EditorUI.current.widgets.style.assetGridCellPadding, cellSize - EditorUI.current.widgets.style.assetGridCellPadding); + Vector2 itemSize = new(cellSize - CurrentBrowserStyle.gridCellPadding, cellSize - CurrentBrowserStyle.gridCellPadding); Ui.PushId(entry.assetPath.ToString()); Ui.PushStyleColor(ImGuiCol.Header, EditorPalette.transparent); Ui.PushStyleColor(ImGuiCol.HeaderHovered, EditorPalette.transparent); @@ -118,6 +141,7 @@ FileBrowserDisplayEntry item ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags.AllowDoubleClick; if (editing) selectableFlags |= ImGuiSelectableFlags.AllowOverlap; + RequestEntryKeyboardFocus(entry); bool activated = Ui.Selectable("##GridItem", selected, selectableFlags, itemSize); bool itemHovered = Ui.IsItemHovered(); bool itemActive = Ui.IsItemActive(); @@ -125,7 +149,7 @@ FileBrowserDisplayEntry item Vector2 itemMax = Ui.GetItemMaximum(); Vector2 layoutCursor = Ui.GetCursorScreenPosition(); bool doubleClicked = itemHovered && Ui.IsMouseDoubleClicked(ImGuiMouseButton.Left); - if (activated) + if (activated && !m_keyboardHandled) { HandleEntryActivation(context, entry, FileBrowserPresentation.Grid, doubleClicked); } @@ -141,13 +165,13 @@ FileBrowserDisplayEntry item DrawGridItemVisual(icon, name, selected, m_gridScale, itemHovered, itemActive, itemMin, itemMax, drawName: !editing); if (editing) { - float width = MathF.Max(1f, itemMax.X - itemMin.X - EditorUI.current.widgets.style.assetGridLabelHorizontalPadding); + float width = MathF.Max(1f, itemMax.X - itemMin.X - CurrentBrowserStyle.gridLabelHorizontalPadding); float lineHeight = Ui.GetTextLineHeight(); - float lineAdvance = MathF.Max(1f, lineHeight + EditorUI.current.widgets.style.assetGridLabelLineSpacing); + float lineAdvance = MathF.Max(1f, lineHeight + CurrentBrowserStyle.gridLabelLineSpacing); float labelAreaHeight = lineHeight + lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); - float labelAreaTop = itemMax.Y - EditorUI.current.widgets.style.assetGridLabelBottomPadding - labelAreaHeight; + float labelAreaTop = itemMax.Y - CurrentBrowserStyle.gridLabelBottomPadding - labelAreaHeight; float y = labelAreaTop + (labelAreaHeight - lineHeight) * 0.5f; - Ui.SetCursorScreenPosition(new Vector2(itemMin.X + EditorUI.current.widgets.style.assetGridLabelHorizontalPadding * 0.5f, y)); + Ui.SetCursorScreenPosition(new Vector2(itemMin.X + CurrentBrowserStyle.gridLabelHorizontalPadding * 0.5f, y)); m_rename.Draw(context, $"grid_{entry.assetPath.ToString()}", entry.assetPath.ToString(), FileBrowserPresentation.Grid, width, lineHeight); Ui.SetCursorScreenPosition(layoutCursor); Ui.Dummy(Vector2.Zero); @@ -172,27 +196,27 @@ private static void DrawGridItemVisual( bool drawName ) { Vector2 size = max - min; - Vector4 bg = selected ? EditorPalette.assetAccent : EditorPalette.collectionHeader; + Vector4 bg = selected ? EditorPalette.contentAccent : EditorPalette.collectionHeader; if (active) bg = EditorPalette.GetActive(bg); else if (hovered) bg = EditorPalette.GetHovered(bg); uint bgColor = Ui.ToPackedColor(bg); - EditorImGui.DrawFilledRectangle(min, max, bgColor, EditorUI.current.widgets.style.assetFrameRounding); + Ui.DrawFilledRectangle(min, max, bgColor, CurrentBrowserStyle.frameRounding); float fontSize = Ui.GetFontSize(); float iconFontSize = fontSize * scale; Vector4 iconBounds = EditorUI.current.widgets.GetGlyphVisualBounds(iconFontSize, icon); Vector2 iconSize = new(iconBounds.Z - iconBounds.X, iconBounds.W - iconBounds.Y); - string[] nameLines = FitTextToLines(name, MathF.Max(1f, size.X - EditorUI.current.widgets.style.assetGridLabelHorizontalPadding), C_GRID_LABEL_LINE_COUNT); + string[] nameLines = FitTextToLines(name, MathF.Max(1f, size.X - CurrentBrowserStyle.gridLabelHorizontalPadding), C_GRID_LABEL_LINE_COUNT); float lineHeight = Ui.GetTextLineHeight(); - float lineAdvance = MathF.Max(1f, lineHeight + EditorUI.current.widgets.style.assetGridLabelLineSpacing); + float lineAdvance = MathF.Max(1f, lineHeight + CurrentBrowserStyle.gridLabelLineSpacing); float labelAreaHeight = lineHeight + lineAdvance * (C_GRID_LABEL_LINE_COUNT - 1); - float labelAreaTop = max.Y - EditorUI.current.widgets.style.assetGridLabelBottomPadding - labelAreaHeight; + float labelAreaTop = max.Y - CurrentBrowserStyle.gridLabelBottomPadding - labelAreaHeight; float labelHeight = lineHeight + lineAdvance * Math.Max(0, nameLines.Length - 1); float labelY = labelAreaTop + (labelAreaHeight - labelHeight) * 0.5f; - float iconAreaTop = min.Y + EditorUI.current.widgets.style.assetGridIconTopPadding; - float iconAreaBottom = MathF.Max(iconAreaTop + 1f, labelAreaTop - EditorUI.current.widgets.style.assetGridIconLabelSpacing); - float maximumIconWidth = MathF.Max(1f, size.X - EditorUI.current.widgets.style.assetGridIconHorizontalPadding * 2f); + float iconAreaTop = min.Y + CurrentBrowserStyle.gridIconTopPadding; + float iconAreaBottom = MathF.Max(iconAreaTop + 1f, labelAreaTop - CurrentBrowserStyle.gridIconLabelSpacing); + float maximumIconWidth = MathF.Max(1f, size.X - CurrentBrowserStyle.gridIconHorizontalPadding * 2f); float maximumIconHeight = MathF.Max(1f, iconAreaBottom - iconAreaTop); float fit = MathF.Min(1f, MathF.Min(maximumIconWidth / MathF.Max(1f, iconSize.X), maximumIconHeight / MathF.Max(1f, iconSize.Y))); if (fit < 1f) @@ -204,14 +228,14 @@ bool drawName float horizontalCenter = min.X + size.X * 0.5f; Vector2 iconAreaCenter = new(horizontalCenter + C_GRID_ICON_HORIZONTAL_OPTICAL_OFFSET * EditorUI.current.widgets.style.zoom, iconAreaTop + maximumIconHeight * 0.5f); - uint textColor = Ui.ToPackedColor(EditorPalette.assetText); + uint textColor = Ui.ToPackedColor(EditorPalette.contentText); Ui.PushClipRect(min, max, true); EditorUI.current.widgets.AddGlyphCentered(iconFontSize, icon, iconAreaCenter, textColor); for (int i = 0; drawName && i < nameLines.Length; i++) { Vector2 lineSize = Ui.MeasureText(nameLines[i]); Vector2 linePosition = new(horizontalCenter - lineSize.X * 0.5f, labelY + lineAdvance * i); - EditorImGui.DrawText(linePosition, textColor, nameLines[i]); + Ui.DrawText(linePosition, textColor, nameLines[i]); } Ui.PopClipRect(); @@ -221,8 +245,8 @@ private float GetGridCellSize() { float fontSize = Ui.GetFontSize(); return MathF.Max( - EditorUI.current.widgets.style.assetGridMinimumCellSize, - fontSize * (m_gridScale + EditorUI.current.widgets.style.assetGridScaleBias) + EditorUI.current.widgets.style.assetGridFixedCellPadding + CurrentBrowserStyle.gridMinimumCellSize, + fontSize * (m_gridScale + CurrentBrowserStyle.gridScaleBias) + CurrentBrowserStyle.gridFixedCellPadding ); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs index 56c9ec60f..56dc7d379 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Layout.cs @@ -1,8 +1,7 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.Assets; +using Inno.Core.Identity; using Inno.Editor.Assets.ImGui; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; @@ -22,7 +21,7 @@ internal sealed partial class FileBrowserPanel private void DrawBrowser(EditorContext context) { m_navigation.SyncExternalDirectoryChange(m_assets.browser.root, m_assets.browser.currentDirectory); - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float breadcrumbBarHeight = GetBreadcrumbBarHeight(m_assets.browser.root, m_assets.browser.currentDirectory); Vector2 bodySize = new(0f, -(breadcrumbBarHeight + style.itemSpacing.Y)); bool mainVisible = Ui.BeginChild("##FileBrowserMain", bodySize, ImGuiChildFlags.None, (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); @@ -76,7 +75,7 @@ private void DrawBrowser(EditorContext context) private static float GetTreeSplitterWidth(EditorUiLayout style) { - return MathF.Max(EditorUI.current.widgets.style.assetSplitterMinimumWidth, style.dockingSeparatorSize); + return MathF.Max(EditorUI.current.widgets.style.splitterMinimumWidth, style.dockingSeparatorSize); } private void DrawTreeSplitter( @@ -103,8 +102,8 @@ float treeWidth Vector2 min = Ui.GetItemMinimum(); Vector2 max = Ui.GetItemMaximum(); - Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; - EditorImGui.DrawFilledRectangle(min, max, Ui.ToPackedColor(color), 0f); + Vector4 color = active ? EditorPalette.contentAccent : hovered ? EditorPalette.contentBorder : EditorPalette.contentBorderSoft; + Ui.DrawFilledRectangle(min, max, Ui.ToPackedColor(color), 0f); } private static float ClampTreeWidth( @@ -113,7 +112,7 @@ private static float ClampTreeWidth( float splitterWidth ) { float combinedPaneWidth = MathF.Max(0f, availableWidth - splitterWidth); - float minimumPaneWidth = MathF.Min(EditorUI.current.widgets.style.assetPaneMinimumVisibleWidth, combinedPaneWidth * 0.5f); + float minimumPaneWidth = MathF.Min(EditorUI.current.widgets.style.paneMinimumVisibleWidth, combinedPaneWidth * 0.5f); float maximumTreeWidth = MathF.Max(minimumPaneWidth, combinedPaneWidth - minimumPaneWidth); return Math.Clamp(requestedWidth, minimumPaneWidth, maximumTreeWidth); } @@ -141,7 +140,7 @@ private void DrawTreePane(EditorContext context) Ui.PushStyleColor(ImGuiCol.ChildBg, EditorPalette.collectionHeader); try { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); Vector2 treePaneSize = new(-style.windowPadding.X, 0f); bool treeVisible = Ui.BeginChild("##TreePane", treePaneSize, ImGuiChildFlags.None, (ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)); try @@ -157,10 +156,8 @@ private void DrawTreePane(EditorContext context) m_tree.PrepareOpenRequests(); try { - if (m_assets.browser.root == AssetBrowserRoot.Assets) - m_tree.DrawEntry(context, string.Empty, "Assets", true); - else - m_tree.DrawPluginRoot(context, m_assets.edits.pipeline.sourceMounts); + m_tree.Draw(context); + m_tree.CompleteNavigation(context); } finally { @@ -197,7 +194,7 @@ private void DrawRootSwitchFooter(EditorContext context) { AssetBrowserRoot nextRoot = m_assets.browser.root == AssetBrowserRoot.Assets ? AssetBrowserRoot.Plugins : AssetBrowserRoot.Assets; DrawFooterTopSplitter(); - PushButtonColors(EditorPalette.assetAccent); + PushButtonColors(EditorPalette.contentAccent); try { if (Ui.Button($"Switch to {nextRoot}##FileBrowserRoot", new Vector2(-1f, 0f))) @@ -223,7 +220,7 @@ private void DrawContentPane(EditorContext context) if (contentVisible) { DrawToolbar(context); - DrawEntriesRegion(context, m_data.CollectVisibleEntries(context, m_entryTypeFilter, m_entryScopeFilter, m_filter)); + DrawEntriesRegion(context, m_data.CollectVisibleEntries(m_entryTypeFilter, m_entryScopeFilter, m_filter)); } } finally @@ -244,7 +241,10 @@ private void DrawEntriesRegion( EditorContext context, IReadOnlyList entries ) { - m_visibleSelection = entries.Select(value => (Inno.Core.Identity.IdentityObject)value.entry).ToArray(); + m_visibleSelection = m_data.visibleSelection; + m_drawKeyboardFocus = m_requestedKeyboardFocus; + m_requestedKeyboardFocus = null; + m_keyboardHandled = false; try { if (m_viewMode == ViewMode.List) @@ -254,18 +254,25 @@ IReadOnlyList entries } finally { - m_visibleSelection = Array.Empty(); + m_visibleSelection = Array.Empty(); + m_drawKeyboardFocus = null; } + RuntimeIdentity? activation = m_pendingActivation; + m_pendingActivation = null; + if (activation is RuntimeIdentity identity + && m_assets.edits.interactions.TryResolveIdentity(identity, out Inno.Core.Identity.IdentityObject? target) + && target is AssetFileEntry entry) + m_navigation.OpenEntry(context, entry, m_tree); } private static void DrawFooterTopSplitter() { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); Vector2 cursor = Ui.GetCursorScreenPosition(); float width = Ui.GetContentRegionAvailable().X; float lineY = cursor.Y + style.windowPadding.Y; - uint color = Ui.ToPackedColor(EditorPalette.assetBorder); - EditorImGui.DrawLine(new Vector2(cursor.X, lineY), new Vector2(cursor.X + width, lineY), color, EditorUI.current.widgets.style.borderSize); + uint color = Ui.ToPackedColor(EditorPalette.contentBorder); + Ui.DrawLine(new Vector2(cursor.X, lineY), new Vector2(cursor.X + width, lineY), color, EditorUI.current.widgets.style.borderSize); Ui.SetCursorPosY(Ui.GetCursorPosY() + style.windowPadding.Y * 2f); } @@ -273,7 +280,7 @@ private static void DrawBreadcrumbTopSeparator() { Vector2 min = Ui.GetWindowPos(); Vector2 size = Ui.GetWindowSize(); - uint color = Ui.ToPackedColor(EditorPalette.assetBorder); - EditorImGui.DrawLine(min, new Vector2(min.X + size.X, min.Y), color, EditorUI.current.widgets.style.borderSize); + uint color = Ui.ToPackedColor(EditorPalette.contentBorder); + Ui.DrawLine(min, new Vector2(min.X + size.X, min.Y), color, EditorUI.current.widgets.style.borderSize); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs index 7db4d824c..4bc007538 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.List.cs @@ -1,8 +1,6 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.Assets; using Inno.Editor.Assets.ImGui; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.IO; @@ -28,6 +26,8 @@ IReadOnlyList entries { if (entriesVisible) { + float rowHeight = Ui.GetTextLineHeight() + Ui.GetLayout().cellPadding.Y * 2f + CurrentBrowserStyle.listRowSpacing; + HandleEntryKeyboardNavigation(Math.Max(1, (int)(Ui.GetContentRegionAvailable().Y / rowHeight))); DrawEntriesTable(context, entries, m_assets.browser.currentDirectory); HandleBackgroundSelection(context); m_contextMenu.DrawBackground(context, "##asset_list_background_context", FileBrowserPresentation.List); @@ -45,7 +45,7 @@ private void DrawEntriesTable( string currentDirectory ) { ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings; - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); Vector2 tableOrigin = Ui.GetCursorScreenPosition(); Vector2 tableSize = Ui.GetContentRegionAvailable(); Ui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(style.windowPadding.X, style.cellPadding.Y)); @@ -66,19 +66,28 @@ string currentDirectory DrawHeaderRow(); uint rowBg = Ui.ToPackedColor(EditorPalette.collectionRow); uint rowAltBg = Ui.ToPackedColor(EditorPalette.collectionRowAlternate); - float entryRowHeight = Ui.GetTextLineHeight() + style.cellPadding.Y * 2f; - for (int i = 0; i < entries.Count; i++) + float entryRowHeight = Ui.GetTextLineHeight() + style.cellPadding.Y * 2f + CurrentBrowserStyle.listRowSpacing; + Span preserved = stackalloc int[3]; + int preservedCount = CollectPreservedEntryRows(entries, preserved); + m_ui.widgets.ClippedRows(entries.Count, -1f, + (panel: this, context, entries, currentDirectory, rowBg, rowAltBg, entryRowHeight), static ( + state, + start, + end + ) => { - FileBrowserDisplayEntry item = entries[i]; - AssetFileEntry entry = item.entry; - if (i > 0) - Ui.TableNextRow(ImGuiTableRowFlags.None, EditorUI.current.widgets.style.assetListRowSpacing); - Ui.TableNextRow(ImGuiTableRowFlags.None, entryRowHeight); - Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, i % 2 == 0 ? rowBg : rowAltBg); - DrawNameCell(context, item); - DrawTextCell(item.isPluginRoot ? "IPLUGIN" : GetTypeText(entry), EditorPalette.assetText); - DrawTextCell(item.isPluginRoot ? "~" : GetSourceText(entry, currentDirectory), EditorPalette.assetText); - } + for (int index = start; index < end; index++) + { + FileBrowserDisplayEntry item = state.entries[index]; + Ui.TableNextRow(ImGuiTableRowFlags.None, state.entryRowHeight); + if (!state.panel.m_data.TryResolve(item, out AssetFileEntry? entry)) + continue; + Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, index % 2 == 0 ? state.rowBg : state.rowAltBg); + state.panel.DrawNameCell(state.context, item, entry); + DrawTextCell(item.isPluginRoot ? "IPLUGIN" : GetTypeText(entry), EditorPalette.contentText); + DrawTextCell(item.isPluginRoot ? "~" : GetSourceText(entry, state.currentDirectory), EditorPalette.contentText); + } + }, preserved[..preservedCount]); } finally { @@ -107,7 +116,7 @@ Vector2 size ) { float width = MathF.Max(1f, size.X); float height = MathF.Max(1f, size.Y); - float hitWidth = EditorUI.current.widgets.style.assetListSeparatorHitWidth; + float hitWidth = CurrentBrowserStyle.listSeparatorHitWidth; bool nameHovered; bool nameActive; bool typeHovered; @@ -161,15 +170,15 @@ private static void DrawListColumnSeparator( bool hovered, bool active ) { - Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; - EditorImGui.DrawLine(new Vector2(x, top), new Vector2(x, bottom), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); + Vector4 color = active ? EditorPalette.contentAccent : hovered ? EditorPalette.contentBorder : EditorPalette.contentBorderSoft; + Ui.DrawLine(new Vector2(x, top), new Vector2(x, bottom), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); } private void SetListColumnSeparators( float namePosition, float typePosition ) { - float minimum = m_ui.widgets.style.assetListMinimumColumnRatio; + float minimum = browserStyle.listMinimumColumnRatio; m_listNameSeparatorPosition = Math.Clamp(namePosition, minimum, 1f - minimum * 2f); m_listTypeSeparatorPosition = Math.Clamp(typePosition, m_listNameSeparatorPosition + minimum, 1f - minimum); } @@ -192,15 +201,15 @@ private static void DrawHeaderRow() private void DrawNameCell( EditorContext context, - FileBrowserDisplayEntry item + FileBrowserDisplayEntry item, + AssetFileEntry entry ) { - AssetFileEntry entry = item.entry; _ = Ui.TableSetColumnIndex(0); string icon = m_assets.GetIcon(entry); string name = item.isPluginRoot ? item.displayName : entry.nameWithoutExtension; bool selected = m_assets.browser.IsSelected(context, entry); bool editing = m_rename.IsEditing(context, entry.assetPath.ToString(), FileBrowserPresentation.List); - var table = UiDrawing.GetTableBounds(); + var table = Ui.GetTableBounds(); float rowMinimumY = table.rowMinimum; float rowMaximumY = table.rowMaximum; InsetListCellContent(); @@ -211,10 +220,11 @@ FileBrowserDisplayEntry item { Vector2 iconTextPos = Ui.GetCursorScreenPosition(); ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags.SpanAllColumns | ImGuiSelectableFlags.AllowDoubleClick | ImGuiSelectableFlags.AllowOverlap; + RequestEntryKeyboardFocus(entry); bool activated = Ui.Selectable($"##entry_{entry.assetPath.ToString()}", selected, selectableFlags); bool itemHovered = Ui.IsItemHovered(); bool doubleClicked = itemHovered && Ui.IsMouseDoubleClicked(ImGuiMouseButton.Left); - if (activated) + if (activated && !m_keyboardHandled) { HandleEntryActivation(context, entry, FileBrowserPresentation.List, doubleClicked); } @@ -227,7 +237,7 @@ FileBrowserDisplayEntry item if (selected || itemHovered) { - Vector4 highlight = itemActive ? EditorPalette.GetHovered(EditorPalette.assetAccent) : EditorPalette.assetAccent; + Vector4 highlight = itemActive ? EditorPalette.GetHovered(EditorPalette.contentAccent) : EditorPalette.contentAccent; Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, Ui.ToPackedColor(highlight)); } @@ -278,7 +288,7 @@ Vector4 color private static void InsetListCellContent() { - Ui.SetCursorPosX(Ui.GetCursorPosX() + EditorUI.current.widgets.style.assetListContentHorizontalPadding); + Ui.SetCursorPosX(Ui.GetCursorPosX() + CurrentBrowserStyle.listContentHorizontalPadding); } private readonly record struct ListColumnSeparatorState( diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs index 2ba85e2eb..1282efbca 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.Navigation.cs @@ -1,6 +1,7 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.Assets; +using Inno.Core.Identity; +using Inno.Editor.Interactions; using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; @@ -18,6 +19,25 @@ namespace Inno.Editor.Panel.FileBrowser; internal sealed partial class FileBrowserPanel { + private int CollectPreservedEntryRows( + IReadOnlyList entries, + Span rows, + int columns = 1 + ) { + RuntimeIdentity? editing = m_rename.activeTarget; + RuntimeIdentity? dragging = m_assets.edits.interactions.TryGetActiveDragIdentity(out RuntimeIdentity source) ? source : null; + if (editing is null && dragging is null && m_drawKeyboardFocus is null) + return 0; + int count = 0; + for (int index = 0; index < entries.Count; index++) + { + RuntimeIdentity identity = entries[index].identity; + if (identity == editing || identity == dragging || identity == m_drawKeyboardFocus) + rows[count++] = index / columns; + } + return count; + } + private void DrawToolbar(EditorContext context) { DrawNavigationBar(context); @@ -29,22 +49,22 @@ private void DrawNavigationBar(EditorContext context) string current = m_assets.browser.currentDirectory; Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.breadcrumbFramePadding); bool canGoBack = m_navigation.canGoBack; - PushButtonColors(canGoBack ? EditorPalette.assetAccent : EditorPalette.assetBorderSoft); + PushButtonColors(canGoBack ? EditorPalette.contentAccent : EditorPalette.contentBorderSoft); Ui.BeginDisabled(!canGoBack); if (Ui.SmallButton($"{ImGuiIcon.AngleLeft}##Back")) m_navigation.GoBack(context); Ui.EndDisabled(); Ui.PopStyleColor(3); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarTightSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarTightSpacing); bool canGoForward = m_navigation.canGoForward; - PushButtonColors(canGoForward ? EditorPalette.assetAccent : EditorPalette.assetBorderSoft); + PushButtonColors(canGoForward ? EditorPalette.contentAccent : EditorPalette.contentBorderSoft); Ui.BeginDisabled(!canGoForward); if (Ui.SmallButton($"{ImGuiIcon.AngleRight}##Forward")) m_navigation.GoForward(context); Ui.EndDisabled(); Ui.PopStyleColor(3); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSectionSpacing); - Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetText); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarSectionSpacing); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.contentText); Ui.Text(GetDirectoryLabel(m_assets.browser.root, current)); Ui.PopStyleColor(); Ui.PopStyleVar(); @@ -53,13 +73,13 @@ private void DrawNavigationBar(EditorContext context) private void DrawViewAndSearchBar() { Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.toolbarFramePadding); - PushButtonColors(EditorPalette.assetAccent); + PushButtonColors(EditorPalette.contentAccent); if (Ui.SmallButton($"{m_viewMode}##ViewMode")) m_viewMode = m_viewMode == ViewMode.List ? ViewMode.Grid : ViewMode.List; Ui.PopStyleColor(3); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarSpacing); DrawEntryFilterCombo(); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarSpacing); Ui.SetNextItemWidth(-1f); DrawSearchInput(); Ui.PopStyleVar(); @@ -72,15 +92,72 @@ private void HandleEntryActivation( bool doubleClicked ) { m_rename.MarkInteraction(presentation); + m_keyboardFocus = entry.identity.runtimeIdentity; + m_assets.edits.interactions.For(FileBrowserInteractionIds.C_AREA, entry).Focus(); if (doubleClicked) { - m_navigation.OpenEntry(context, entry, m_tree); + m_pendingActivation = entry.identity.runtimeIdentity; return; } EditorUI.current.widgets.SelectTarget(m_assets.edits.interactions, entry, m_visibleSelection); } + private void HandleEntryKeyboardNavigation(int pageSize) + { + EditorInteractions interactions = m_assets.edits.interactions; + int focus = -1; + int selectedIndex = -1; + RuntimeIdentity? selected = (interactions.selection.selectedTarget as IdentityObject)?.identity.runtimeIdentity; + for (int i = 0; i < m_visibleSelection.Count; i++) + { + RuntimeIdentity candidate = m_visibleSelection[i]; + if (candidate == m_keyboardFocus) + { + focus = i; + break; + } + if (candidate == selected) + selectedIndex = i; + } + if (focus < 0) + focus = selectedIndex; + ListNavigationResult navigation = m_ui.widgets.NavigateList(interactions, m_visibleSelection.Count, focus, pageSize); + m_keyboardHandled = navigation.focusChanged || navigation.selectionRequested || navigation.activate || navigation.cancel; + if (navigation.focusedIndex >= 0 && m_keyboardHandled) + { + RuntimeIdentity identity = m_visibleSelection[navigation.focusedIndex]; + if (!interactions.TryResolveIdentity(identity, out IdentityObject? target) || target is null) + return; + m_keyboardFocus = identity; + if (navigation.focusChanged) + { + m_requestedKeyboardFocus = m_keyboardFocus; + m_drawKeyboardFocus = m_keyboardFocus; + } + if (navigation.selectionRequested) + { + if (navigation.extendRange) + interactions.SelectRange(m_visibleSelection, target, navigation.additive); + else if (navigation.toggle) + interactions.ToggleSelection(target); + else + interactions.SetSelection(target); + } + interactions.For(FileBrowserInteractionIds.C_AREA, interactions.selection.selectedTarget).Focus(); + if (navigation.activate) + m_pendingActivation = m_keyboardFocus; + } + if (navigation.cancel) + m_keyboardFocus = null; + } + + private void RequestEntryKeyboardFocus(AssetFileEntry entry) + { + if (entry.identity.runtimeIdentity == m_drawKeyboardFocus && Ui.IsWindowFocused(ImGuiFocusedFlags.ChildWindows)) + Ui.FocusNextItem(); + } + private void DrawBreadcrumbBar( EditorContext context, float height @@ -95,18 +172,18 @@ float height if (Ui.BeginChild("##BreadcrumbBar", new Vector2(0f, height), (ImGuiChildFlags.None), (ImGuiWindowFlags.HorizontalScrollbar))) { DrawBreadcrumbTopSeparator(); - float contentHeight = hasHorizontalOverflow ? height - UiDrawing.GetLayout().scrollbarSize : height; + float contentHeight = hasHorizontalOverflow ? height - Ui.GetLayout().scrollbarSize : height; float itemHeight = EditorUI.current.widgets.GetClickableTextSize("A", framePadding).Y; Ui.SetCursorPosY(MathF.Max(0f, (contentHeight - itemHeight) * 0.5f)); - Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetBreadcrumbText); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.contextText); for (int i = 0; i < parts.Count; i++) { BreadcrumbPart part = parts[i]; if (i > 0) { - Ui.SameLine(0f, EditorUI.current.widgets.style.assetBreadcrumbSpacing); + Ui.SameLine(0f, CurrentBrowserStyle.breadcrumbSpacing); EditorUI.current.widgets.CenteredText(">", new Vector2(Ui.MeasureText(">").X, itemHeight)); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetBreadcrumbSpacing); + Ui.SameLine(0f, CurrentBrowserStyle.breadcrumbSpacing); } Vector2 itemSize = EditorUI.current.widgets.GetClickableTextSize(part.label, framePadding); @@ -131,10 +208,10 @@ private static float GetBreadcrumbBarHeight( string currentDirectory ) { IReadOnlyList parts = BuildBreadcrumbParts(root, currentDirectory); - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float itemHeight = EditorUI.current.widgets.GetClickableTextSize("A", EditorUI.current.widgets.style.breadcrumbFramePadding).Y; float contentHeight = MathF.Max( - EditorUI.current.widgets.style.assetBreadcrumbHeight, + CurrentBrowserStyle.breadcrumbHeight, MathF.Ceiling(itemHeight + style.itemSpacing.Y * 2f + EditorUI.current.widgets.style.borderSize) ); float contentWidth = CalculateBreadcrumbContentWidth(parts, EditorUI.current.widgets.style.breadcrumbFramePadding); @@ -152,7 +229,7 @@ Vector2 framePadding for (int i = 0; i < parts.Count; i++) { if (i > 0) - width += separatorWidth + EditorUI.current.widgets.style.assetBreadcrumbSpacing * 2f; + width += separatorWidth + CurrentBrowserStyle.breadcrumbSpacing * 2f; width += Ui.MeasureText(parts[i].label).X + framePadding.X * 2f; } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs index 75b17b80f..7e92fc74d 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserPanel.cs @@ -20,7 +20,12 @@ namespace Inno.Editor.Panel.FileBrowser; [EditorPanel("asset.file-browser", "File", order: 300, menuPath: "Content")] internal sealed partial class FileBrowserPanel : EditorPanel { - private IReadOnlyList m_visibleSelection = Array.Empty(); + private IReadOnlyList m_visibleSelection = Array.Empty(); + private RuntimeIdentity? m_keyboardFocus; + private RuntimeIdentity? m_requestedKeyboardFocus; + private RuntimeIdentity? m_drawKeyboardFocus; + private RuntimeIdentity? m_pendingActivation; + private bool m_keyboardHandled; private const int C_SEARCH_BUFFER_SIZE = 256; private const int C_GRID_LABEL_LINE_COUNT = 2; private const float C_GRID_ICON_HORIZONTAL_OPTICAL_OFFSET = -1f; @@ -52,6 +57,8 @@ private enum ViewMode /// public override bool allowScrolling => false; + private AssetBrowserStyle browserStyle => new(m_ui.widgets.style); + /// /// Captures an immutable snapshot of the current observable state. /// @@ -94,13 +101,13 @@ protected override void Restore(EditorState state) float restoredTreePaneRatio = state.Get("treePaneRatio", 0.5f); m_treePaneRatio = float.IsFinite(restoredTreePaneRatio) ? Math.Clamp(restoredTreePaneRatio, 0f, 1f) : 0.5f; m_gridScale = Math.Clamp( - state.Get("gridScale", m_ui.widgets.style.assetGridDefaultScale), - m_ui.widgets.style.assetGridMinimumScale, - m_ui.widgets.style.assetGridMaximumScale + state.Get("gridScale", browserStyle.gridDefaultScale), + browserStyle.gridMinimumScale, + browserStyle.gridMaximumScale ); SetListColumnSeparators( - state.Get("listNameSeparator", m_ui.widgets.style.assetListNameSeparatorPosition), - state.Get("listTypeSeparator", m_ui.widgets.style.assetListTypeSeparatorPosition) + state.Get("listNameSeparator", browserStyle.listNameSeparatorPosition), + state.Get("listTypeSeparator", browserStyle.listTypeSeparatorPosition) ); } @@ -113,9 +120,10 @@ EditorUiSession ui ) { m_assets = assets; m_ui = ui ?? throw new ArgumentNullException(nameof(ui)); - m_gridScale = ui.widgets.style.assetGridDefaultScale; - m_listNameSeparatorPosition = ui.widgets.style.assetListNameSeparatorPosition; - m_listTypeSeparatorPosition = ui.widgets.style.assetListTypeSeparatorPosition; + AssetBrowserStyle style = new(ui.widgets.style); + m_gridScale = style.gridDefaultScale; + m_listNameSeparatorPosition = style.listNameSeparatorPosition; + m_listTypeSeparatorPosition = style.listTypeSeparatorPosition; m_data = new FileBrowserData(assets); m_navigation = new FileBrowserNavigation(assets); m_dragDrop = new FileBrowserDragDrop(assets); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs index 53d424e8b..adfd71578 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserRename.cs @@ -20,6 +20,7 @@ internal sealed class FileBrowserRename(AssetBrowserModule assets) private RuntimeIdentity? m_activeTarget; private FileBrowserPresentation m_lastPresentation = FileBrowserPresentation.List; private FileBrowserPresentation m_activePresentation; + internal RuntimeIdentity? activeTarget => m_activeTarget; internal void Update(EditorContext context) { AssetFileEntry? target = assets.edits.interactions.selection.selectedTarget as AssetFileEntry; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Navigation.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Navigation.cs new file mode 100644 index 000000000..edfab7a28 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Navigation.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Core.Identity; +using Inno.Editor.Assets.ImGui; +using Inno.Editor.Assets; +using Inno.Editor.Core; +using Inno.Editor.ImGui; +using Inno.Editor.ImGui.ImGuiWidget; +using Inno.Editor.Interactions; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Panel.FileBrowser; + +internal sealed partial class FileBrowserTree +{ + private readonly List m_navigationRows = []; + private readonly List m_visibleSelection = []; + private RuntimeIdentity? m_keyboardFocus; + private RuntimeIdentity? m_pendingSelection; + private RuntimeIdentity? m_requestedKeyboardFocus; + private RuntimeIdentity? m_drawKeyboardFocus; + private bool m_rootFocused; + private bool m_requestedRootFocus; + private bool m_drawRootFocus; + private bool m_pendingActivation; + private bool m_pendingRootNavigation; + private int m_keyboardPageSize; + + internal void CompleteNavigation(EditorContext context) + { + EditorInteractions interactions = m_assets.edits.interactions; + if (m_pendingSelection is RuntimeIdentity pending + && interactions.TryResolveIdentity(pending, out IdentityObject? resolved) && resolved is AssetFileEntry entry) + { + if (m_pendingActivation) + { + if (!interactions.selection.Contains(entry)) + interactions.SetSelection(entry); + m_navigation.OpenEntry(context, entry, this); + } + else + EditorUiSession.current.widgets.SelectTarget(interactions, entry, m_visibleSelection); + m_keyboardFocus = pending; + m_rootFocused = false; + interactions.For(FileBrowserInteractionIds.C_AREA, entry).Focus(); + } + if (m_pendingRootNavigation) + { + m_navigation.NavigateToRoot(context, m_assets.browser.root); + m_assets.browser.Select(context, null); + m_keyboardFocus = null; + m_rootFocused = true; + interactions.For(FileBrowserInteractionIds.C_AREA).Focus(); + } + int focus = -1; + int selectedIndex = -1; + RuntimeIdentity? selectedIdentity = (interactions.selection.selectedTarget as IdentityObject)?.identity.runtimeIdentity; + for (int index = 0; index < m_navigationRows.Count; index++) + { + TreeNavigationRow row = m_navigationRows[index]; + if (row.identity is null ? m_rootFocused : row.identity == m_keyboardFocus) + { + focus = index; + break; + } + if (row.identity is not null && row.identity == selectedIdentity) + selectedIndex = index; + } + if (focus < 0) + focus = selectedIndex; + ListNavigationResult navigation = EditorUiSession.current.widgets.NavigateList(interactions, m_navigationRows.Count, focus, m_keyboardPageSize); + if (navigation.focusedIndex >= 0 && (navigation.focusChanged || navigation.selectionRequested || navigation.activate)) + { + TreeNavigationRow row = m_navigationRows[navigation.focusedIndex]; + m_keyboardFocus = row.identity; + m_rootFocused = row.identity is null; + if (navigation.focusChanged) + { + m_requestedKeyboardFocus = row.identity; + m_requestedRootFocus = m_rootFocused; + } + if (row.identity is RuntimeIdentity identity + && interactions.TryResolveIdentity(identity, out IdentityObject? target) && target is AssetFileEntry asset) + { + if (navigation.selectionRequested) + { + if (navigation.extendRange) + interactions.SelectRange(m_visibleSelection, asset, navigation.additive); + else if (navigation.toggle) + interactions.ToggleSelection(asset); + else + interactions.SetSelection(asset); + } + if (navigation.activate) + m_navigation.OpenEntry(context, asset, this); + } + else if (navigation.activate) + { + m_navigation.NavigateToRoot(context, m_assets.browser.root); + m_assets.browser.Select(context, null); + } + interactions.For(FileBrowserInteractionIds.C_AREA, interactions.selection.selectedTarget).Focus(); + } + if (navigation.cancel) + { + m_keyboardFocus = null; + m_rootFocused = false; + } + } + + private void BeginNavigation() + { + ClearNavigationFrame(); + m_drawKeyboardFocus = m_requestedKeyboardFocus; + m_drawRootFocus = m_requestedRootFocus; + m_requestedKeyboardFocus = null; + m_requestedRootFocus = false; + float rowHeight = Ui.GetFrameHeight() + Ui.GetLayout().itemSpacing.Y; + m_keyboardPageSize = Math.Max(1, (int)(Ui.GetContentRegionAvailable().Y / rowHeight)); + } + + private bool RequestsKeyboardFocus( + AssetFileEntry? entry, + bool isRoot + ) => isRoot ? m_drawRootFocus : entry is not null && entry.identity.runtimeIdentity == m_drawKeyboardFocus; + + private void ClearNavigationFrame() + { + m_pendingSelection = null; + m_pendingActivation = false; + m_pendingRootNavigation = false; + } + + private readonly record struct TreeNavigationRow(RuntimeIdentity? identity); +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Rows.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Rows.cs new file mode 100644 index 000000000..e75b98273 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.Rows.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Core.Identity; +using Inno.Editor.Assets; +using Inno.Editor.Assets.ImGui; +using Inno.Editor.Core; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Panel.FileBrowser; + +internal sealed partial class FileBrowserTree +{ + private readonly record struct BrowserTreeRow( + string path, + string label, + string nodeId, + int depth, + bool isRoot, + bool virtualRoot, + bool isLeaf, + RuntimeIdentity? identity + ); + + private readonly List m_rows = []; + private long m_sourceRevision = long.MinValue; + private ulong m_expansionRevision = ulong.MaxValue; + private AssetBrowserRoot? m_rowsRoot; + + internal void Draw(EditorContext context) + { + RefreshRows(); + RuntimeIdentity? dragging = m_assets.edits.interactions.TryGetActiveDragIdentity(out RuntimeIdentity source) ? source : null; + RuntimeIdentity? editing = m_rename.activeTarget; + Span preserved = stackalloc int[4]; + int preservedCount = 0; + for (int index = 0; index < m_rows.Count; index++) + { + BrowserTreeRow row = m_rows[index]; + if (row.identity is { } identity && (identity == dragging || identity == editing || identity == m_drawKeyboardFocus) + || row.isRoot && m_drawRootFocus) + preserved[preservedCount++] = index; + } + EditorUI.current.widgets.ClippedRows(m_rows.Count, -1f, (tree: this, context), static ( + state, + start, + end + ) => + { + for (int index = start; index < end; index++) + state.tree.DrawRow(state.context, state.tree.m_rows[index]); + }, preserved[..preservedCount]); + } + + private void RefreshRows() + { + var widgets = EditorUI.current.widgets; + AssetPipeline pipeline = m_assets.edits.pipeline; + if (!m_rootOpenRequest && !m_currentDirectoryOpenRequest && m_rowsRoot == m_assets.browser.root + && m_sourceRevision == pipeline.revision && m_expansionRevision == widgets.treeExpansionRevision) + return; + var rows = new List(); + var pending = new Stack<(string path, string label, bool root, int depth)>(); + if (m_assets.browser.root == AssetBrowserRoot.Assets) + pending.Push((string.Empty, "Assets", true, 0)); + else + { + const string rootId = "tree_virtual_plugins"; + if (m_rootOpenRequest || m_currentDirectoryOpenRequest) + widgets.SetTreeNodeOpen(rootId, true); + rows.Add(new(string.Empty, "Plugins", rootId, 0, true, true, false, null)); + if (widgets.IsTreeNodeOpen(rootId)) + { + AssetSourceMount[] mounts = pipeline.sourceMounts.Where(mount => m_assets.edits.IsPluginSource(mount.id)) + .OrderBy(static mount => mount.id.value, StringComparer.OrdinalIgnoreCase).ToArray(); + for (int index = mounts.Length - 1; index >= 0; index--) + pending.Push((new AssetPath(mounts[index].id, string.Empty).ToString(), mounts[index].id.value, false, 1)); + } + } + while (pending.TryPop(out var next)) + { + AssetFileEntry? entry = null; + if (!next.root && (!pipeline.TryGetFileSystemEntry(AssetPath.Parse(next.path), out entry) || entry is null)) + continue; + bool directory = next.root || entry!.isDirectory; + IReadOnlyList children = directory ? m_data.SortTreeEntries(m_data.GetVisibleChildren(next.path)) : Array.Empty(); + string id = next.root ? "tree_root_" + next.path : "tree_" + next.path; + if (ShouldOpenTreeEntry(next.path, next.root, directory)) + widgets.SetTreeNodeOpen(id, true); + RuntimeIdentity? identity = next.root ? null : entry!.identity.runtimeIdentity + ?? throw new InvalidOperationException("A visible asset tree entry requires a registered identity."); + rows.Add(new(next.path, next.label, id, next.depth, next.root, false, + !directory || !next.root && children.Count == 0, identity)); + if (!directory || !widgets.IsTreeNodeOpen(id)) + continue; + for (int index = children.Count - 1; index >= 0; index--) + pending.Push((children[index].assetPath.ToString(), children[index].name, false, next.depth + 1)); + } + m_rows.Clear(); + m_rows.AddRange(rows); + m_navigationRows.Clear(); + m_visibleSelection.Clear(); + foreach (BrowserTreeRow row in rows) + { + m_navigationRows.Add(new TreeNavigationRow(row.identity)); + if (row.identity is RuntimeIdentity identity) + m_visibleSelection.Add(identity); + } + m_rowsRoot = m_assets.browser.root; + m_sourceRevision = pipeline.revision; + m_expansionRevision = widgets.treeExpansionRevision; + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs index 1141b27c9..568aa0283 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserTree.cs @@ -1,5 +1,6 @@ using EditorUI = Inno.Editor.ImGui.EditorUiSession; using Inno.Editor.Assets; +using Inno.Core.Identity; using Inno.Editor.Assets.ImGui; using System; using System.Collections.Generic; @@ -13,7 +14,7 @@ using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.FileBrowser; -internal sealed class FileBrowserTree +internal sealed partial class FileBrowserTree { private readonly FileBrowserData m_data; private readonly FileBrowserNavigation m_navigation; @@ -41,125 +42,58 @@ AssetBrowserModule assets m_assets = assets; } - internal void DrawEntry( + private void DrawRow( EditorContext context, - string relativePath, - string label, - bool isRoot + BrowserTreeRow row ) { - List sorted = m_data.SortTreeEntries(m_data.GetVisibleChildren(relativePath)); - bool isDirectory = isRoot || IsDirectoryPath(m_assets.edits.pipeline, relativePath); - bool selected = string.Equals(m_assets.browser.GetSelectedPath(context), relativePath, StringComparison.Ordinal); - bool isLeaf = !isDirectory || (!isRoot && sorted.Count == 0); - bool isCurrentDirectory = isDirectory - && string.Equals( - m_assets.browser.currentDirectory, - relativePath, - StringComparison.Ordinal - ) - && (m_assets.browser.root == AssetBrowserRoot.Assets) == (AssetPath.Parse(relativePath).source == AssetSourceId.project); - string icon = m_assets.folderIcon; - if (!isDirectory && m_assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry iconEntry)) - icon = m_assets.GetIcon(iconEntry); - bool editing = !isRoot && m_rename.IsEditing(context, relativePath, FileBrowserPresentation.Tree); - string nodeId = $"tree_{(isRoot ? $"root_{relativePath}" : relativePath)}"; - if (ShouldOpenTreeEntry(relativePath, isRoot, isDirectory)) - EditorUI.current.widgets.SetNextTreeNodeOpen(true); - TreeNodeResult result = EditorUI.current.widgets.TreeNode( - nodeId, - drawContext => DrawRowContent(context, nodeId, relativePath, label, icon, isCurrentDirectory, editing, drawContext.rowHeight), - new TreeNodeOptions { selected = selected, isLeaf = isLeaf } - ); - AssetFileEntry? treeEntry = null; - if (!isRoot && m_assets.edits.pipeline.TryGetFileSystemEntry(AssetPath.Parse(relativePath), out AssetFileEntry resolvedEntry)) - treeEntry = resolvedEntry; - if (treeEntry is not null && result.isDoubleClicked) - { - m_rename.MarkInteraction(FileBrowserPresentation.Tree); - m_navigation.OpenEntry(context, treeEntry, this); - } - else if (treeEntry is not null && result.isClicked) - { - m_rename.MarkInteraction(FileBrowserPresentation.Tree); - m_assets.browser.Select(context, relativePath); - } - else if (isRoot && (result.isClicked || result.isDoubleClicked)) - { - m_navigation.NavigateTo(context, relativePath); - m_assets.browser.Select(context, null); - } - - if (treeEntry is not null) + AssetFileEntry? entry = null; + if (row.identity is { } identity) { - if (!editing) + if (!m_assets.edits.interactions.TryResolveIdentity(identity, out Inno.Core.Identity.IdentityObject? resolved) + || resolved is not AssetFileEntry current) { - m_dragDrop.DrawAssetSource(context, treeEntry); - m_contextMenu.DrawEntry(context, $"##asset_tree_context_{treeEntry.assetPath.ToString()}", treeEntry.assetPath.ToString(), FileBrowserPresentation.Tree); - if (treeEntry.isDirectory && !treeEntry.isReadOnly) - m_dragDrop.DrawDirectoryTarget(context, treeEntry.assetPath.ToString()); + Ui.Dummy(new(1f, Ui.GetFrameHeight())); + return; } + entry = current; } - else if (isRoot) - { - m_contextMenu.DrawDirectory(context, $"##asset_tree_root_context_{relativePath}", relativePath, FileBrowserPresentation.Tree); - m_dragDrop.DrawDirectoryTarget(context, relativePath); - } - - if (!result.isOpen) + bool selected = entry is not null && m_assets.edits.interactions.selection.Contains(entry); + bool currentDirectory = row.virtualRoot ? string.IsNullOrEmpty(m_assets.browser.currentDirectory) + : (entry?.isDirectory ?? row.isRoot) && string.Equals(m_assets.browser.currentDirectory, row.path, StringComparison.Ordinal) + && (m_assets.browser.root == AssetBrowserRoot.Assets) == (AssetPath.Parse(row.path).source == AssetSourceId.project); + string icon = entry is { isDirectory: false } ? m_assets.GetIcon(entry) : m_assets.folderIcon; + bool editing = !row.isRoot && m_rename.IsEditing(context, row.path, FileBrowserPresentation.Tree); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( + row.nodeId, + drawContext => DrawRowContent(context, row.nodeId, row.path, row.label, icon, currentDirectory, editing, drawContext.rowHeight), + new TreeNodeOptions { depth = row.depth, selected = selected, isLeaf = row.isLeaf, + requestFocus = RequestsKeyboardFocus(entry, row.isRoot), keepContentActive = editing }); + if (!result.isSubmitted) return; - try + if (entry is not null && result.isDoubleClicked) { - for (int i = 0; i < sorted.Count; i++) - { - AssetFileEntry child = sorted[i]; - DrawEntry(context, child.assetPath.ToString(), child.name, false); - } + m_rename.MarkInteraction(FileBrowserPresentation.Tree); + m_pendingSelection = entry.identity.runtimeIdentity; + m_pendingActivation = true; } - finally + else if (entry is not null && result.isClicked) { - Ui.TreePop(); - } - } - - internal void DrawPluginRoot( - EditorContext context, - IReadOnlyList sourceMounts - ) { - AssetSourceMount[] plugins = sourceMounts.Where(mount => m_assets.edits.IsPluginSource(mount.id)).OrderBy( - static mount => mount.id.value, - StringComparer.OrdinalIgnoreCase - ).ToArray(); - bool hasPluginDirectoryOpenRequest = m_currentDirectoryOpenRequest && m_assets.edits.IsPluginSource(AssetPath.Parse(m_currentDirectoryOpenTarget).source); - if (m_rootOpenRequest || hasPluginDirectoryOpenRequest) - EditorUI.current.widgets.SetNextTreeNodeOpen(true); - bool isCurrentDirectory = m_assets.browser.root == AssetBrowserRoot.Plugins && string.IsNullOrEmpty(m_assets.browser.currentDirectory); - TreeNodeResult result = EditorUI.current.widgets.TreeNode( - "tree_virtual_plugins", - _ => EditorUI.current.widgets.IconText(m_assets.folderIcon, "Plugins", isCurrentDirectory), - new TreeNodeOptions { selected = false, isLeaf = false } - ); - if (result.isClicked || result.isDoubleClicked) - { - m_navigation.NavigateToRoot(context, AssetBrowserRoot.Plugins); - m_assets.browser.Select(context, null); + m_rename.MarkInteraction(FileBrowserPresentation.Tree); + m_pendingSelection = entry.identity.runtimeIdentity; } - - if (!result.isOpen) - return; - try + else if (row.isRoot && (result.isClicked || result.isDoubleClicked)) + m_pendingRootNavigation = true; + if (entry is not null && !editing) { - for (int i = 0; i < plugins.Length; i++) - { - AssetSourceMount mount = plugins[i]; - string root = new AssetPath(mount.id, string.Empty).ToString(); - // A mount root is a normal directory below the virtual Plugins root. Treating it - // as a browser root would make a single click navigate instead of only selecting. - DrawEntry(context, root, mount.id.value, false); - } + m_dragDrop.DrawAssetSource(context, entry); + m_contextMenu.DrawEntry(context, "##asset_tree_context_" + row.path, row.path, FileBrowserPresentation.Tree); + if (entry.isDirectory && !entry.isReadOnly) + m_dragDrop.DrawDirectoryTarget(context, row.path); } - finally + else if (row.isRoot && !row.virtualRoot) { - Ui.TreePop(); + m_contextMenu.DrawBackground(context, "##asset_tree_root_context", FileBrowserPresentation.Tree); + m_dragDrop.DrawDirectoryTarget(context, row.path); } } @@ -186,6 +120,7 @@ float rowHeight internal void PrepareOpenRequests() { + BeginNavigation(); string currentDirectory = NormalizePath(m_assets.browser.currentDirectory); if (!m_currentDirectoryOpenRequest && !string.Equals(m_lastCurrentDirectoryOpenTarget, currentDirectory, StringComparison.Ordinal)) { @@ -199,6 +134,7 @@ internal void ClearOpenRequests() { m_rootOpenRequest = false; m_currentDirectoryOpenRequest = false; + ClearNavigationFrame(); } internal void RequestOpenRoot() => m_rootOpenRequest = true; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs index 7d00d7660..72d39922e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.FileBrowser/Browser/FileBrowserUtility.cs @@ -15,6 +15,8 @@ namespace Inno.Editor.Panel.FileBrowser; internal static class FileBrowserUtility { + internal static AssetBrowserStyle CurrentBrowserStyle => new(EditorUI.current.widgets.style); + internal readonly record struct BreadcrumbPart( string label, AssetBrowserRoot root, @@ -22,23 +24,23 @@ string directory ); internal static void PushBrowserStyle() { - Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetText); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.contentText); Ui.PushStyleColor((ImGuiCol.WindowBg), EditorPalette.collectionHeader); Ui.PushStyleColor((ImGuiCol.ChildBg), EditorPalette.collectionHeader); - Ui.PushStyleColor((ImGuiCol.Border), EditorPalette.assetBorder); + Ui.PushStyleColor((ImGuiCol.Border), EditorPalette.contentBorder); Ui.PushStyleColor((ImGuiCol.TableHeaderBg), EditorPalette.collectionHeader); - Ui.PushStyleColor((ImGuiCol.TableBorderStrong), EditorPalette.assetBorder); - Ui.PushStyleColor((ImGuiCol.TableBorderLight), EditorPalette.assetBorderSoft); + Ui.PushStyleColor((ImGuiCol.TableBorderStrong), EditorPalette.contentBorder); + Ui.PushStyleColor((ImGuiCol.TableBorderLight), EditorPalette.contentBorderSoft); Ui.PushStyleColor((ImGuiCol.TableRowBg), EditorPalette.collectionRow); Ui.PushStyleColor((ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); - Ui.PushStyleColor((ImGuiCol.Header), EditorPalette.assetAccent); - Ui.PushStyleColor((ImGuiCol.HeaderHovered), EditorPalette.assetAccent); - Ui.PushStyleColor((ImGuiCol.HeaderActive), EditorPalette.assetAccent); - Ui.PushStyleVar((ImGuiStyleVar.WindowPadding), EditorUI.current.widgets.style.assetWindowPadding); + Ui.PushStyleColor((ImGuiCol.Header), EditorPalette.contentAccent); + Ui.PushStyleColor((ImGuiCol.HeaderHovered), EditorPalette.contentAccent); + Ui.PushStyleColor((ImGuiCol.HeaderActive), EditorPalette.contentAccent); + Ui.PushStyleVar((ImGuiStyleVar.WindowPadding), CurrentBrowserStyle.windowPadding); Ui.PushStyleVar((ImGuiStyleVar.ChildBorderSize), EditorUI.current.widgets.style.borderSize); - Ui.PushStyleVar((ImGuiStyleVar.CellPadding), EditorUI.current.widgets.style.assetCellPadding); - Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.assetItemSpacing); - Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), EditorUI.current.widgets.style.assetFrameRounding); + Ui.PushStyleVar((ImGuiStyleVar.CellPadding), CurrentBrowserStyle.cellPadding); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), CurrentBrowserStyle.itemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), CurrentBrowserStyle.frameRounding); } internal static void PopBrowserStyle() diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.Rows.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.Rows.cs new file mode 100644 index 000000000..3b9dc8e31 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.Rows.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using Inno.Core.Identity; +using Inno.Editor.Core; +using Inno.Scene; +using Inno.Scene.Components; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Panel.Hierarchy; + +internal sealed partial class HierarchyPanel +{ + private readonly record struct HierarchyRow( + RuntimeIdentity identity, + int depth + ); + + private readonly List m_rows = []; + private readonly List<(RuntimeIdentity identity, ulong revision)> m_sceneInputs = []; + private ulong m_expansionRevision = ulong.MaxValue; + + private void RefreshRows(IReadOnlyList scenes) + { + var widgets = EditorUI.current.widgets; + foreach (GameScene scene in scenes) + if (m_initializedSceneIds.Add(scene.identity.persistentId)) + widgets.SetTreeNodeOpen("scene_" + scene.identity.persistentId.ToString("N"), true); + foreach (Guid id in m_forceOpenSceneIds) + widgets.SetTreeNodeOpen("scene_" + id.ToString("N"), true); + foreach (Guid id in m_forceOpenIds) + widgets.SetTreeNodeOpen("hierarchy_" + id.ToString("N"), true); + m_forceOpenSceneIds.Clear(); + m_forceOpenIds.Clear(); + + bool changed = m_expansionRevision != widgets.treeExpansionRevision || m_sceneInputs.Count != scenes.Count; + for (int index = 0; !changed && index < scenes.Count; index++) + changed = m_sceneInputs[index] != (RequireIdentity(scenes[index]), scenes[index].hierarchyRevision); + if (!changed) + return; + + var rows = new List(); + var inputs = new List<(RuntimeIdentity, ulong)>(); + var pending = new Stack<(GameObject target, int depth)>(); + var visited = new HashSet(); + foreach (GameScene scene in scenes) + { + RuntimeIdentity sceneId = RequireIdentity(scene); + inputs.Add((sceneId, scene.hierarchyRevision)); + rows.Add(new(sceneId, 0)); + if (!widgets.IsTreeNodeOpen("scene_" + scene.identity.persistentId.ToString("N"))) + continue; + IReadOnlyList roots = m_selection.GetRootObjects(scene); + for (int index = roots.Count - 1; index >= 0; index--) + pending.Push((roots[index], 1)); + while (pending.TryPop(out var next)) + { + if (!next.target.isRuntimeValid) + continue; + RuntimeIdentity identity = RequireIdentity(next.target); + if (!visited.Add(identity)) + throw new InvalidOperationException("A hierarchy view encountered a duplicate object identity."); + rows.Add(new(identity, next.depth)); + if (!widgets.IsTreeNodeOpen("hierarchy_" + next.target.identity.persistentId.ToString("N"))) + continue; + IReadOnlyList children = next.target.transform.children; + for (int index = children.Count - 1; index >= 0; index--) + pending.Push((children[index].gameObject, next.depth + 1)); + } + } + + m_rows.Clear(); + m_rows.AddRange(rows); + m_sceneInputs.Clear(); + m_sceneInputs.AddRange(inputs); + m_visibleSelection.Clear(); + foreach (HierarchyRow row in rows) + m_visibleSelection.Add(row.identity); + m_expansionRevision = widgets.treeExpansionRevision; + } + + private void DrawRows(EditorContext context) + { + RuntimeIdentity? selected = (m_interactions.selection.selectedTarget as IdentityObject)?.identity.runtimeIdentity; + RuntimeIdentity? dragging = m_interactions.TryGetActiveDragIdentity(out RuntimeIdentity source) ? source : null; + Span preserved = stackalloc int[3]; + int count = 0; + for (int index = 0; index < m_rows.Count; index++) + if (m_rows[index].identity == selected || m_rows[index].identity == dragging || m_rows[index].identity == m_drawKeyboardFocus) + preserved[count++] = index; + EditorUI.current.widgets.ClippedRows(m_rows.Count, -1f, (panel: this, context), static ( + state, + start, + end + ) => + { + for (int index = start; index < end; index++) + { + state.panel.m_visibleRowIndex = index; + HierarchyRow row = state.panel.m_rows[index]; + if (state.panel.m_interactions.TryResolveIdentity(row.identity, out IdentityObject? target)) + { + if (target is GameScene scene) + state.panel.DrawScene(state.context, scene, row.depth); + else if (target is GameObject gameObject) + state.panel.DrawObject(state.context, gameObject, row.depth); + else + Ui.Dummy(new(1f, Ui.GetFrameHeight())); + } + else + Ui.Dummy(new(1f, Ui.GetFrameHeight())); + } + }, preserved[..count]); + } + + private static RuntimeIdentity RequireIdentity(IdentityObject target) + => target.identity.runtimeIdentity ?? throw new InvalidOperationException("A visible hierarchy row requires a registered identity."); +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs index 6b26e4fab..401638c0a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Presentation/HierarchyPanel.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; @@ -19,11 +18,16 @@ namespace Inno.Editor.Panel.Hierarchy; /// Edits the active scene object hierarchy. /// [EditorPanel("scene.hierarchy", "Hierarchy", order: 100, menuPath: "Workspace")] -internal sealed class HierarchyPanel : EditorPanel +internal sealed partial class HierarchyPanel : EditorPanel { - private readonly List m_visibleSelection = []; - private RuntimeIdentity? m_pendingSelection; private const nuint C_NAME_BUFFER_SIZE = 512; + + private readonly List m_visibleSelection = []; + private RuntimeIdentity? m_pendingSelection; + private RuntimeIdentity? m_keyboardFocus; + private RuntimeIdentity? m_requestedKeyboardFocus; + private RuntimeIdentity? m_drawKeyboardFocus; + private int m_keyboardPageSize = 1; private readonly IEditorSceneWorkspace m_workspace; private readonly EditorInteractions m_interactions; private readonly SceneEdits m_edits; @@ -32,7 +36,6 @@ internal sealed class HierarchyPanel : EditorPanel private readonly HierarchyDropVisual m_dropVisual = new(); private readonly HashSet m_forceOpenIds = []; private readonly HashSet m_forceOpenSceneIds = []; - private readonly HashSet m_drawnIds = []; private readonly HashSet m_initializedSceneIds = []; private ulong m_lastRevealedSelectionVersion = ulong.MaxValue; private int m_visibleRowIndex; @@ -73,7 +76,10 @@ protected override void OnDraw(EditorContext context) try { if (visible) + { + m_keyboardPageSize = Math.Max(1, (int)(Ui.GetContentRegionAvailable().Y / Math.Max(1f, Ui.GetFrameHeight()))); DrawContent(context); + } } finally { @@ -81,15 +87,17 @@ protected override void OnDraw(EditorContext context) } } + private static HierarchyStyle hierarchyStyle => new(EditorUI.current.widgets.style); + private void DrawContent(EditorContext context) { - m_drawnIds.Clear(); - m_visibleSelection.Clear(); + m_drawKeyboardFocus = m_requestedKeyboardFocus; + m_requestedKeyboardFocus = null; m_pendingSelection = null; m_visibleRowIndex = 0; m_selection.Prune(context); RevealSelection(); - Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), EditorUI.current.widgets.style.hierarchyItemSpacing); + Ui.PushStyleVar((ImGuiStyleVar.ItemSpacing), hierarchyStyle.itemSpacing); Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.compactFramePadding); try { @@ -97,160 +105,139 @@ private void DrawContent(EditorContext context) if (scenes.Count > 0) { Vector2 contentOrigin = Ui.GetCursorScreenPosition(); - Ui.SetCursorScreenPosition(new Vector2(contentOrigin.X, contentOrigin.Y - UiDrawing.GetWindowBounds().padding.Y)); + Ui.SetCursorScreenPosition(new Vector2(contentOrigin.X, contentOrigin.Y - Ui.GetWindowBounds().padding.Y)); } - for (int i = 0; i < scenes.Count; i++) - { - DrawScene(context, scenes[i]); - } + RefreshRows(scenes); + DrawRows(context); DrawBlankArea(context); if (m_pendingSelection is RuntimeIdentity pending && m_interactions.TryResolveIdentity(pending, out IdentityObject? target) && target is not null) + { EditorUI.current.widgets.SelectTarget(m_interactions, target, m_visibleSelection); + m_keyboardFocus = pending; + m_interactions.For(HierarchyInteractionIds.C_AREA, target).Focus(); + } + HandleKeyboardNavigation(); } finally { m_pendingSelection = null; - m_visibleSelection.Clear(); Ui.PopStyleVar(2); } } - private void DrawScene( - EditorContext context, - GameScene scene - ) { - Guid sceneId = ((IdentityObject)scene).identity.persistentId; - string id = sceneId.ToString("N"); - bool shouldOpen = m_initializedSceneIds.Add(sceneId); - shouldOpen |= m_forceOpenSceneIds.Remove(sceneId); - if (shouldOpen) + private void HandleKeyboardNavigation() + { + int focused = -1; + int selected = -1; + RuntimeIdentity? selectedTarget = (m_interactions.selection.selectedTarget as IdentityObject)?.identity.runtimeIdentity; + for (int index = 0; index < m_visibleSelection.Count; index++) { - EditorUI.current.widgets.SetNextTreeNodeOpen(true); + RuntimeIdentity item = m_visibleSelection[index]; + if (item == m_keyboardFocus) + focused = index; + if (item == selectedTarget) + selected = index; } - - m_visibleSelection.Add(scene); - bool selected = m_interactions.selection.Contains(scene); - TreeNodeResult result = EditorUI.current.widgets.TreeNode( - $"scene_{id}", - drawContext => DrawSceneRowContent(context, scene, id, drawContext.rowHeight), - new TreeNodeOptions { isLeaf = false, selected = selected, showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow, suppressHoverHighlight = false } - ); - try + if (focused < 0) + focused = selected; + ListNavigationResult navigation = EditorUI.current.widgets.NavigateList(m_interactions, m_visibleSelection.Count, focused, m_keyboardPageSize); + if (navigation.focusedIndex >= 0 && (navigation.focusChanged || navigation.selectionRequested || navigation.activate)) { - if (result.isClicked || result.isDoubleClicked) - m_pendingSelection = scene.identity.runtimeIdentity; - if (result.isDoubleClicked) - { - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).Execute(HierarchyInteractionIds.C_RENAME); - } - - _ = EditorDragDropRenderer.Source(m_interactions.For(HierarchyInteractionIds.C_AREA, scene), new EditorDragData(scene, scene.name), () => Ui.Text(scene.name)); - EditorDropPlacement scenePlacement = m_dropVisual.GetScenePlacement(result, Ui.GetMousePos().Y); - EditorDropWidgetResult sceneDrop = EditorDragDropRenderer.Target( - m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchySceneDropTarget(scene)), - scenePlacement - ); - if (sceneDrop.isPreviewing && sceneDrop.status.canDrop) - m_dropVisual.Draw(result, sceneDrop.status.visual); - ApplyDropResult(sceneDrop.result); - DrawSceneContextMenu(context, scene, id); - if (!result.isOpen) - { + RuntimeIdentity identity = m_visibleSelection[navigation.focusedIndex]; + if (!m_interactions.TryResolveIdentity(identity, out IdentityObject? target) || target is null) return; + m_keyboardFocus = identity; + if (navigation.focusChanged) + m_requestedKeyboardFocus = m_keyboardFocus; + if (navigation.selectionRequested) + { + if (navigation.extendRange) + m_interactions.SelectRange(m_visibleSelection, target, navigation.additive); + else if (navigation.toggle) + m_interactions.ToggleSelection(target); + else + m_interactions.SetSelection(target); } - - IReadOnlyList roots = m_selection.GetRootObjects(scene); - for (int i = 0; i < roots.Count; i++) + m_interactions.For(HierarchyInteractionIds.C_AREA, m_interactions.selection.selectedTarget).Focus(); + if (navigation.activate) { - DrawObject(context, scene, roots[i]); + if (!m_interactions.selection.Contains(target)) + m_interactions.SetSelection(target); + _ = m_interactions.For(HierarchyInteractionIds.C_AREA, target).Execute(HierarchyInteractionIds.C_RENAME); } } - finally - { - if (result.isOpen) - Ui.TreePop(); - } + if (navigation.cancel) + m_keyboardFocus = null; } - private void DrawObject( + private void DrawScene( EditorContext context, GameScene scene, - GameObject gameObject + int depth ) { - if (!gameObject.isRuntimeValid || !m_drawnIds.Add(gameObject.identity.persistentId)) - { + string id = scene.identity.persistentId.ToString("N"); + bool selected = m_interactions.selection.Contains(scene); + TreeNodeResult result = EditorUI.current.widgets.TreeNode( + $"scene_{id}", + drawContext => DrawSceneRowContent(context, scene, id, drawContext.rowHeight), + new TreeNodeOptions { + depth = depth, selected = selected, requestFocus = scene.identity.runtimeIdentity == m_drawKeyboardFocus, + keepContentActive = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).IsActive(HierarchyInteractionIds.C_RENAME), + showBackground = true, backgroundColor = EditorPalette.title + }); + if (!result.isSubmitted) return; - } + if (result.isClicked || result.isDoubleClicked) + m_pendingSelection = scene.identity.runtimeIdentity; + if (result.isDoubleClicked) + _ = m_interactions.For(HierarchyInteractionIds.C_AREA, scene).Execute(HierarchyInteractionIds.C_RENAME); + _ = EditorDragDropRenderer.Source(m_interactions.For(HierarchyInteractionIds.C_AREA, scene), + new EditorDragData(scene, scene.name), () => Ui.Text(scene.name)); + EditorDropPlacement placement = m_dropVisual.GetScenePlacement(result, Ui.GetMousePos().Y); + EditorDropWidgetResult drop = EditorDragDropRenderer.Target( + m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchySceneDropTarget(scene)), placement); + if (drop.isPreviewing && drop.status.canDrop) + m_dropVisual.Draw(result, drop.status.visual); + ApplyDropResult(drop.result); + DrawSceneContextMenu(context, scene, id); + } - Transform transform = gameObject.GetComponent(); - m_visibleSelection.Add(gameObject); - bool selected = m_interactions.selection.Contains(gameObject); + private void DrawObject( + EditorContext context, + GameObject gameObject, + int depth + ) { + if (!gameObject.isRuntimeValid) + return; string id = gameObject.identity.persistentId.ToString("N"); - if (m_forceOpenIds.Remove(gameObject.identity.persistentId)) - { - EditorUI.current.widgets.SetNextTreeNodeOpen(true); - } - TreeNodeResult result = EditorUI.current.widgets.TreeNode( $"hierarchy_{id}", drawContext => DrawRowContent(context, gameObject, drawContext.rowHeight), new TreeNodeOptions { - selected = selected, - isLeaf = transform.children.Count == 0, - showBackground = true, + depth = depth, selected = m_interactions.selection.Contains(gameObject), + requestFocus = gameObject.identity.runtimeIdentity == m_drawKeyboardFocus, + keepContentActive = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).IsActive(HierarchyInteractionIds.C_RENAME), + isLeaf = gameObject.transform.children.Count == 0, showBackground = true, backgroundColor = m_visibleRowIndex % 2 == 0 ? EditorPalette.collectionRow : EditorPalette.collectionRowAlternate, drawViewportOverlay = () => DrawVisibilityButton(gameObject, id) - } - ); - m_visibleRowIndex++; - try - { - if (result.isClicked || result.isDoubleClicked) - { - m_pendingSelection = gameObject.identity.runtimeIdentity; - } - - if (result.isDoubleClicked) - { - _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).Execute(HierarchyInteractionIds.C_RENAME); - } - - _ = EditorDragDropRenderer.Source( - m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject), - new EditorDragData(gameObject, gameObject.name), - () => Ui.Text(gameObject.name) - ); - EditorDropPlacement placement = m_dropVisual.GetObjectPlacement(result, Ui.GetMousePos().Y); - EditorDropWidgetResult objectDrop = EditorDragDropRenderer.Target( - m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchyObjectDropTarget(gameObject)), - placement - ); - if (objectDrop.isPreviewing && objectDrop.status.canDrop) - m_dropVisual.Draw(result, objectDrop.status.visual); - ApplyDropResult(objectDrop.result); - DrawObjectContextMenu(context, gameObject, id); - if (!result.isOpen) - { - return; - } - - IReadOnlyList children = transform.children; - for (int i = 0; i < children.Count; i++) - { - GameObject? child = children[i].gameObject; - if (child is not null) - { - DrawObject(context, scene, child); - } - } - } - finally - { - if (result.isOpen) - Ui.TreePop(); - } + }); + if (!result.isSubmitted) + return; + if (result.isClicked || result.isDoubleClicked) + m_pendingSelection = gameObject.identity.runtimeIdentity; + if (result.isDoubleClicked) + _ = m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject).Execute(HierarchyInteractionIds.C_RENAME); + _ = EditorDragDropRenderer.Source(m_interactions.For(HierarchyInteractionIds.C_AREA, gameObject), + new EditorDragData(gameObject, gameObject.name), () => Ui.Text(gameObject.name)); + EditorDropPlacement placement = m_dropVisual.GetObjectPlacement(result, Ui.GetMousePos().Y); + EditorDropWidgetResult drop = EditorDragDropRenderer.Target( + m_interactions.For(HierarchyInteractionIds.C_AREA, new HierarchyObjectDropTarget(gameObject)), placement); + if (drop.isPreviewing && drop.status.canDrop) + m_dropVisual.Draw(result, drop.status.visual); + ApplyDropResult(drop.result); + DrawObjectContextMenu(context, gameObject, id); } private void DrawRowContent( @@ -262,7 +249,7 @@ float rowHeight bool dimmed = !gameObject.activeInHierarchy; if (dimmed) { - Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.hierarchyInactiveText); + Ui.PushStyleColor((ImGuiCol.Text), HierarchyStyle.inactiveText); } try @@ -275,8 +262,8 @@ float rowHeight Ui.SameLine(0f, 0f); float visibilityWidth = GetVisibilityButtonWidth(); float renameWidth = MathF.Max( - EditorUI.current.widgets.style.hierarchyRenameMinimumWidth, - Ui.GetContentRegionAvailable().X - visibilityWidth - EditorUI.current.widgets.style.hierarchyRenameTrailingGap + hierarchyStyle.renameMinimumWidth, + Ui.GetContentRegionAvailable().X - visibilityWidth - hierarchyStyle.renameTrailingGap ); _ = interaction.Present(HierarchyInteractionIds.C_RENAME, new InlineRenamePresentation($"hierarchy_{id}", renameWidth, rowHeight, C_NAME_BUFFER_SIZE)); } @@ -312,8 +299,8 @@ float rowHeight ); Ui.SameLine(0f, 0f); float renameWidth = MathF.Max( - EditorUI.current.widgets.style.hierarchyRenameMinimumWidth, - Ui.GetContentRegionAvailable().X - EditorUI.current.widgets.style.hierarchyRenameTrailingGap + hierarchyStyle.renameMinimumWidth, + Ui.GetContentRegionAvailable().X - hierarchyStyle.renameTrailingGap ); _ = interaction.Present(HierarchyInteractionIds.C_RENAME, new InlineRenamePresentation($"scene_{id}", renameWidth, rowHeight, C_NAME_BUFFER_SIZE)); } @@ -324,7 +311,7 @@ string id ) { string icon = gameObject.activeSelf ? ImGuiIcon.Eye : ImGuiIcon.EyeSlash; float buttonWidth = GetVisibilityButtonWidth(); - float right = UiDrawing.GetWindowBounds().workMaximum.X - EditorUI.current.widgets.style.windowPadding.X - buttonWidth; + float right = Ui.GetWindowBounds().workMaximum.X - EditorUI.current.widgets.style.windowPadding.X - buttonWidth; Ui.SameLine(0f, -1f); Vector2 cursor = Ui.GetCursorScreenPosition(); Ui.SetCursorScreenPosition(new Vector2(MathF.Max(cursor.X, right), cursor.Y)); @@ -358,7 +345,7 @@ string id private void DrawBlankArea(EditorContext context) { Vector2 available = Ui.GetContentRegionAvailable(); - Vector2 size = new(MathF.Max(1f, available.X), MathF.Max(EditorUI.current.widgets.style.hierarchyBlankMinimumHeight, available.Y)); + Vector2 size = new(MathF.Max(1f, available.X), MathF.Max(hierarchyStyle.blankMinimumHeight, available.Y)); _ = Ui.InvisibleButton("##hierarchy_blank", size); if (Ui.IsItemClicked((ImGuiMouseButton.Left))) { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Styling/HierarchyStyle.cs b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Styling/HierarchyStyle.cs new file mode 100644 index 000000000..b8aeb77c1 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Hierarchy/Styling/HierarchyStyle.cs @@ -0,0 +1,36 @@ +using System.Numerics; +using Inno.Editor.ImGui; + +namespace Inno.Editor.Panel.Hierarchy; + +internal readonly struct HierarchyStyle +{ + private readonly float m_zoom; + + internal HierarchyStyle(EditorStyleMetrics metrics) => m_zoom = metrics.zoom; + + /// + /// Gets the text color of inactive hierarchy entries. + /// + internal static Vector4 inactiveText { get; } = new(0.52f, 0.52f, 0.54f, EditorPalette.opacityOpaque); + + /// + /// Gets hierarchy item spacing. + /// + internal Vector2 itemSpacing => new Vector2(3f, 2f) * m_zoom; + + /// + /// Gets the minimum inline hierarchy rename width. + /// + internal float renameMinimumWidth => 48f * m_zoom; + + /// + /// Gets the hierarchy rename trailing-control gap. + /// + internal float renameTrailingGap => 8f * m_zoom; + + /// + /// Gets the minimum height of the hierarchy blank drop area. + /// + internal float blankMinimumHeight => 24f * m_zoom; +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs index cbd20cb6d..b78dff422 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/AssetReferenceDropTarget.cs @@ -3,8 +3,12 @@ namespace Inno.Editor.Panel.Inspector; /// -/// Provides an assignable asset-reference property drop target. +/// Borrows an asset property's type and assignment callback for one synchronous drop request. /// +/// +/// Recreate this target for each presentation request. Its type and callback must not be retained +/// across frames or extension generations, or included in History or persistent state. +/// public sealed class AssetReferenceDropTarget { private readonly Action m_assign; @@ -22,9 +26,6 @@ public sealed class AssetReferenceDropTarget /// /// Thrown when either argument is . /// - /// - /// Thrown when the type does not belong to the active type catalog. - /// public AssetReferenceDropTarget( Type expectedType, Action assign diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs index c0c91194d..443c58f84 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/DragDrop/EngineObjectReferenceDropTarget.cs @@ -6,8 +6,12 @@ namespace Inno.Editor.Panel.Inspector; /// -/// Provides an assignable engine-object-reference property drop target. +/// Borrows an object property's type and assignment callback for one synchronous drop request. /// +/// +/// Recreate this target for each presentation request. Its type and callback must not be retained +/// across frames or extension generations, or included in History or persistent state. +/// public sealed class EngineObjectReferenceDropTarget { private readonly Action m_assign; @@ -25,9 +29,6 @@ public sealed class EngineObjectReferenceDropTarget /// /// Thrown when either argument is . /// - /// - /// Thrown when the type does not belong to the active type catalog. - /// public EngineObjectReferenceDropTarget( Type expectedType, Action assign diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs index d9c672f74..c7cc565ac 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameObjectLayerSelector.cs @@ -64,7 +64,7 @@ float width string preview = FormatLayerPreview(stack.GetName(target.layer)); if (mixed) preview = "Mixed values"; - EditorUI.current.widgets.LabelChip("Layer", EditorPalette.inspectorLayerLabel); + EditorUI.current.widgets.LabelChip("Layer", InspectorTargetStyle.layerLabel); Ui.SameLine(0f, 0f); float selectorWidth = MathF.Max(1f, width); if (!EditorUI.current.widgets.BeginMenuSelector($"game_object_layer_{target.identity.persistentId:N}", preview, selectorWidth, selectorWidth)) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs index 2723a735f..bde4b828e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs @@ -207,18 +207,18 @@ private static void DrawDragPreview( bool dimmed ) { Vector2 origin = Ui.GetCursorScreenPosition(); - Vector2 padding = EditorUI.current.widgets.style.inspectorCardHeaderPadding; + Vector2 padding = EditorUI.current.widgets.style.cardHeaderPadding; Vector2 gripSize = Ui.MeasureText(ImGuiIcon.GripVertical); Vector2 titleSize = Ui.MeasureText(title); Vector2 size = new( - padding.X * 2f + gripSize.X + EditorUI.current.widgets.style.inspectorHeaderControlSpacing + titleSize.X, + padding.X * 2f + gripSize.X + EditorUI.current.widgets.style.headerControlSpacing + titleSize.X, MathF.Max(gripSize.Y, titleSize.Y) + padding.Y * 2f ); - EditorImGui.DrawFilledRectangle(origin, origin + size, Ui.ToPackedColor(EditorPalette.inspectorCardHeader), EditorUI.current.widgets.style.frameRounding); - uint color = Ui.ToPackedColor(dimmed ? EditorPalette.inspectorCardDisabledText : EditorPalette.text); + EditorImGui.DrawFilledRectangle(origin, origin + size, Ui.ToPackedColor(EditorPalette.cardHeader), EditorUI.current.widgets.style.frameRounding); + uint color = Ui.ToPackedColor(dimmed ? EditorPalette.cardDisabledText : EditorPalette.text); Vector2 textOrigin = origin + padding; EditorImGui.DrawText(textOrigin, color, ImGuiIcon.GripVertical); - EditorImGui.DrawText(new Vector2(textOrigin.X + gripSize.X + EditorUI.current.widgets.style.inspectorHeaderControlSpacing, textOrigin.Y), color, title); + EditorImGui.DrawText(new Vector2(textOrigin.X + gripSize.X + EditorUI.current.widgets.style.headerControlSpacing, textOrigin.Y), color, title); Ui.Dummy(size); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs index b41e2cda6..b78a37cbc 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs @@ -84,7 +84,7 @@ InspectionDrawContext context try { float rowHeight = Ui.GetFrameHeight(); - float rowSpacing = EditorUI.current.widgets.style.inspectorTargetHeaderRowSpacing; + float rowSpacing = EditorUI.current.widgets.style.targetHeaderRowSpacing; float headerContentHeight = rowHeight * 2f + rowSpacing; DrawIcon(drawer.GetIcon(context), headerContentHeight); Ui.SameLine(0f, -1f); @@ -156,7 +156,7 @@ float slotSize ) { Vector2 minimum = Ui.GetCursorScreenPosition(); EditorUI.current.widgets.AddGlyphCentered( - Ui.GetFontSize() * EditorUI.current.widgets.style.inspectorTargetIconScale, + Ui.GetFontSize() * EditorUI.current.widgets.style.targetIconScale, icon, minimum + new Vector2(slotSize * 0.5f), Ui.GetStyleColor(ImGuiCol.Text) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs index b8c39018b..b500609c3 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTypeOrigin.cs @@ -12,7 +12,7 @@ internal static class InspectorTypeOrigin internal static void Draw(Type type) { ArgumentNullException.ThrowIfNull(type); - Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.assetBreadcrumbText); + Ui.PushStyleColor((ImGuiCol.Text), EditorPalette.contextText); try { EditorUI.current.widgets.WrappedText("Source: " + Describe(type)); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Styling/InspectorTargetStyle.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Styling/InspectorTargetStyle.cs new file mode 100644 index 000000000..010d5e1ae --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Styling/InspectorTargetStyle.cs @@ -0,0 +1,17 @@ +using System.Numerics; +using Inno.Editor.ImGui; + +namespace Inno.Editor.Panel.Inspector; + +internal static class InspectorTargetStyle +{ + /// + /// Gets the subdued background of Tag labels in Inspector target headers. + /// + internal static Vector4 tagLabel { get; } = new(0.34f, 0.29f, 0.44f, EditorPalette.opacityOpaque); + + /// + /// Gets the subdued background of Layer labels in Inspector target headers. + /// + internal static Vector4 layerLabel { get; } = new(0.24f, 0.34f, 0.40f, EditorPalette.opacityOpaque); +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs index a76904421..d35ee1c92 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectInspectionDrawer.cs @@ -107,13 +107,13 @@ GameObject target Ui.SameLine(0f, -1f); Ui.Text("Active"); - Ui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.headerSectionSpacing); float available = Ui.GetContentRegionAvailable().X; float tagLabelWidth = EditorUI.current.widgets.GetLabelChipSize("Tag").X; float layerLabelWidth = EditorUI.current.widgets.GetLabelChipSize("Layer").X; - float controlWidth = MathF.Max(1f, (available - tagLabelWidth - layerLabelWidth - EditorUI.current.widgets.style.inspectorHeaderSectionSpacing) * 0.5f); + float controlWidth = MathF.Max(1f, (available - tagLabelWidth - layerLabelWidth - EditorUI.current.widgets.style.headerSectionSpacing) * 0.5f); m_tagSelector.Draw([target], context.interactions, controlWidth); - Ui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.headerSectionSpacing); m_layerSelector.Draw([target], context.interactions, controlWidth); } @@ -237,7 +237,7 @@ GameObject gameObject } m_cardControls.DrawComponentDropTarget(context.interactions, m_edits, component, i, cardMinimum, cardMaximum); - Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.inspectorCardSpacing)); + Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.cardSpacing)); } } @@ -245,7 +245,7 @@ private void DrawAddComponent( InspectionDrawContext context, GameObject gameObject ) { - if (EditorUI.current.widgets.CenteredButton("Add Component", EditorUI.current.widgets.style.inspectorAddButtonTopPadding)) + if (EditorUI.current.widgets.CenteredButton("Add Component", EditorUI.current.widgets.style.addButtonTopPadding)) { m_componentSearch = string.Empty; Ui.OpenPopup("##add_component_popup"); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs index 85e07ecdb..1478f8a5f 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameObjectTagSelector.cs @@ -63,7 +63,7 @@ float width string preview = m_settings.tagCatalog.IsDefined(target.tag) ? target.tag : $"{target.tag} (Undefined)"; if (mixed) preview = "Mixed values"; - EditorUI.current.widgets.LabelChip("Tag", EditorPalette.inspectorTagLabel); + EditorUI.current.widgets.LabelChip("Tag", InspectorTargetStyle.tagLabel); Ui.SameLine(0f, 0f); float selectorWidth = MathF.Max(1f, width); if (!EditorUI.current.widgets.BeginMenuSelector($"game_object_tag_{target.identity.persistentId:N}", preview, selectorWidth, selectorWidth)) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs index c8da97237..08f698ef5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameSceneInspectionDrawer.cs @@ -187,7 +187,7 @@ GameScene scene } m_cardControls.DrawSystemDropTarget(context.interactions, m_edits, scene, system, i, cardMinimum, cardMaximum); - Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.inspectorCardSpacing)); + Ui.Dummy(new Vector2(0f, EditorUI.current.widgets.style.cardSpacing)); } } @@ -195,7 +195,7 @@ private void DrawAddSystem( InspectionDrawContext context, GameScene scene ) { - if (EditorUI.current.widgets.CenteredButton("Add System", EditorUI.current.widgets.style.inspectorAddButtonTopPadding)) + if (EditorUI.current.widgets.CenteredButton("Add System", EditorUI.current.widgets.style.addButtonTopPadding)) { m_systemSearch = string.Empty; Ui.OpenPopup("##add_system_popup"); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs index ebfbbb542..5d926093a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/SelectionInspectionDrawer.cs @@ -127,7 +127,7 @@ private void DrawAddComponents( GameObject primary ) { var widgets = EditorUiSession.current.widgets; - if (widgets.CenteredButton("Add Component to Selection", widgets.style.inspectorAddButtonTopPadding)) + if (widgets.CenteredButton("Add Component to Selection", widgets.style.addButtonTopPadding)) { m_componentSearch = ""; Inno.Editor.ImGui.ImGui.OpenPopup("selection.add-component"); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs index e1c20853c..684f70cdf 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleEntryCopyTarget.cs @@ -5,16 +5,18 @@ namespace Inno.Editor.Panel.Logging; internal readonly struct ConsoleEntryCopyTarget { + private readonly EditorConsoleOccurrence m_entry; + private readonly int m_repeatCount; internal ConsoleEntryCopyTarget( EditorConsoleOccurrence entry, int repeatCount ) { - message = entry.displayMessage; - fullText = FormatFullText(entry, repeatCount); + m_entry = entry; + m_repeatCount = repeatCount; } - internal string message { get; } - internal string fullText { get; } + internal string message => m_entry.displayMessage; + internal string fullText => FormatFullText(m_entry, m_repeatCount); private static string FormatFullText( EditorConsoleOccurrence entry, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.Layout.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.Layout.cs new file mode 100644 index 000000000..fdeced606 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.Layout.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using Ui = Inno.Editor.ImGui.ImGui; +using EditorUI = Inno.Editor.ImGui.EditorUiSession; + +namespace Inno.Editor.Panel.Logging; + +internal sealed partial class ConsolePanel +{ + private sealed class EntryMeasurement( + ConsoleView.Row row, + bool open, + float height + ) { + internal readonly ConsoleView.Row row = row; + internal readonly bool open = open; + internal float height = height; + internal int remainingMeasurements = open ? 2 : 0; + } + + private Dictionary m_measurements = new(StringComparer.Ordinal); + private EntryMeasurement[] m_layout = []; + private readonly List m_pendingMeasurements = []; + private IReadOnlyList? m_layoutRows; + private float[] m_offsets = [0f]; + private (float width, float frameHeight, float padding, float spacing, float zoom)? m_layoutShape; + private int m_openRevision; + private int m_layoutOpenRevision = -1; + private bool m_scrollFocusedRow; + private readonly Dictionary m_cardSlots = new(StringComparer.Ordinal); + private readonly List m_cardWindowNames = []; + private readonly Stack m_freeCardWindows = []; + private readonly List m_retiredCardKeys = []; + private ulong m_cardFrame; + + private sealed class CardWindowSlot(int index) + { + internal readonly int index = index; + internal ulong lastFrame; + } + + private string AcquireCardWindow(string key) + { + if (!m_cardSlots.TryGetValue(key, out CardWindowSlot? slot)) + { + if (!m_freeCardWindows.TryPop(out int index)) + { + index = m_cardWindowNames.Count; + m_cardWindowNames.Add("##ConsoleEntryCard_" + index); + } + slot = new CardWindowSlot(index); + m_cardSlots.Add(key, slot); + } + slot.lastFrame = m_cardFrame; + return m_cardWindowNames[slot.index]; + } + + private void RetireCardWindows() + { + m_retiredCardKeys.Clear(); + foreach (var entry in m_cardSlots) + if (entry.Value.lastFrame != m_cardFrame) + m_retiredCardKeys.Add(entry.Key); + foreach (string key in m_retiredCardKeys) + { + m_freeCardWindows.Push(m_cardSlots[key].index); + m_cardSlots.Remove(key); + } + m_retiredCardKeys.Clear(); + } + + private void ToggleEntry(string key) + { + if (!m_openEntries.Remove(key)) + m_openEntries.Add(key); + m_openRevision++; + } + + private void ClearLayout() + { + m_measurements.Clear(); + m_layoutRows = null; + m_layout = []; + m_pendingMeasurements.Clear(); + m_offsets = [0f]; + m_layoutShape = null; + m_scrollFocusedRow = false; + m_cardSlots.Clear(); + m_freeCardWindows.Clear(); + for (int index = m_cardWindowNames.Count - 1; index >= 0; index--) + m_freeCardWindows.Push(index); + } + + private void RefreshLayout() + { + var layout = Ui.GetLayout(); + var shape = (width: Ui.GetContentRegionAvailable().X, frameHeight: Ui.GetFrameHeight(), padding: layout.framePadding.Y, + spacing: layout.itemSpacing.Y, zoom: EditorUI.current.widgets.style.zoom); + if (ReferenceEquals(m_layoutRows, m_view.rows) && m_layoutShape == shape && m_layoutOpenRevision == m_openRevision) + return; + bool sameShape = m_layoutShape == shape; + float collapsedHeight = MathF.Truncate(shape.frameHeight + shape.padding * 2f); + var measurements = new Dictionary(m_view.rows.Count, StringComparer.Ordinal); + var entries = new EntryMeasurement[m_view.rows.Count]; + for (int index = 0; index < entries.Length; index++) + { + ConsoleView.Row row = m_view.rows[index]; + bool open = m_openEntries.Contains(row.key); + EntryMeasurement measurement = sameShape && m_measurements.TryGetValue(row.key, out EntryMeasurement? previous) + && ReferenceEquals(previous.row, row) && previous.open == open + ? previous : new(row, open, collapsedHeight); + entries[index] = measurement; + measurements.Add(row.key, measurement); + } + m_measurements = measurements; + m_layout = entries; + m_pendingMeasurements.Clear(); + for (int index = 0; index < entries.Length; index++) + if (entries[index].remainingMeasurements > 0) + m_pendingMeasurements.Add(index); + m_offsets = new float[entries.Length + 1]; + m_layoutRows = m_view.rows; + m_layoutShape = shape; + m_layoutOpenRevision = m_openRevision; + RefreshOffsets(0); + } + + private void RefreshOffsets(int start) + { + float spacing = Ui.GetLayout().itemSpacing.Y; + for (int index = start; index < m_layout.Length; index++) + m_offsets[index + 1] = m_offsets[index] + m_layout[index].height + spacing; + } + + private void DrawVisibleEntries() + { + var clip = Ui.GetClipBounds(); + if (clip.maximum.Y <= clip.minimum.Y || clip.maximum.X <= clip.minimum.X) + return; + RefreshLayout(); + m_cardFrame++; + Vector2 origin = Ui.GetCursorScreenPosition(); + int first = FindLayoutRow(clip.minimum.Y - origin.Y); + int last = FindLayoutRow(clip.maximum.Y - origin.Y); + int focused = m_view.FindIndex(m_focusedKey); + int changedFrom = m_layout.Length; + float adjustment = 0f; + int visibleIndex = first; + int pendingIndex = 0; + bool focusPending = focused >= 0; + while (visibleIndex <= last || pendingIndex < m_pendingMeasurements.Count || focusPending) + { + int index = Math.Min(visibleIndex <= last ? visibleIndex : int.MaxValue, + pendingIndex < m_pendingMeasurements.Count ? m_pendingMeasurements[pendingIndex] : int.MaxValue); + if (focusPending) + index = Math.Min(index, focused); + if (index == visibleIndex) + visibleIndex++; + if (pendingIndex < m_pendingMeasurements.Count && index == m_pendingMeasurements[pendingIndex]) + pendingIndex++; + if (index == focused) + focusPending = false; + EntryMeasurement entry = m_layout[index]; + bool measure = entry.remainingMeasurements > 0; + Ui.SetCursorScreenPosition(origin + new Vector2(0f, m_offsets[index] + adjustment)); + float height = DrawConsoleEntry(entry.row, measure); + if (!float.IsFinite(height) || height <= 0f) + throw new InvalidOperationException("Console row measurement did not produce a finite positive height."); + if (measure) + entry.remainingMeasurements--; + if (height != entry.height) + { + adjustment += height - entry.height; + entry.height = height; + changedFrom = Math.Min(changedFrom, index); + } + if (index == focused && m_scrollFocusedRow) + { + float top = origin.Y + m_offsets[index] + adjustment; + if (top < clip.minimum.Y) + Ui.SetScrollHereY(0f); + else if (top + height > clip.maximum.Y) + Ui.SetScrollHereY(1f); + } + } + for (int index = m_pendingMeasurements.Count - 1; index >= 0; index--) + if (m_layout[m_pendingMeasurements[index]].remainingMeasurements == 0) + m_pendingMeasurements.RemoveAt(index); + if (changedFrom < m_layout.Length) + RefreshOffsets(changedFrom); + m_scrollFocusedRow = false; + // A slot is reusable only after all rows have finished submitting this frame. + RetireCardWindows(); + Ui.SetCursorScreenPosition(origin); + Ui.Dummy(new(1f, MathF.Max(1f, m_offsets[^1] - Ui.GetLayout().itemSpacing.Y))); + } + + private int FindLayoutRow(float position) + { + int index = Array.BinarySearch(m_offsets, position); + return Math.Clamp(index < 0 ? ~index - 1 : index, 0, m_layout.Length - 1); + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs index 6a55beb08..df1122689 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanel.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; @@ -17,13 +16,21 @@ namespace Inno.Editor.Panel.Logging; /// Displays append-only logs and current diagnostics in the unified editor Console. /// [EditorPanel("diagnostics.console", "Console", order: 400, menuPath: "Diagnostics")] -internal sealed class ConsolePanel : EditorPanel +internal sealed partial class ConsolePanel : EditorPanel { #region State private readonly ConsolePanelContent m_content = new(); private readonly HashSet m_filterLevels = Enum.GetValues().ToHashSet(); private readonly LogLevel[] m_levels = Enum.GetValues(); private readonly HashSet m_openEntries = new(StringComparer.Ordinal); + private readonly ConsoleView m_view = new(); + private string m_query = string.Empty; + private string? m_focusedKey; + private string? m_selectedKey; + private string? m_contextKey; + private bool m_requestContextMenu; + private bool m_keyboardHandled; + private object? m_focusedCopyTarget; private bool m_collapse = true; private long m_lastRevision = -1; private bool m_requestScrollToBottom = true; @@ -78,6 +85,17 @@ protected override void Restore(EditorState state) m_collapse = state.Get("collapse", true); } + /// + protected override void OnDetach(EditorContext context) + { + ClearFocus(); + m_view.Clear(); + m_openEntries.Clear(); + ClearLayout(); + m_lastRevision = -1; + m_requestScrollToBottom = true; + } + /// /// Draws this feature using the current editor presentation context. /// @@ -101,8 +119,12 @@ protected override void OnDraw(EditorContext context) #endregion #region Toolbar + private static ConsoleStyle consoleStyle => new(EditorUI.current.widgets.style); + private void DrawToolbar() { + if (EditorUI.current.widgets.SearchInput("console", "Search messages, sources and files", ref m_query, 1024)) + m_requestScrollToBottom = false; bool collapseChanged = Ui.Checkbox("Collapse", ref m_collapse); if (collapseChanged) { @@ -121,6 +143,7 @@ private void DrawToolbar() { m_console.Clear(); m_openEntries.Clear(); + ClearFocus(); m_requestScrollToBottom = true; } } @@ -166,13 +189,29 @@ private void DrawConsoleRegion() try { EditorConsoleSnapshot snapshot = m_console.Capture(); - RemoveStaleOpenEntries(snapshot); + if (m_lastRevision != snapshot.revision) + RemoveStaleOpenEntries(snapshot); + bool viewChanged = m_view.Update(snapshot, m_query, m_filterLevels, m_collapse); + if (viewChanged) + { + int focused = m_view.FindIndex(m_focusedKey); + if (focused < 0) + { + ClearFocus(); + } + else if (m_interactions.focusedArea == LoggingInteractionIds.C_ENTRY_AREA) + FocusRow(m_view.rows[focused], select: false); + } bool hasNewEntries = m_lastRevision >= 0 && snapshot.revision != m_lastRevision; m_lastRevision = snapshot.revision; - bool scrollAtBottom = Ui.GetScrollY() >= Ui.GetScrollMaxY() - EditorUI.current.widgets.style.logAutoScrollTolerance; + bool scrollAtBottom = Ui.GetScrollY() >= Ui.GetScrollMaxY() - consoleStyle.autoScrollTolerance; bool shouldScrollToBottom = m_requestScrollToBottom || (hasNewEntries && scrollAtBottom); + HandleKeyboardNavigation(); + if (m_keyboardHandled) + m_requestScrollToBottom = false; DrawEntries(snapshot); - if (shouldScrollToBottom) + DrawEntryContextMenu(); + if (shouldScrollToBottom && !m_keyboardHandled) { Ui.SetScrollHereY(1f); m_requestScrollToBottom = false; @@ -186,105 +225,100 @@ private void DrawConsoleRegion() #endregion #region Entries + private void ClearFocus() + { + m_focusedKey = null; + m_selectedKey = null; + m_focusedCopyTarget = null; + m_contextKey = null; + m_requestContextMenu = false; + if (m_interactions.focusedArea == LoggingInteractionIds.C_ENTRY_AREA) + m_interactions.For(LoggingInteractionIds.C_ENTRY_AREA).Focus(); + } + private void DrawEntries(EditorConsoleSnapshot snapshot) { if (snapshot.occurrences.Count == 0) { + ClearLayout(); m_content.DrawDisabledHint("No logs or diagnostics yet."); return; } - int visibleCount = 0; - if (m_collapse) - { - for (int i = 0; i < snapshot.groups.Count; i++) - { - EditorConsoleGroup group = snapshot.groups[i]; - if (!m_filterLevels.Contains(group.latest.level)) - continue; - visibleCount++; - DrawConsoleEntry(group.latest, group.occurrences, group.count, $"group/{group.identity}"); - } - } - else + if (m_view.rows.Count == 0) { - for (int i = 0; i < snapshot.occurrences.Count; i++) - { - EditorConsoleOccurrence occurrence = snapshot.occurrences[i]; - if (!m_filterLevels.Contains(occurrence.level)) - continue; - visibleCount++; - DrawConsoleEntry(occurrence, [occurrence], 1, $"occurrence/{occurrence.sequence}"); - } + ClearLayout(); + m_content.DrawDisabledHint("No entries match the current search and filters."); + return; } - - if (visibleCount == 0) - m_content.DrawDisabledHint("All logs are filtered out."); + DrawVisibleEntries(); } #endregion #region Entry Card - private void DrawConsoleEntry( - EditorConsoleOccurrence entry, - IReadOnlyList occurrences, - int repeatCount, - string identity + private float DrawConsoleEntry( + ConsoleView.Row row, + bool measure ) { - (Vector4 levelColor, string levelIcon) = m_content.GetLevelVisual(entry.level); - ConsoleEntryImGuiIdentity.Push(identity); - try - { - DrawEntryCard(entry, occurrences, repeatCount, identity, levelColor, levelIcon); - } - finally - { - ConsoleEntryImGuiIdentity.Pop(); - } + (Vector4 levelColor, string levelIcon) = m_content.GetLevelVisual(row.latest.level); + return DrawEntryCard(row, levelColor, levelIcon, measure); } - private void DrawEntryCard( - EditorConsoleOccurrence entry, - IReadOnlyList occurrences, - int repeatCount, - string identity, + private float DrawEntryCard( + ConsoleView.Row row, Vector4 levelColor, - string levelIcon + string levelIcon, + bool measure ) { - bool open = m_openEntries.Contains(identity); + bool open = m_openEntries.Contains(row.key); Vector4 cardBg = open ? m_content.GetExpandedBgColor(levelColor) : m_content.GetCollapsedBgColor(); - Vector4 cardBorder = open ? EditorPalette.GetLogExpandedBorder(levelColor) : m_content.GetCollapsedBorderColor(); + Vector4 cardBorder = row.key == m_selectedKey ? EditorPalette.contentAccent + : open ? ConsoleStyle.GetExpandedBorder(levelColor) : m_content.GetCollapsedBorderColor(); Ui.PushStyleColor((ImGuiCol.FrameBg), cardBg); Ui.PushStyleColor((ImGuiCol.Border), cardBorder); - Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), UiDrawing.GetLayout().frameRounding); + Ui.PushStyleVar((ImGuiStyleVar.FrameRounding), Ui.GetLayout().frameRounding); Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), EditorUI.current.widgets.style.borderSize); Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.framePadding); try { ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | (open ? ImGuiChildFlags.AutoResizeY : ImGuiChildFlags.None); + if (open && measure) + childFlags |= ImGuiChildFlags.AlwaysAutoResize; ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; - float height = open ? 0f : Ui.GetFrameHeight() + UiDrawing.GetLayout().framePadding.Y * 2f; - bool visible = Ui.BeginChild("##ConsoleEntryCard", new Vector2(0f, height), childFlags, windowFlags); + float height = open ? 0f : Ui.GetFrameHeight() + Ui.GetLayout().framePadding.Y * 2f; + bool visible = Ui.BeginChild(AcquireCardWindow(row.key), new Vector2(0f, height), childFlags, windowFlags); try { if (visible) { - DrawHeader(entry, repeatCount, levelColor, levelIcon, open, cardBg, out bool toggled); - if (toggled) + ConsoleEntryImGuiIdentity.Push(row.key); + try { + DrawHeader(row.latest, row.occurrences.Count, levelColor, levelIcon, open, cardBg, out bool toggled); + if (toggled) + ToggleEntry(row.key); if (open) - m_openEntries.Remove(identity); - else - m_openEntries.Add(identity); + DrawOccurrences(row.occurrences, cardBg); + } + finally + { + ConsoleEntryImGuiIdentity.Pop(); } - if (open) - DrawOccurrences(occurrences, cardBg); } } finally { Ui.EndChild(); } - DrawEntryContextMenu(entry, repeatCount); + float measuredHeight = Ui.GetItemMaximum().Y - Ui.GetItemMinimum().Y; + if (Ui.IsItemClicked(ImGuiMouseButton.Left) || Ui.IsItemClicked(ImGuiMouseButton.Right)) + FocusRow(row, select: true); + if (Ui.IsItemHovered() && Ui.IsMouseReleased(ImGuiMouseButton.Right)) + { + m_contextKey = row.key; + m_requestContextMenu = true; + } + return measuredHeight; } finally { @@ -297,7 +331,7 @@ private static void DrawOccurrences( IReadOnlyList occurrences, Vector4 cardBackground ) { - Ui.PushStyleColor(ImGuiCol.Separator, EditorPalette.GetLogSeparator(cardBackground)); + Ui.PushStyleColor(ImGuiCol.Separator, ConsoleStyle.GetSeparator(cardBackground)); try { Ui.Separator(); @@ -334,12 +368,12 @@ private void DrawHeader( out bool toggled ) { toggled = false; - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); string content = isOpen ? entry.displayMessage : m_content.GetFirstLine(entry.displayMessage); string repeatText = repeatCount > 1 ? $" (x{repeatCount})" : string.Empty; string prefix = $"{levelIcon} [{entry.level}] "; string toggleText = isOpen ? "▼" : "▶"; - float toggleW = Ui.MeasureText(toggleText).X + EditorUI.current.widgets.style.logDisclosurePadding.X * 2f; + float toggleW = Ui.MeasureText(toggleText).X + consoleStyle.disclosurePadding.X * 2f; float prefixW = Ui.MeasureText(prefix).X + toggleW + style.itemInnerSpacing.X; float suffixW = Ui.MeasureText(repeatText).X; float suffixColW = MathF.Max(1f, suffixW); @@ -367,14 +401,14 @@ out bool toggled } _ = Ui.TableSetColumnIndex(0); - Ui.PushStyleVar((ImGuiStyleVar.FramePadding), EditorUI.current.widgets.style.logDisclosurePadding); + Ui.PushStyleVar((ImGuiStyleVar.FramePadding), consoleStyle.disclosurePadding); Ui.PushStyleVar((ImGuiStyleVar.FrameBorderSize), 0f); - Ui.PushStyleColor((ImGuiCol.Button), EditorPalette.logToggle); - Ui.PushStyleColor((ImGuiCol.ButtonHovered), EditorPalette.logToggleHovered); - Ui.PushStyleColor((ImGuiCol.ButtonActive), EditorPalette.logToggleActive); + Ui.PushStyleColor((ImGuiCol.Button), ConsoleStyle.toggle); + Ui.PushStyleColor((ImGuiCol.ButtonHovered), ConsoleStyle.toggleHovered); + Ui.PushStyleColor((ImGuiCol.ButtonActive), ConsoleStyle.toggleActive); try { - if (Ui.SmallButton($"{toggleText}##HeaderToggle")) + if (Ui.SmallButton($"{toggleText}##HeaderToggle") && !m_keyboardHandled) { toggled = true; } @@ -428,7 +462,7 @@ private static void DrawDetail(EditorConsoleOccurrence entry) // These cards may be culled between frames; do not rely on a previous frame's auto-fit width. float labelWidth = Ui.MeasureText("Session:").X; float availableWidth = Ui.GetContentRegionAvailable().X; - if (availableWidth < labelWidth + Ui.MeasureText("Diagnostic").X + UiDrawing.GetLayout().cellPadding.X * 4) + if (availableWidth < labelWidth + Ui.MeasureText("Diagnostic").X + Ui.GetLayout().cellPadding.X * 4) { DrawStackedField("Kind:", entry.kind.ToString()); DrawStackedField("File:", fileWithLineText); @@ -463,11 +497,69 @@ private static void DrawDetail(EditorConsoleOccurrence entry) } } - private void DrawEntryContextMenu( - EditorConsoleOccurrence entry, - int repeatCount + private void HandleKeyboardNavigation() + { + int current = m_view.FindIndex(m_focusedKey); + int pageSize = Math.Max(1, (int)(Ui.GetContentRegionAvailable().Y / Math.Max(1f, Ui.GetFrameHeight()))); + ListNavigationResult navigation = EditorUI.current.widgets.NavigateList(m_interactions, m_view.rows.Count, current, pageSize); + m_keyboardHandled = navigation.activate || navigation.selectionRequested || navigation.focusChanged; + m_scrollFocusedRow = navigation.focusChanged || navigation.activate; + if (navigation.focusedIndex >= 0 && (navigation.focusChanged || navigation.selectionRequested || navigation.activate)) + { + ConsoleView.Row row = m_view.rows[navigation.focusedIndex]; + FocusRow(row, navigation.selectionRequested || navigation.activate); + if (navigation.activate) + { + ToggleEntry(row.key); + } + } + if (navigation.cancel) + ClearFocus(); + } + + private void FocusRow( + ConsoleView.Row row, + bool select ) { - _ = EditorUI.current.menus.ContextMenu("##ConsoleEntryContextMenu", m_interactions.For(LoggingInteractionIds.C_ENTRY_AREA, new ConsoleEntryCopyTarget(entry, repeatCount))); + m_focusedKey = row.key; + if (select) + m_selectedKey = row.key; + // The view owns this neutral boxed payload while Core keeps only a weak action target. + m_focusedCopyTarget = row.copyTarget; + m_interactions.For(LoggingInteractionIds.C_ENTRY_AREA, m_focusedCopyTarget).Focus(); + } + + private void DrawEntryContextMenu() + { + const string popupId = "##ConsoleEntryContextMenu"; + if (m_contextKey is null) + return; + int index = m_view.FindIndex(m_contextKey); + if (index < 0) + { + if (Ui.BeginPopup(popupId)) + { + try + { + Ui.CloseCurrentPopup(); + } + finally + { + Ui.EndPopup(); + } + } + m_contextKey = null; + m_requestContextMenu = false; + return; + } + if (m_requestContextMenu) + { + Ui.OpenPopup(popupId); + m_requestContextMenu = false; + } + if (!EditorUI.current.menus.ContextMenu(popupId, + m_interactions.For(LoggingInteractionIds.C_ENTRY_AREA, m_view.rows[index].copyTarget))) + m_contextKey = null; } private void RemoveStaleOpenEntries(EditorConsoleSnapshot snapshot) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs index be07e53fb..33604dca3 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsolePanelContent.cs @@ -24,17 +24,17 @@ internal void DrawDisabledHint(string text) { return level switch { - LogLevel.Debug => (EditorPalette.logDebug, C_ICON_BUG), - LogLevel.Info => (EditorPalette.logInfo, C_ICON_INFO), - LogLevel.Warn => (EditorPalette.logWarning, C_ICON_WARN), - LogLevel.Error => (EditorPalette.logError, C_ICON_ERROR), - LogLevel.Fatal => (EditorPalette.logFatal, C_ICON_FATAL), + LogLevel.Debug => (ConsoleStyle.debug, C_ICON_BUG), + LogLevel.Info => (ConsoleStyle.info, C_ICON_INFO), + LogLevel.Warn => (ConsoleStyle.warning, C_ICON_WARN), + LogLevel.Error => (ConsoleStyle.error, C_ICON_ERROR), + LogLevel.Fatal => (ConsoleStyle.fatal, C_ICON_FATAL), _ => (EditorPalette.text, C_ICON_DEFAULT)}; } - internal Vector4 GetCollapsedBgColor() => EditorPalette.logCollapsedCard; - internal Vector4 GetCollapsedBorderColor() => EditorPalette.logCollapsedBorder; - internal Vector4 GetExpandedBgColor(Vector4 levelColor) => EditorPalette.GetLogExpandedCard(levelColor); + internal Vector4 GetCollapsedBgColor() => ConsoleStyle.collapsedCard; + internal Vector4 GetCollapsedBorderColor() => ConsoleStyle.collapsedBorder; + internal Vector4 GetExpandedBgColor(Vector4 levelColor) => ConsoleStyle.GetExpandedCard(levelColor); internal string GetFirstLine(string text) { if (string.IsNullOrEmpty(text)) diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleView.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleView.cs new file mode 100644 index 000000000..5a17c1422 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/ConsoleView.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Inno.Core.Logging; +using Inno.Editor.Diagnostics; + +namespace Inno.Editor.Panel.Logging; + +internal sealed class ConsoleView +{ + private long m_revision = -1; + private string m_query = string.Empty; + private ulong m_levels; + private bool m_collapsed; + private readonly Dictionary m_indices = new(StringComparer.Ordinal); + + internal IReadOnlyList rows { get; private set; } = Array.Empty(); + + internal bool Update( + EditorConsoleSnapshot snapshot, + string query, + IReadOnlySet levels, + bool collapsed + ) { + ulong mask = 0; + foreach (LogLevel level in levels) + mask |= 1UL << (int)level; + if (m_revision == snapshot.revision && m_levels == mask && m_collapsed == collapsed + && string.Equals(m_query, query, StringComparison.Ordinal)) + return false; + var filtered = new List(); + if (collapsed) + { + foreach (EditorConsoleGroup group in snapshot.groups) + { + var matches = new List(); + foreach (EditorConsoleOccurrence occurrence in group.occurrences) + { + if (Matches(occurrence, query, levels)) + matches.Add(occurrence); + } + if (matches.Count > 0) + filtered.Add(new Row("group/" + group.identity, matches[^1], matches.AsReadOnly())); + } + } + else + { + foreach (EditorConsoleOccurrence occurrence in snapshot.occurrences) + { + if (Matches(occurrence, query, levels)) + filtered.Add(new Row("occurrence/" + occurrence.sequence, occurrence, Array.AsReadOnly(new[] { occurrence }))); + } + } + if (collapsed) + { + filtered.Sort(static ( + first, + second + ) => first.latest.sequence.CompareTo(second.latest.sequence)); + } + for (int index = 0; index < filtered.Count; index++) + { + Row candidate = filtered[index]; + if (m_indices.TryGetValue(candidate.key, out int previousIndex)) + { + Row previous = rows[previousIndex]; + if (ReferenceEquals(previous.latest, candidate.latest) && previous.occurrences.SequenceEqual(candidate.occurrences)) + filtered[index] = previous; + } + } + m_indices.Clear(); + for (int index = 0; index < filtered.Count; index++) + m_indices.Add(filtered[index].key, index); + rows = filtered.AsReadOnly(); + m_revision = snapshot.revision; + m_query = query; + m_levels = mask; + m_collapsed = collapsed; + return true; + } + + internal int FindIndex(string? key) => key is not null && m_indices.TryGetValue(key, out int index) ? index : -1; + + internal void Clear() + { + rows = Array.Empty(); + m_indices.Clear(); + m_revision = -1; + m_query = string.Empty; + m_levels = 0; + m_collapsed = false; + } + + private static bool Matches( + EditorConsoleOccurrence occurrence, + string query, + IReadOnlySet levels + ) => levels.Contains(occurrence.level) + && (query.Length == 0 + || occurrence.message.Contains(query, StringComparison.OrdinalIgnoreCase) + || occurrence.source.Contains(query, StringComparison.OrdinalIgnoreCase) + || occurrence.file.Contains(query, StringComparison.OrdinalIgnoreCase) + || occurrence.code.Contains(query, StringComparison.OrdinalIgnoreCase) + || occurrence.category.Contains(query, StringComparison.OrdinalIgnoreCase) + || occurrence.stackTrace.Contains(query, StringComparison.OrdinalIgnoreCase)); + + internal sealed class Row + { + internal Row( + string key, + EditorConsoleOccurrence latest, + IReadOnlyList occurrences + ) { + this.key = key; + this.latest = latest; + this.occurrences = occurrences; + copyTarget = new ConsoleEntryCopyTarget(latest, occurrences.Count); + } + + internal string key { get; } + internal EditorConsoleOccurrence latest { get; } + internal IReadOnlyList occurrences { get; } + internal object copyTarget { get; } + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs index 3e65dcebc..7fa3f4ac3 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleDetailsCommand.cs @@ -1,9 +1,11 @@ using Inno.Editor.Interactions; +using Inno.Core.Input; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; [EditorAction(LoggingInteractionIds.C_COPY_DETAILS, LoggingInteractionIds.C_ENTRY_AREA)] [EditorMenu(LoggingInteractionIds.C_ENTRY_AREA, "Copy Full Entry", order: 110)] +[EditorShortcut(LoggingInteractionIds.C_ENTRY_AREA, KeyCode.C, KeyModifier.Shift, primary: true)] internal sealed class CopyConsoleDetailsCommand : EditorAction { /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs index c96125f25..3352f1666 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Console/CopyConsoleMessageCommand.cs @@ -1,9 +1,11 @@ using Inno.Editor.Interactions; +using Inno.Core.Input; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Logging; [EditorAction(LoggingInteractionIds.C_COPY_MESSAGE, LoggingInteractionIds.C_ENTRY_AREA)] [EditorMenu(LoggingInteractionIds.C_ENTRY_AREA, "Copy Message", order: 100)] +[EditorShortcut(LoggingInteractionIds.C_ENTRY_AREA, KeyCode.C, primary: true)] internal sealed class CopyConsoleMessageCommand : EditorAction { /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Logging/Styling/ConsoleStyle.cs b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Styling/ConsoleStyle.cs new file mode 100644 index 000000000..ef94e75e6 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Logging/Styling/ConsoleStyle.cs @@ -0,0 +1,114 @@ +using System.Numerics; +using Inno.Editor.ImGui; + +namespace Inno.Editor.Panel.Logging; + +internal readonly struct ConsoleStyle +{ + private readonly float m_zoom; + + internal ConsoleStyle(EditorStyleMetrics metrics) => m_zoom = metrics.zoom; + + /// + /// Gets debug log color. + /// + internal static Vector4 debug { get; } = new(0.80f, 0.90f, 0.85f, EditorPalette.opacityOpaque); + + /// + /// Gets informational log color. + /// + internal static Vector4 info { get; } = new(0.20f, 1f, 0.20f, EditorPalette.opacityOpaque); + + /// + /// Gets warning log color. + /// + internal static Vector4 warning { get; } = new(1f, 1f, 0.20f, EditorPalette.opacityOpaque); + + /// + /// Gets error log color. + /// + internal static Vector4 error { get; } = new(1f, 0.20f, 0.20f, EditorPalette.opacityOpaque); + + /// + /// Gets fatal log color. + /// + internal static Vector4 fatal { get; } = new(1f, 0.20f, 1f, EditorPalette.opacityOpaque); + + /// + /// Gets collapsed log card background. + /// + internal static Vector4 collapsedCard { get; } = new(0.61960787f, 0.5764706f, 0.76862746f, EditorPalette.opacityMuted); + + /// + /// Gets collapsed log card border. + /// + internal static Vector4 collapsedBorder { get; } = new(0.32f, 0.34f, 0.37f, EditorPalette.opacityMedium); + + /// + /// Gets the base expanded log card background. + /// + internal static Vector4 expandedBase { get; } = new(0.10f, 0.10f, 0.10f, EditorPalette.opacityOpaque); + + /// + /// Gets the base expanded log card border. + /// + internal static Vector4 expandedBorderBase { get; } = new(0.24f, 0.24f, 0.24f, EditorPalette.opacityOpaque); + + /// + /// Gets log header button background. + /// + internal static Vector4 toggle { get; } = new(0.61960787f, 0.5764706f, 0.76862746f, EditorPalette.opacityNone); + + /// + /// Gets hovered log header button background. + /// + internal static Vector4 toggleHovered { get; } = new(0.7372549f, 0.69411767f, 0.8862745f, EditorPalette.opacityMedium); + + /// + /// Gets active log header button background. + /// + internal static Vector4 toggleActive { get; } = new(0.8156863f, 0.77254903f, 0.9647059f, EditorPalette.opacityMedium); + + /// + /// Gets the expanded log card background derived from a severity color. + /// + /// + /// The palette color representing the log severity. + /// + /// + /// The standard expanded-card base blended with the severity color. + /// + internal static Vector4 GetExpandedCard(Vector4 severityColor) => EditorPalette.Lerp(expandedBase, severityColor, 0.12f); + + /// + /// Gets the expanded log card border derived from a severity color. + /// + /// + /// The palette color representing the log severity. + /// + /// + /// The standard expanded-border base blended with the severity color. + /// + internal static Vector4 GetExpandedBorder(Vector4 severityColor) => EditorPalette.Lerp(expandedBorderBase, severityColor, 0.20f); + + /// + /// Gets the separator color used inside an expanded log card. + /// + /// + /// The resolved background color of the expanded card. + /// + /// + /// The card color blended toward the shared text color. + /// + internal static Vector4 GetSeparator(Vector4 cardColor) => EditorPalette.Lerp(cardColor, EditorPalette.text, 0.12f); + + /// + /// Gets the distance from the bottom treated as auto-scroll. + /// + internal float autoScrollTolerance => m_zoom; + + /// + /// Gets log disclosure padding. + /// + internal Vector2 disclosurePadding => new Vector2(6f, 1f) * m_zoom; +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs index ca7fa87fe..14a8fc5e4 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs @@ -648,7 +648,7 @@ string message private bool DrawManipulationToolbar(ManipulationToolbarLayout layout) { EditorDrawList drawList = EditorImGui.GetDrawList(); - Vector4 background = EditorPalette.inspectorTargetHeader; + Vector4 background = EditorPalette.targetHeader; background.W = EditorPalette.opacityMedium; EditorImGui.DrawFilledRectangle(layout.minimum, layout.maximum, Ui.ToPackedColor(background), EditorUI.current.widgets.style.frameRounding); float itemX = layout.minimum.X + layout.padding; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs index d5989d427..256645cd5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs @@ -130,7 +130,7 @@ float height if (!visible) return; DrawNavigationButtons(); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarSectionSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarSectionSpacing); bool wasSearching = !string.IsNullOrWhiteSpace(m_query); Ui.SetNextItemWidth(-1f); bool queryChanged = EditorUI.current.widgets.SearchInput("settings", "Search settings", ref m_query, width: -1f); @@ -226,7 +226,7 @@ float height Vector2 minimum = Ui.GetItemMinimum(); Vector2 maximum = Ui.GetItemMaximum(); - System.Numerics.Vector4 color = active ? EditorPalette.assetAccent : hovered ? EditorPalette.assetBorder : EditorPalette.assetBorderSoft; + System.Numerics.Vector4 color = active ? EditorPalette.contentAccent : hovered ? EditorPalette.contentBorder : EditorPalette.contentBorderSoft; float centerX = (minimum.X + maximum.X) * 0.5f; EditorImGui.DrawLine(new Vector2(centerX, minimum.Y), new Vector2(centerX, maximum.Y), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); } @@ -243,7 +243,7 @@ private static float ClampTreeWidth( float requested, float usable ) { - float minimum = MathF.Min(EditorUI.current.widgets.style.assetPaneMinimumVisibleWidth, usable * 0.5f); + float minimum = MathF.Min(EditorUI.current.widgets.style.paneMinimumVisibleWidth, usable * 0.5f); float maximum = MathF.Max(minimum, usable - minimum); return Math.Clamp(requested, minimum, maximum); } @@ -282,7 +282,7 @@ private void DrawNavigationButtons() } Ui.EndDisabled(); - Ui.SameLine(0f, EditorUI.current.widgets.style.assetToolbarTightSpacing); + Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarTightSpacing); Ui.BeginDisabled(!m_navigation.canGoForward); if (Ui.Button($"{Inno.Editor.ImGui.ImGuiIcon.AngleRight}##SettingsForward", buttonSize)) { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs index 6b9ccb792..0954cb21e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs @@ -12,6 +12,8 @@ namespace Inno.Editor.Panel.Settings; internal sealed class SettingsPageView(SettingsEditSession session) { + private static SettingsStyle settingsStyle => new(EditorUI.current.widgets.style); + internal void Draw( SettingsPage page, Action navigate @@ -106,7 +108,7 @@ ref int fieldIndex ) { ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.NoPadOuterX; float tableWidth = MathF.Max(1f, Ui.GetContentRegionAvailable().X); - Ui.PushStyleVar(ImGuiStyleVar.CellPadding, EditorUI.current.widgets.style.settingsFieldPadding); + Ui.PushStyleVar(ImGuiStyleVar.CellPadding, settingsStyle.fieldPadding); bool tableStarted = false; try { @@ -131,7 +133,7 @@ private void DrawFieldRow( int fieldIndex ) { Ui.TableNextRow(); - uint background = Ui.ToPackedColor(fieldIndex % 2 == 0 ? EditorPalette.settingsField : EditorPalette.settingsFieldAlternate); + uint background = Ui.ToPackedColor(fieldIndex % 2 == 0 ? SettingsStyle.field : SettingsStyle.alternateField); Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, background); Ui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, background); Ui.PushId(setting.path); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Styling/SettingsStyle.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Styling/SettingsStyle.cs new file mode 100644 index 000000000..e08247097 --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Styling/SettingsStyle.cs @@ -0,0 +1,15 @@ +using System.Numerics; +using Inno.Editor.ImGui; + +namespace Inno.Editor.Panel.Settings; + +internal readonly struct SettingsStyle +{ + private readonly float m_zoom; + + internal SettingsStyle(EditorStyleMetrics metrics) => m_zoom = metrics.zoom; + + internal static Vector4 field { get; } = new(0.36f, 0.36f, 0.38f, EditorPalette.opacityTrace); + internal static Vector4 alternateField { get; } = new(0.52f, 0.52f, 0.56f, EditorPalette.opacityTrace); + internal Vector2 fieldPadding => new Vector2(8f, 6f) * m_zoom; +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs index 9dc988164..4195f7257 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.MiniMap.cs @@ -106,9 +106,9 @@ Vector2 maximum m_miniMapGeometry.Clear(); foreach (GraphNodeRecord node in Controller.document.nodes) { - GraphPosition position = draft.dragPreview.GetValueOrDefault(node.id, node.position); + GraphPosition position = draft.pointer.previewPositions.GetValueOrDefault(node.id, node.position); Vector2 nodeMinimum = new(position.x, position.y); - Vector2 nodeMaximum = nodeMinimum + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20f); + Vector2 nodeMaximum = nodeMinimum + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + 20f); m_miniMapGeometry.Add(node.id, new(nodeMinimum, nodeMaximum)); worldMinimum = Vector2.Min(worldMinimum, nodeMinimum); worldMaximum = Vector2.Max(worldMaximum, nodeMaximum); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs index cfb20a739..2f0ac8f1e 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs @@ -1,5 +1,4 @@ using Inno.Editor.Core; -using UiDrawing = Inno.Editor.ImGui.ImGui; using Inno.Editor.Graph.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; @@ -22,13 +21,13 @@ namespace Inno.Editor.Panel.ShaderEditor; internal sealed partial class ShaderEditorCanvas( ShaderEditorDocuments owner, ShaderEditorDocuments.Draft draft -) -{ +) { private readonly record struct PortHandle( GraphEndpoint endpoint, GraphPortDirection direction ); internal const string C_AREA = "panel/rendering.shader-editor"; + private static readonly Vector4 C_EMPTY_GROUP_HEADER = new(0.16f, 0.17f, 0.20f, EditorPalette.opacityOpaque); private const string C_NODE_WIDTH = "editor.width"; private const float C_DEFAULT_WIDTH = 270f; private const float C_MINIMUM_WIDTH = 180f; @@ -48,14 +47,15 @@ internal void Draw() m_origin = ImGuiApi.GetCursorScreenPosition(); m_size = Vector2.Max(ImGuiApi.GetContentRegionAvailable(), Vector2.One); RefreshPorts(); + RefreshGeometry(); owner.RefreshCompilation(draft); if (draft.frameRequested) { + owner.CompleteCanvasEdit(draft); Frame(); draft.frameRequested = false; } - Dictionary points = PortPositions(); ImGuiApi.SetNextItemAllowOverlap(); _ = ImGuiApi.InvisibleButton("##shader-canvas", m_size, (ImGuiButtonFlags.MouseButtonLeft | ImGuiButtonFlags.MouseButtonMiddle | ImGuiButtonFlags.MouseButtonRight)); bool hovered = ImGuiApi.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem); @@ -73,7 +73,7 @@ internal void Draw() } GraphNodeRecord? hit = hitGroup is null ? HitNode(mouse) : null; - draft.selectedEdge = hit is null ? HitEdge(mouse, points) : null; + draft.selectedEdge = hit is null ? HitEdge(mouse) : null; if (draft.selectedEdge is not null) Canvas.ClearSelection(); if (hit is not null && !Canvas.selectedNodes.Contains(hit.id)) @@ -90,20 +90,20 @@ internal void Draw() } Navigate(hovered && !miniMapHovered); - EditorDrawList draw = Inno.Editor.ImGui.ImGui.GetDrawList(); + EditorDrawList draw = ImGuiApi.GetDrawList(); draw.PushClip(m_origin, m_origin + m_size, true); try { draw.FilledRectangle(m_origin, m_origin + m_size, Color(0.065f, 0.071f, 0.083f), 0, false); Grid(draw); Groups(draw); - Edges(draw, points); + Edges(draw); foreach (GraphNodeRecord node in Controller.document.nodes) - Node(draw, node, points); - Pointer(hovered && !miniMapHovered, points); - if (draft.boxSelecting) + Node(draw, node); + Pointer(hovered && !miniMapHovered); + if (draft.pointer.isBoxSelecting) { - Vector2 min = Vector2.Min(draft.pointerStart, mouse), max = Vector2.Max(draft.pointerStart, mouse); + Vector2 min = Vector2.Min(draft.pointer.boxStart, mouse), max = Vector2.Max(draft.pointer.boxStart, mouse); draw.FilledRectangle(min, max, Color(0.52f, 0.37f, 0.78f, EditorPalette.opacitySubtle), 0, false); draw.Rectangle(min, max, Color(0.65f, 0.47f, 0.88f), 0, 1f); } @@ -417,7 +417,13 @@ GraphPortDirection direction private void Navigate(bool hovered) { - EditorUiInput io = UiDrawing.GetInput(); + if (ImGuiApi.IsAnyPopupOpen()) + { + owner.CompleteCanvasEdit(draft); + draft.navigation.Cancel(); + return; + } + EditorUiInput io = ImGuiApi.GetInput(); draft.navigation.Update( hovered, ImGuiApi.IsMouseClicked(ImGuiMouseButton.Left), @@ -434,49 +440,50 @@ private void Navigate(bool hovered) if (hovered && io.wheel != 0) { + owner.CompleteCanvasEdit(draft); Vector2 pivot = ImGuiApi.GetMousePos() - m_origin; Canvas.ZoomAt(EditorPlanarNavigation.WheelFactor(io.wheel), pivot.X, pivot.Y); } if (hovered && !io.wantsText && ImGuiApi.IsKeyPressed(ImGuiKey.F, false)) + { + owner.CompleteCanvasEdit(draft); Frame(); + } if (!io.wantsText && ImGuiApi.IsKeyPressed(ImGuiKey.Escape, false)) { - draft.dragging = draft.boxSelecting = false; - draft.resizingNode = null; + ShaderEditorDocuments.CancelCanvasEdit(draft); draft.navigation.Cancel(); - draft.dragPreview.Clear(); - Canvas.CancelConnection(); draft.createFromPort = null; + RefreshGeometry(); } } - private void Pointer( - bool hovered, - Dictionary points - ) { - if (draft.navigation.isPanning || UiDrawing.GetInput().alt) + private void Pointer(bool hovered) + { + if (ImGuiApi.IsAnyPopupOpen()) + return; + if (draft.navigation.isPanning || ImGuiApi.GetInput().alt) + { + owner.CompleteCanvasEdit(draft); + return; + } + if (draft.pendingMove is not null) + { + owner.CompleteCanvasEdit(draft); return; + } Vector2 mouse = ImGuiApi.GetMousePos(); if (draft.resizingNode is GraphNodeId resizing) { ImGuiApi.SetMouseCursor(ImGuiMouseCursor.ResizeNwse); if (ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) { - draft.resizePreviewWidth = Math.Clamp(draft.resizeStartWidth + (mouse.X - draft.pointerStart.X) / Canvas.zoom, C_MINIMUM_WIDTH, C_MAXIMUM_WIDTH); + draft.resizePreviewWidth = Math.Clamp(draft.resizeStartWidth + (mouse.X - draft.resizePointerStart.X) / Canvas.zoom, C_MINIMUM_WIDTH, C_MAXIMUM_WIDTH); } else { - GraphNodeRecord? resizedNode = Controller.document.FindNode(resizing); - if (resizedNode is not null && MathF.Abs(NodeWidth(resizedNode, includePreview: false) - draft.resizePreviewWidth) > 0.01f) - { - GraphDocument resizedGraph = Controller.document.Clone(); - resizedGraph.FindNode(resizing)!.SetValue(C_NODE_WIDTH, ShaderGraphDocument.Encode(draft.resizePreviewWidth, owner.serialization, owner.context)); - Controller.ReplaceDocument(resizedGraph, "Resize Shader Node"); - owner.Changed(draft); - } - - draft.resizingNode = null; + owner.CompleteCanvasEdit(draft); } return; @@ -495,195 +502,109 @@ Dictionary points draft.resizingNode = resizeTarget.id; draft.resizeStartWidth = NodeWidth(resizeTarget, includePreview: false); draft.resizePreviewWidth = draft.resizeStartWidth; - draft.pointerStart = mouse; + draft.resizePointerStart = mouse; return; } - PortHandle? hit = HitPort(mouse, points); - if (!draft.readOnly && hit is PortHandle handle && !draft.missingPorts.Contains(handle.endpoint)) - { - Canvas.BeginConnection(handle.endpoint, handle.direction); - return; - } - - GraphNodeRecord? node = HitNode(mouse); - ShaderCanvasGroup? group = node is null ? HitGroupHeader(mouse) : null; + GraphNodeRecord? hitNode = HitNode(mouse); + ShaderCanvasGroup? group = hitNode is null ? HitGroupHeader(mouse) : null; if (group is ShaderCanvasGroup selectedGroup) { draft.selectedEdge = null; draft.selectedGroupId = selectedGroup.id; Canvas.SelectNodes(selectedGroup.nodes.Select(static id => new GraphNodeId(id))); + SynchronizeInspection(); if (!draft.readOnly) - { - draft.dragStart.Clear(); - foreach (GraphNodeId id in Canvas.selectedNodes) - if (Controller.document.FindNode(id) is GraphNodeRecord member) - draft.dragStart[id] = member.position; - draft.dragging = draft.dragStart.Count != 0; - draft.pointerStart = mouse; - } - + draft.pointer.BeginMove(draft.layout, Canvas.selectedNodes, mouse); return; } - - if (node is not null && mouse.Y <= Rect(node).min.Y + C_HEADER * Canvas.zoom) + if (hitNode is null && HitPort(mouse) is null) { draft.selectedGroupId = ""; - draft.selectedEdge = null; - if (UiDrawing.GetInput().shift) - Canvas.ToggleNode(node.id); - else if (!Canvas.selectedNodes.Contains(node.id)) - Canvas.SelectNodes([node.id]); - SetStage(node); - if (draft.readOnly) - return; - draft.dragStart.Clear(); - foreach (GraphNodeId id in Canvas.selectedNodes) - draft.dragStart[id] = Controller.document.FindNode(id)!.position; - draft.dragging = true; - draft.pointerStart = mouse; - } - else if (node is null) - { - draft.selectedGroupId = ""; - if (!UiDrawing.GetInput().shift) - Canvas.ClearSelection(); - draft.selectedEdge = HitEdge(mouse, points); + draft.selectedEdge = HitEdge(mouse); if (draft.selectedEdge is not null) + { + if (!ImGuiApi.GetInput().shift) + Canvas.ClearSelection(); return; - draft.boxSelecting = true; - draft.pointerStart = mouse; + } } } - if (draft.dragging && ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) + GraphCanvasPointerResult intent = draft.pointer.Step(View, draft.layout, new GraphCanvasPointerInput + { + position = mouse, + isHovered = hovered, + isPressed = ImGuiApi.IsMouseClicked(ImGuiMouseButton.Left), + isDown = ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left), + isReleased = ImGuiApi.IsMouseReleased(ImGuiMouseButton.Left), + shift = ImGuiApi.GetInput().shift, + readOnly = draft.readOnly + }); + if (intent.nodePressed is GraphNodeId pressed && Controller.document.FindNode(pressed) is { } node) { - Vector2 delta = (mouse - draft.pointerStart) / Canvas.zoom; - foreach ((GraphNodeId id, GraphPosition position)in draft.dragStart) - draft.dragPreview[id] = new(position.x + delta.X, position.y + delta.Y); + draft.selectedGroupId = ""; + draft.selectedEdge = null; + SetStage(node); + SynchronizeInspection(); + if (!draft.readOnly && !draft.pointer.isMoving) + draft.pointer.BeginMove(draft.layout, Canvas.selectedNodes, mouse); } - - if (!ImGuiApi.IsMouseButtonDown(ImGuiMouseButton.Left)) + if (intent.movedNodes is { } positions) + { + draft.pendingMove = positions; + owner.CompleteCanvasEdit(draft); + } + if (intent.output is GraphEndpoint output && intent.input is GraphEndpoint input) { - if (draft.dragging && draft.dragPreview.Count != 0) + ShaderNodePort outputPort = Port(output); + ShaderNodePort inputPort = Port(input); + if (outputPort.type.IsEquivalentTo(inputPort.type) || inputPort.type.id == "any") { - Controller.MoveNodes(draft.dragPreview); + Controller.Connect(output, input); owner.Changed(draft); } - - if (draft.boxSelecting) - { - Vector2 min = Vector2.Min(draft.pointerStart, mouse), max = Vector2.Max(draft.pointerStart, mouse); - Canvas.SelectNodes(Controller.document.nodes.Where(node => - { - var rect = Rect(node); - return rect.max.X >= min.X && rect.min.X <= max.X && rect.max.Y >= min.Y && rect.min.Y <= max.Y; - }).Select(static node => node.id)); - } - - if (Canvas.pendingConnection is GraphEndpoint sourceEndpoint && Canvas.pendingConnectionDirection is GraphPortDirection sourceDirection) - { - var source = new PortHandle(sourceEndpoint, sourceDirection); - PortHandle? target = HitPort(mouse, points); - if (target is PortHandle destination && destination.direction != source.direction && !draft.missingPorts.Contains(destination.endpoint)) - { - PortHandle output = source.direction == GraphPortDirection.Output ? source : destination; - PortHandle input = source.direction == GraphPortDirection.Input ? source : destination; - ShaderNodePort outputPort = Port(output); - ShaderNodePort inputPort = Port(input); - if (outputPort.type.IsEquivalentTo(inputPort.type) || inputPort.type.id == "any") - { - Controller.Connect(output.endpoint, input.endpoint); - owner.Changed(draft); - } - } - else if (source.direction == GraphPortDirection.Output && target is null && hovered && HitNode(mouse) is null) - { - draft.createFromPort = source.endpoint; - draft.menuPosition = ToGraph(mouse); - ImGuiApi.OpenPopup("##shader-menu"); - } - - Canvas.CancelConnection(); - } - - draft.dragging = draft.boxSelecting = false; - draft.dragPreview.Clear(); + } + if (intent.releasedOutput is GraphEndpoint released) + { + draft.createFromPort = released; + draft.menuPosition = ToGraph(mouse); + ImGuiApi.OpenPopup("##shader-menu"); } } private void Node( EditorDrawList draw, - GraphNodeRecord node, - Dictionary points + GraphNodeRecord node ) { var rect = Rect(node); if (rect.max.X < m_origin.X || rect.min.X > m_origin.X + m_size.X || rect.max.Y < m_origin.Y || rect.min.Y > m_origin.Y + m_size.Y) return; float zoom = Canvas.zoom; - bool selected = Canvas.selectedNodes.Contains(node.id); - draw.FilledRectangle(rect.min + new Vector2(3, 5), rect.max + new Vector2(3, 5), Color(0, 0, 0, EditorPalette.opacityMuted), 6 * zoom, false); - draw.FilledRectangle(rect.min, rect.max, Color(0.115f, 0.12f, 0.14f), 6 * zoom, false); - Vector4 headerColor = NodeHeaderColor(node); - draw.FilledRectangle(rect.min, new(rect.max.X, rect.min.Y + C_HEADER * zoom), ImGuiApi.ToPackedColor(headerColor), 6 * zoom, true); - draw.Rectangle(rect.min, rect.max, selected ? Color(0.65f, 0.47f, 0.88f) : Color(0.26f, 0.27f, 0.31f), 6 * zoom, selected ? 2 : 1); - { - draw.Text(rect.min + new Vector2(10, 7) * zoom, ImGuiApi.GetStyleColor(ImGuiCol.Text), Title(node), (ImGuiApi.GetFontSize() * zoom) / ImGuiApi.GetFontSize()); - } - + if (!draft.layout.TryGetNode(node.id, out GraphCanvasNode? presentation)) + return; + _ = View.DrawNode(presentation, ImGuiApi.ToPackedColor(NodeHeaderColor(node)), Canvas.selectedNodes.Contains(node.id), + previewPosition: draft.pointer.previewPositions.TryGetValue(node.id, out GraphPosition preview) ? preview : null); ShaderNodePort[] nodePorts = draft.ports[node.id]; - bool splitPorts = UsesSplitPortLayout(nodePorts); - float divider = (rect.min.X + rect.max.X) * 0.5f; - float bodyTop = rect.min.Y + C_HEADER * zoom; - if (splitPorts) + if (UsesSplitPortLayout(nodePorts)) { - float bodyPadding = 7f * zoom; - draw.Line(new Vector2(divider, bodyTop + bodyPadding), new Vector2(divider, rect.max.Y - bodyPadding), Color(0.62f, 0.63f, 0.69f, EditorPalette.opacityFaint), 1f); + float divider = (rect.min.X + rect.max.X) * 0.5f; + float padding = 7f * zoom; + draw.Line(new(divider, rect.min.Y + C_HEADER * zoom + padding), new(divider, rect.max.Y - padding), + Color(0.62f, 0.63f, 0.69f, EditorPalette.opacityFaint), 1f); } - foreach (ShaderNodePort port in nodePorts) { - var handle = new PortHandle(new(node.id, new(port.id)), port.direction); - Vector2 point = points[handle]; - bool missing = draft.missingPorts.Contains(new(node.id, new(port.id))); - bool optional = port.direction == GraphPortDirection.Input && !port.required; - uint portColor = missing ? Color(0.95f, 0.35f, 0.3f) : Color(0.60f, 0.48f, 0.84f); - if (missing || !optional) - draw.FilledCircle(point, 5 * zoom, portColor); - else - { - draw.FilledCircle(point, 5 * zoom, portColor); - draw.FilledCircle(point, 2.75f * zoom, Color(0.115f, 0.12f, 0.14f)); - } - - float halfPadding = 12f * zoom; - float portAreaWidth = splitPorts ? (rect.max.X - rect.min.X) * 0.5f : rect.max.X - rect.min.X; - float availableWidth = MathF.Max(1f, portAreaWidth - halfPadding * 2f); - string label = FitPortLabel(port.id, availableWidth, zoom); - float labelWidth = ImGuiApi.MeasureText(label).X * zoom; - bool input = port.direction == GraphPortDirection.Input; - float x = input ? rect.min.X + halfPadding : rect.max.X - labelWidth - halfPadding; - float clipLeft = splitPorts && !input ? divider + 1f : rect.min.X + 1f; - float clipRight = splitPorts && input ? divider - 1f : rect.max.X - 1f; - Vector2 clipMin = new(clipLeft, bodyTop); - Vector2 clipMax = new(clipRight, rect.max.Y); - draw.PushClip(clipMin, clipMax, true); - try - { - draw.Text( - new(x, point.Y - 8 * zoom), - ImGuiApi.ToPackedColor(optional ? EditorPalette.textDisabled : EditorPalette.text), - label, - (ImGuiApi.GetFontSize() * zoom) / ImGuiApi.GetFontSize() - ); - } - - finally + var endpoint = new GraphEndpoint(node.id, new GraphPortId(port.id)); + if (!TryPortPosition(endpoint, port.direction, out Vector2 point)) + continue; + if (Vector2.DistanceSquared(point, ImGuiApi.GetMousePos()) <= 64f) { - draw.PopClip(); + bool missing = draft.missingPorts.Contains(endpoint); + bool optional = port.direction == GraphPortDirection.Input && !port.required; + EditorUI.current.widgets.DrawTooltip(port.type.id + (missing ? " · missing port; reconnect explicitly" + : optional ? " · optional input" : port.direction == GraphPortDirection.Input ? " · required input" : "")); } - if (Vector2.DistanceSquared(point, ImGuiApi.GetMousePos()) <= 64) - EditorUI.current.widgets.DrawTooltip(port.type.id + (missing ? " · missing port; reconnect explicitly" : optional ? " · optional input" : port.direction == GraphPortDirection.Input ? " · required input" : "")); } if (!draft.readOnly) @@ -775,35 +696,77 @@ private void Groups(EditorDrawList draw) return (min - new Vector2(20, 40) * Canvas.zoom, max + new Vector2(20) * Canvas.zoom); } - private Dictionary PortPositions() + private void RefreshGeometry() { - var points = new Dictionary(); + bool changed = draft.layoutRevision != Controller.revision + || draft.layoutAssetRevision != owner.assets.revision || draft.layoutTypeRevision != owner.typeVersion; + if (!changed && draft.resizingNode is GraphNodeId resizing + && draft.layout.TryGetNode(resizing, out GraphCanvasNode? current) + && Controller.document.FindNode(resizing) is { } resizedNode) + { + if (current.size.X != NodeWidth(resizedNode)) + _ = draft.layout.SetNodes(draft.layout.nodes.Select(node => node.id == resizing ? DescribeNode(resizedNode) : node)); + return; + } + if (!changed) + return; + var nodes = new List(Controller.document.nodes.Count); foreach (GraphNodeRecord node in Controller.document.nodes) + nodes.Add(DescribeNode(node)); + _ = draft.layout.SetNodes(nodes); + draft.layoutRevision = Controller.revision; + draft.layoutAssetRevision = owner.assets.revision; + draft.layoutTypeRevision = owner.typeVersion; + + GraphCanvasNode DescribeNode(GraphNodeRecord node) { - var rect = Rect(node); - int left = 0, right = 0; + float width = NodeWidth(node); + bool split = UsesSplitPortLayout(draft.ports[node.id]); + int left = 0; + int right = 0; + var ports = new List(); foreach (ShaderNodePort port in draft.ports[node.id]) { - int row = port.direction == GraphPortDirection.Input ? left++ : right++; - var handle = new PortHandle(new(node.id, new(port.id)), port.direction); - points[handle] = new(port.direction == GraphPortDirection.Input ? rect.min.X : rect.max.X, rect.min.Y + (C_HEADER + C_ROW * (row + 0.5f)) * Canvas.zoom); + bool input = port.direction == GraphPortDirection.Input; + int row = input ? left++ : right++; + float y = C_HEADER + C_ROW * (row + 0.5f); + var endpoint = new GraphEndpoint(node.id, new GraphPortId(port.id)); + ports.Add(new GraphCanvasPort(endpoint, port.direction, new(input ? 0f : width, y), port.id, + input && !port.required, draft.missingPorts.Contains(endpoint), + (new(split && !input ? width * 0.5f : 0f, y - C_ROW * 0.5f), + new(split && input ? width * 0.5f : width, y + C_ROW * 0.5f)))); } + return new GraphCanvasNode(node.id, node.position, + new(width, C_HEADER + Rows(node) * C_ROW + 20f), C_HEADER, Title(node), ports); } - - return points; } - private void Edges( - EditorDrawList draw, - Dictionary points + private bool TryPortPosition( + GraphEndpoint endpoint, + GraphPortDirection direction, + out Vector2 position ) { + if (draft.layout.TryGetNode(endpoint.nodeId, out GraphCanvasNode? node)) + foreach (GraphCanvasPort port in node.ports) + if (port.endpoint == endpoint && port.direction == direction) + { + position = View.ToScreen(draft.pointer.previewPositions.GetValueOrDefault(node.id, node.position)) + port.position * Canvas.zoom; + return true; + } + position = default; + return false; + } + + private void Edges(EditorDrawList draw) + { foreach (GraphEdgeRecord edge in Controller.document.edges) - if (points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) && points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) + if (TryPortPosition(edge.output, GraphPortDirection.Output, out Vector2 a) && TryPortPosition(edge.input, GraphPortDirection.Input, out Vector2 b)) Curve(a, b, edge.id == draft.selectedEdge); if (Canvas.pendingConnection is GraphEndpoint pending && Canvas.pendingConnectionDirection is GraphPortDirection direction - && points.TryGetValue( - new(pending, direction), + && TryPortPosition( + pending, + direction, out Vector2 start )) { @@ -822,13 +785,11 @@ void Curve( } } - private GraphEdgeId? HitEdge( - Vector2 mouse, - Dictionary points - ) { + private GraphEdgeId? HitEdge(Vector2 mouse) + { foreach (GraphEdgeRecord edge in Controller.document.edges) { - if (!points.TryGetValue(new(edge.output, GraphPortDirection.Output), out Vector2 a) || !points.TryGetValue(new(edge.input, GraphPortDirection.Input), out Vector2 b)) + if (!TryPortPosition(edge.output, GraphPortDirection.Output, out Vector2 a) || !TryPortPosition(edge.input, GraphPortDirection.Input, out Vector2 b)) continue; if (GraphCanvasView.HitConnection(mouse, a, b)) return edge.id; @@ -839,12 +800,12 @@ Dictionary points private (Vector2 min, Vector2 max) Rect(GraphNodeRecord node) { - GraphPosition position = draft.dragPreview.GetValueOrDefault(node.id, node.position); - Vector2 min = View.ToScreen(position); - return (min, min + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20) * Canvas.zoom); + if (draft.layout.TryGetNode(node.id, out GraphCanvasNode? presentation)) + return View.NodeBounds(presentation, draft.pointer.previewPositions.TryGetValue(node.id, out GraphPosition preview) ? preview : null); + Vector2 minimum = View.ToScreen(node.position); + return (minimum, minimum + new Vector2(NodeWidth(node), C_HEADER + Rows(node) * C_ROW + 20f) * Canvas.zoom); } - private static float ControlHeight(GraphNodeRecord node) => 0f; private int Rows(GraphNodeRecord node) { int rows = Math.Max( @@ -856,30 +817,10 @@ private int Rows(GraphNodeRecord node) private static bool UsesSplitPortLayout(IEnumerable ports) => ports.Any(static port => port.direction == GraphPortDirection.Input) && ports.Any(static port => port.direction == GraphPortDirection.Output); - private GraphNodeRecord? HitNode(Vector2 point) => Controller.document.nodes.Reverse().FirstOrDefault(node => Contains(Rect(node), point)); - private PortHandle? HitPort( - Vector2 point, - Dictionary ports - ) { - foreach ((PortHandle handle, Vector2 position)in ports) - if (Vector2.DistanceSquared(point, position) <= MathF.Pow(MathF.Max(8, 7 * Canvas.zoom), 2)) - return handle; - foreach ((PortHandle handle, Vector2 position)in ports) - { - GraphNodeRecord node = Controller.document.FindNode(handle.endpoint.nodeId)!; - (Vector2 min, Vector2 max) rect = Rect(node); - bool splitPorts = UsesSplitPortLayout(draft.ports[node.id]); - float divider = (rect.min.X + rect.max.X) * 0.5f; - bool input = handle.direction == GraphPortDirection.Input; - float minimumX = splitPorts && !input ? divider : rect.min.X; - float maximumX = splitPorts && input ? divider : rect.max.X; - float halfRow = C_ROW * Canvas.zoom * 0.5f; - if (point.X >= minimumX && point.X <= maximumX && point.Y >= position.Y - halfRow && point.Y <= position.Y + halfRow) - return handle; - } + private GraphNodeRecord? HitNode(Vector2 point) + => View.HitNode(draft.layout, point) is { } node ? Controller.document.FindNode(node.id) : null; - return null; - } + private PortHandle? HitPort(Vector2 point) => View.HitPort(draft.layout, point) is { } port ? new PortHandle(port.endpoint, port.direction) : null; private GraphNodeRecord? HitResizeHandle(Vector2 point) { @@ -911,31 +852,6 @@ private float NodeWidth( return float.IsFinite(width) ? Math.Clamp(width, C_MINIMUM_WIDTH, C_MAXIMUM_WIDTH) : C_DEFAULT_WIDTH; } - private static string FitPortLabel( - string label, - float availableWidth, - float zoom - ) { - if (ImGuiApi.MeasureText(label).X * zoom <= availableWidth) - return label; - const string ellipsis = "…"; - float ellipsisWidth = ImGuiApi.MeasureText(ellipsis).X * zoom; - if (ellipsisWidth >= availableWidth) - return string.Empty; - int low = 0; - int high = label.Length; - while (low < high) - { - int length = (low + high + 1) / 2; - if (ImGuiApi.MeasureText(label[..length]).X * zoom + ellipsisWidth <= availableWidth) - low = length; - else - high = length - 1; - } - - return label[..low] + ellipsis; - } - private ShaderNodePort Port(PortHandle handle) => draft.ports[handle.endpoint.nodeId].Single(port => port.id == handle.endpoint.portId.value && port.direction == handle.direction); private ShaderNodePort Port(GraphEndpoint endpoint) => draft.ports[endpoint.nodeId].Single(port => port.id == endpoint.portId.value); private GraphPosition ToGraph(Vector2 point) => View.ToGraph(point); @@ -979,7 +895,7 @@ private Vector4 GroupHeaderColor(ShaderCanvasGroup group) } if (count == 0) - return EditorPalette.shaderNodeHeader; + return C_EMPTY_GROUP_HEADER; Vector4 average = total / count; return EditorPalette.WithOpacity(average, EditorPalette.opacityNearOpaque); } @@ -1014,7 +930,7 @@ float hueRange return new(red + match, green + match, blue + match, EditorPalette.opacityOpaque); } - private static bool CenteredAddButton(string label) => EditorUI.current.widgets.CenteredButton(label, EditorUI.current.widgets.style.inspectorAddButtonTopPadding); + private static bool CenteredAddButton(string label) => EditorUI.current.widgets.CenteredButton(label, EditorUI.current.widgets.style.addButtonTopPadding); private T Read( GraphNodeRecord node, string key, @@ -1084,7 +1000,7 @@ private void Frame() return; float minX = selection.Min(static node => node.position.x), minY = selection.Min(static node => node.position.y); float maxX = selection.Max(node => node.position.x + NodeWidth(node)); - float maxY = selection.Max(node => node.position.y + C_HEADER + Rows(node) * C_ROW + ControlHeight(node) + 20); + float maxY = selection.Max(node => node.position.y + C_HEADER + Rows(node) * C_ROW + 20); float scale = Math.Clamp(MathF.Min(MathF.Max(1, m_size.X - 80) / MathF.Max(1, maxX - minX), MathF.Max(1, m_size.Y - 80) / MathF.Max(1, maxY - minY)), 0.1f, 1f); Canvas.SetViewport(new(m_size.X / 2 - (minX + maxX) / 2 * scale, m_size.Y / 2 - (minY + maxY) / 2 * scale), scale); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.CanvasEdits.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.CanvasEdits.cs new file mode 100644 index 000000000..c1b20ea5e --- /dev/null +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.CanvasEdits.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using Inno.Editor.Shaders; +using Inno.Rendering.Shaders; + +namespace Inno.Editor.Panel.ShaderEditor; + +internal sealed partial class ShaderEditorDocuments +{ + internal void CompleteCanvasEdit(Draft draft) + { + GraphDocumentController controller = Controller(draft); + // A preview remains presentation-only until this atomic domain edit succeeds. + // Preserve a released intent when History rejects the operation so it can be retried. + draft.pendingMove ??= draft.pointer.previewPositions.Count == 0 + ? null + : new Dictionary(draft.pointer.previewPositions); + if (draft.pendingMove is { } positions) + { + bool changed = false; + foreach (var entry in positions) + changed |= controller.document.FindNode(entry.Key) is not { } current || current.position != entry.Value; + if (changed) + { + controller.MoveNodes(positions); + Changed(draft); + } + draft.pendingMove = null; + } + if (draft.resizingNode is GraphNodeId resizing + && controller.document.FindNode(resizing) is { } node) + { + GraphDocument replacement = controller.document.Clone(); + replacement.FindNode(node.id)!.SetValue("editor.width", ShaderGraphDocument.Encode(draft.resizePreviewWidth, serialization, context)); + controller.ReplaceDocument(replacement, "Resize Shader Node"); + Changed(draft); + } + CancelCanvasEdit(draft); + } + + private void CompleteCanvasEdits() + { + foreach (Draft draft in m_drafts.Values) + CompleteCanvasEdit(draft); + } + + internal static void CancelCanvasEdit(Draft draft) + { + draft.pendingMove = null; + draft.pointer.Cancel(); + draft.resizingNode = null; + } +} diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs index 25b2ae366..73cb1336b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Documents/ShaderEditorDocuments.cs @@ -1,3 +1,4 @@ +using Inno.Editor.Graph.ImGui; using System; using System.Collections.Generic; using System.Diagnostics; @@ -262,9 +263,7 @@ internal void RemoveNodes(Draft draft) if (draft.activeStage is GraphNodeId stage && controller.document.FindNode(stage) is null) draft.activeStage = null; draft.navigation.Cancel(); - draft.dragging = draft.boxSelecting = false; - draft.dragPreview.Clear(); - draft.canvas.CancelConnection(); + draft.pointer.Cancel(); } internal void Changed(Draft draft) @@ -369,6 +368,8 @@ protected override void OnStart(EditorContext editor) templates = m_lifetime.Own(new ShaderGraphTemplateRegistry(m_types)); targets = m_lifetime.Own(new ShaderTargetRegistry(m_types)); _ = m_lifetime.Own(interactions.documents.RegisterProvider(new Provider(this))); + var operations = m_lifetime.Own(interactions.events.CreateHub(int.MaxValue - 1)); + operations.Listen(_ => CompleteCanvasEdits()); } catch (Exception failure) { @@ -464,6 +465,7 @@ protected override void OnStop(EditorContext editor) // Recovery is not an asset save. Closing the panel, shutdown and reload must not apply a draft. foreach (Draft draft in m_drafts.Values) { + CancelCanvasEdit(draft); if (Controller(draft).isDirty) PreserveRecovery(draft, Controller(draft)); ClearNodeDiagnostics(draft); @@ -694,6 +696,7 @@ public override void Close(EditorDocumentContext context) if (File.Exists(recovery)) File.Delete(recovery); owner.RememberView(draft); + CancelCanvasEdit(draft); ClearNodeDiagnostics(draft); owner.ReleasePreview(draft); draft.navigation.Cancel(); @@ -743,14 +746,17 @@ bool readOnly internal ShaderInspectionSelection? inspectionSelection; internal string valueGesture = Guid.NewGuid().ToString("N"); internal readonly EditorPlanarNavigation navigation = new(); - internal bool dragging; - internal bool boxSelecting; + private GraphCanvasPointerController? m_pointer; + internal GraphCanvasPointerController pointer => m_pointer ??= new(canvas); + internal IReadOnlyDictionary? pendingMove; + internal readonly GraphCanvasLayout layout = new(); + internal ulong layoutRevision = ulong.MaxValue; + internal long layoutAssetRevision = long.MinValue; + internal long layoutTypeRevision = long.MinValue; internal GraphNodeId? resizingNode; internal float resizeStartWidth; internal float resizePreviewWidth; - internal System.Numerics.Vector2 pointerStart; - internal readonly Dictionary dragStart = []; - internal readonly Dictionary dragPreview = []; + internal System.Numerics.Vector2 resizePointerStart; internal ulong portRevision = ulong.MaxValue; internal long assetRevision = long.MinValue; internal long typeRevision = long.MinValue; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs index 25cc4d48e..7c71b5f09 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Inspection/ShaderInspectionDrawers.cs @@ -170,20 +170,20 @@ out AssetInfo? info out AssetFileEntry entry )) { - EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Node selection unavailable"); + EditorUI.current.widgets.ColoredText(EditorPalette.contextText, "Node selection unavailable"); return; } if (!documents.TryOpen(entry, out ShaderEditorDocuments.Draft draft)) { - EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Waiting for Shader asset import…"); + EditorUI.current.widgets.ColoredText(EditorPalette.contextText, "Waiting for Shader asset import…"); return; } if (target.nodes.Count == 1 && documents.Controller(draft).document.FindNode(target.nodes[0]) is GraphNodeRecord node) - EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, "Node: " + new ShaderEditorCanvas(documents, draft).Title(node)); + EditorUI.current.widgets.ColoredText(EditorPalette.contextText, "Node: " + new ShaderEditorCanvas(documents, draft).Title(node)); else - EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, $"Nodes: {target.nodes.Count} selected"); + EditorUI.current.widgets.ColoredText(EditorPalette.contextText, $"Nodes: {target.nodes.Count} selected"); } /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Stats/FrameStatisticsSampler.cs b/src/composition/editor/panels/Inno.Editor.Panel.Stats/FrameStatisticsSampler.cs index c19b0b129..81d04bdf5 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Stats/FrameStatisticsSampler.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Stats/FrameStatisticsSampler.cs @@ -1,49 +1,76 @@ using System; -using System.Collections.Generic; namespace Inno.Editor.Panel.Stats; internal sealed class FrameStatisticsSampler { - private const float C_SAMPLE_WINDOW_SECONDS = 0.5f; + private const int C_SAMPLE_CAPACITY = 120; + private const float C_SAMPLE_INTERVAL_SECONDS = 0.5f; private const float C_SAMPLE_RESET_SECONDS = 1f; - - private readonly Queue m_frameDurations = []; - private double m_sampleDuration; - private float m_lastSampleTime = float.NaN; + private const float C_SAMPLE_WINDOW_SECONDS = 60f; + private readonly float[] m_frameTimes = new float[C_SAMPLE_CAPACITY]; + private readonly float[] m_frameRates = new float[C_SAMPLE_CAPACITY]; + private readonly float[] m_sampleTimes = new float[C_SAMPLE_CAPACITY]; + private float m_lastFrameTime = float.NaN; + private float m_nextSampleTime; + private double m_duration; + private int m_frames; + private int m_count; internal float deltaTime { get; private set; } internal float framesPerSecond { get; private set; } + internal ReadOnlySpan frameTimes => m_frameTimes.AsSpan(0, m_count); + internal ReadOnlySpan frameRates => m_frameRates.AsSpan(0, m_count); - internal void Update( + internal bool Update( float totalTime, float frameDeltaTime ) { - if (!float.IsFinite(frameDeltaTime) || frameDeltaTime <= 0f) - return; - - if (float.IsFinite(m_lastSampleTime) && totalTime - m_lastSampleTime > C_SAMPLE_RESET_SECONDS) + if (!float.IsFinite(totalTime) || !float.IsFinite(frameDeltaTime) || frameDeltaTime <= 0f) + return false; + if (float.IsFinite(m_lastFrameTime) + && (totalTime < m_lastFrameTime || totalTime - m_lastFrameTime > C_SAMPLE_RESET_SECONDS + || totalTime - m_lastFrameTime > frameDeltaTime * 1.5f + 0.0001f)) Reset(); - m_lastSampleTime = totalTime; - - m_frameDurations.Enqueue(frameDeltaTime); - m_sampleDuration += frameDeltaTime; - while (m_frameDurations.Count > 1 && - m_sampleDuration - m_frameDurations.Peek() >= C_SAMPLE_WINDOW_SECONDS) - { - m_sampleDuration -= m_frameDurations.Dequeue(); - } - - double averageFrameDuration = m_sampleDuration / m_frameDurations.Count; - deltaTime = (float)averageFrameDuration; - framesPerSecond = averageFrameDuration > 0d ? (float)(1d / averageFrameDuration) : 0f; + m_lastFrameTime = totalTime; + int expired = 0; + while (expired < m_count && m_sampleTimes[expired] < totalTime - C_SAMPLE_WINDOW_SECONDS) + expired++; + RemoveOldest(expired); + m_duration += frameDeltaTime; + m_frames++; + if (m_count > 0 && totalTime < m_nextSampleTime) + return false; + deltaTime = (float)(m_duration / m_frames); + framesPerSecond = (float)(m_frames / m_duration); + if (m_count == C_SAMPLE_CAPACITY) + RemoveOldest(1); + m_frameTimes[m_count] = deltaTime * 1000f; + m_frameRates[m_count] = framesPerSecond; + m_sampleTimes[m_count++] = totalTime; + m_nextSampleTime = totalTime + C_SAMPLE_INTERVAL_SECONDS; + m_frames = 0; + m_duration = 0d; + return true; } - private void Reset() + internal void Reset() { - m_frameDurations.Clear(); - m_sampleDuration = 0d; + m_count = 0; + m_lastFrameTime = float.NaN; + m_frames = 0; + m_duration = 0d; deltaTime = 0f; framesPerSecond = 0f; } + + private void RemoveOldest(int count) + { + if (count == 0) + return; + m_count -= count; + Array.Copy(m_frameTimes, count, m_frameTimes, 0, m_count); + Array.Copy(m_frameRates, count, m_frameRates, 0, m_count); + Array.Copy(m_sampleTimes, count, m_sampleTimes, 0, m_count); + } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs index 8023e09fc..3839f690c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Stats/StatsPanel.cs @@ -1,4 +1,5 @@ using EditorUI = Inno.Editor.ImGui.EditorUiSession; +using System; using System.Collections.Generic; using System.Linq; using Inno.Editor.Core; @@ -13,7 +14,12 @@ namespace Inno.Editor.Panel.Stats; [EditorPanel("diagnostics.stats", "Stats", order: 500, menuPath: "Diagnostics")] internal sealed class StatsPanel : EditorPanel { + private const float C_ROW_PADDING = 3f; + private readonly FrameStatisticsSampler m_statistics = new(); + private IReadOnlyList? m_previousStatistics; + private StatisticGroup[] m_groups = []; + private (string label, string value)[] m_frameValues = []; /// /// Gets whether use window padding is active for the current instance. /// @@ -34,32 +40,52 @@ internal StatsPanel() /// protected override void OnDraw(EditorContext context) { - m_statistics.Update(context.frame.totalTime, context.frame.deltaTime); - DrawGroup( - "Frame Timing", - [("Time", $"{context.frame.totalTime:F2} s"), ("Delta", $"{m_statistics.deltaTime * 1000f:F2} ms"), ("FPS", $"{m_statistics.framesPerSecond:F1}")] - ); + if (m_statistics.Update(context.frame.totalTime, context.frame.deltaTime)) + { + m_frameValues = + [ + ("Time", $"{context.frame.totalTime:F2} s"), + ("Delta", $"{m_statistics.deltaTime * 1000f:F2} ms"), + ("FPS", $"{m_statistics.framesPerSecond:F1}") + ]; + } + DrawGroup("Frame Timing", m_frameValues); + EditorUI.current.widgets.Trend("Frame time (ms)", m_statistics.frameTimes); + EditorUI.current.widgets.Trend("Frames per second", m_statistics.frameRates); IReadOnlyList statistics = context.statistics.GetSnapshot(); - if (statistics.Count == 0) + if (!ReferenceEquals(statistics, m_previousStatistics)) + { + m_groups = statistics.GroupBy(static statistic => statistic.groupId) + .Select(static group => new StatisticGroup(group.First().groupName, + group.Select(static value => (value.label, value.value)).ToArray())).ToArray(); + m_previousStatistics = statistics; + } + if (m_groups.Length == 0) { EditorUI.current.widgets.Hint("No feature statistics were published for the current frame."); return; } + foreach (StatisticGroup group in m_groups) + DrawGroup(group.title, group.values); + } - foreach (IGrouping group in statistics.GroupBy(static statistic => statistic.groupId)) - { - DrawGroup(group.First().groupName, group.Select(static statistic => (statistic.label, statistic.value))); - } + /// + protected override void OnDetach(EditorContext context) + { + m_statistics.Reset(); + m_previousStatistics = null; + m_groups = []; + m_frameValues = []; } private static void DrawGroup( string title, - IEnumerable<(string label, string value)> values + ReadOnlySpan<(string label, string value)> values ) { EditorUI.current.widgets.CollectionSectionHeader(title); - System.Numerics.Vector2 padding = EditorUI.current.widgets.style.inspectorSectionPadding; + System.Numerics.Vector2 padding = EditorUI.current.widgets.style.sectionPadding; ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.PadOuterX; - Ui.PushStyleVar((ImGuiStyleVar.CellPadding), new System.Numerics.Vector2(padding.X, EditorUI.current.widgets.style.statisticRowPadding)); + Ui.PushStyleVar((ImGuiStyleVar.CellPadding), new System.Numerics.Vector2(padding.X, C_ROW_PADDING * EditorUI.current.widgets.style.zoom)); Ui.PushStyleColor((ImGuiCol.TableRowBg), EditorPalette.collectionRow); Ui.PushStyleColor((ImGuiCol.TableRowBgAlt), EditorPalette.collectionRowAlternate); try @@ -70,7 +96,7 @@ private static void DrawGroup( { Ui.TableSetupColumn("##label", (ImGuiTableColumnFlags.WidthStretch), 0.42f); Ui.TableSetupColumn("##value", (ImGuiTableColumnFlags.WidthStretch), 0.58f); - foreach ((string label, string value)in values) + foreach ((string label, string value) in values) { Ui.TableNextRow(); Ui.TableSetColumnIndex(0); @@ -90,4 +116,9 @@ private static void DrawGroup( Ui.PopStyleVar(); } } + + private sealed record StatisticGroup( + string title, + (string label, string value)[] values + ); } diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj index ca910bbac..601723f9e 100644 --- a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inno.Editor.Assets.ImGui.csproj @@ -11,10 +11,10 @@ - + diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs index e64a28d5a..27a7a6550 100644 --- a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Inspection/AssetSelectionInspectionDrawer.cs @@ -76,7 +76,7 @@ AssetFileEntry target protected override void DrawHeader( InspectionDrawContext context, AssetFileEntry target - ) => EditorUI.current.widgets.ColoredText(EditorPalette.assetBreadcrumbText, target.assetPath.ToString()); + ) => EditorUI.current.widgets.ColoredText(EditorPalette.contextText, target.assetPath.ToString()); /// /// Renders the value presentation for the current editor frame. /// diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs index 930055ea3..747e04fa4 100644 --- a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Properties/ScriptingApi.cs @@ -4,4 +4,5 @@ [assembly: ScriptingApiNamespace("InnoEditor.Assets", "Inno.Editor.Assets.ImGui", ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetBrowserRoot), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetBrowserState), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(AssetBrowserStyle), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(AssetIconAttribute), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs new file mode 100644 index 000000000..5b2d2b7f2 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Assets.ImGui/Styling/AssetBrowserStyle.cs @@ -0,0 +1,158 @@ +using System; +using System.Numerics; +using Inno.Editor.ImGui; + +namespace Inno.Editor.Assets.ImGui; + +/// +/// Freezes Browser-specific layout values from the shared UI scale without retaining a session. +/// The default value uses the standard 100 percent scale. +/// +public readonly struct AssetBrowserStyle +{ + private readonly float m_zoom; + + /// + /// Captures the current shared scale for one Browser presentation pass. + /// + /// + /// The existing session metrics; later mutations do not change this value. + /// + /// + /// The metrics are null. + /// + public AssetBrowserStyle(EditorStyleMetrics metrics) + { + ArgumentNullException.ThrowIfNull(metrics); + m_zoom = metrics.zoom; + } + + /// + /// Gets asset browser window padding. + /// + public Vector2 windowPadding => ScaleVector(new(2f, 1f)); + + /// + /// Gets asset browser cell padding. + /// + public Vector2 cellPadding => ScaleVector(new(5f, 2f)); + + /// + /// Gets asset browser item spacing. + /// + public Vector2 itemSpacing => ScaleVector(new(2f, 2f)); + + /// + /// Gets asset browser frame rounding. + /// + public float frameRounding => Scale(1f); + + /// + /// Gets asset breadcrumb bar height. + /// + public float breadcrumbHeight => Scale(25f); + + /// + /// Gets minimum asset grid cell size. + /// + public float gridMinimumCellSize => Scale(32f); + + /// + /// Gets asset grid cell padding. + /// + public float gridCellPadding => Scale(2f); + + /// + /// Gets default asset grid scale. + /// + public float gridDefaultScale => 3f; + + /// + /// Gets minimum asset grid scale. + /// + public float gridMinimumScale => 1f; + + /// + /// Gets maximum asset grid scale. + /// + public float gridMaximumScale => 10f; + + /// + /// Gets spacing between asset list rows. + /// + public float listRowSpacing => Scale(2f); + + /// + /// Gets the default normalized position of the asset list name/type separator. + /// + public float listNameSeparatorPosition => 0.4f; + + /// + /// Gets the default normalized position of the asset list type/source separator. + /// + public float listTypeSeparatorPosition => 0.7f; + + /// + /// Gets the minimum normalized width reserved for each asset list column. + /// + public float listMinimumColumnRatio => 0.1f; + + /// + /// Gets the horizontal hit width of an asset list column separator. + /// + public float listSeparatorHitWidth => Scale(8f); + + /// + /// Gets the horizontal inset between an asset list separator and column content. + /// + public float listContentHorizontalPadding => Scale(6f); + + /// + /// Gets horizontal padding for asset grid labels. + /// + public float gridLabelHorizontalPadding => Scale(6f); + + /// + /// Gets bottom padding for asset grid labels. + /// + public float gridLabelBottomPadding => Scale(3f); + + /// + /// Gets the additional vertical spacing between asset grid label lines. + /// + public float gridLabelLineSpacing => Scale(-2f); + + /// + /// Gets fixed padding added to calculated asset grid cells. + /// + public float gridFixedCellPadding => Scale(8f); + + /// + /// Gets the scale bias added to asset grid icons. + /// + public float gridScaleBias => 2f; + + /// + /// Gets the top inset reserved above an asset grid icon. + /// + public float gridIconTopPadding => Scale(6f); + + /// + /// Gets the horizontal inset that constrains an asset grid icon inside its card. + /// + public float gridIconHorizontalPadding => Scale(6f); + + /// + /// Gets the vertical spacing between an asset grid icon and its label. + /// + public float gridIconLabelSpacing => Scale(4f); + + /// + /// Gets spacing around breadcrumb separators. + /// + public float breadcrumbSpacing => Scale(4f); + + private float Scale(float value) => value * (m_zoom == 0f ? 1f : m_zoom); + + private Vector2 ScaleVector(Vector2 value) => value * (m_zoom == 0f ? 1f : m_zoom); +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs new file mode 100644 index 000000000..64eb02377 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasLayout.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Inno.Core.Graphs; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Owns a document's neutral node display snapshot, independently of viewport transforms and domain instances. +/// +public sealed class GraphCanvasLayout +{ + private IReadOnlyList m_nodes = Array.Empty(); + private Dictionary m_byId = []; + + /// + /// Gets nodes in draw order; reverse order determines overlapping-node hit priority. + /// + public IReadOnlyList nodes => m_nodes; + + /// + /// Atomically replaces a complete display snapshot, reusing unchanged node geometry by value. + /// + /// + /// The current neutral node descriptions in draw order. + /// + /// + /// The number of inserted, changed or removed nodes. Ordering changes do not rebuild geometry. + /// + /// + /// The collection or one of its nodes is null. + /// + /// + /// A node identity occurs more than once; the previous snapshot remains active. + /// + public int SetNodes(IEnumerable nodes) + { + ArgumentNullException.ThrowIfNull(nodes); + GraphCanvasNode[] candidate = nodes.ToArray(); + var byId = new Dictionary(candidate.Length); + int changes = 0; + for (int index = 0; index < candidate.Length; index++) + { + GraphCanvasNode node = candidate[index]; + ArgumentNullException.ThrowIfNull(node); + if (m_byId.TryGetValue(node.id, out GraphCanvasNode? current) && Equivalent(current, node)) + candidate[index] = node = current; + else + changes++; + if (!byId.TryAdd(node.id, node)) + throw new ArgumentException("A graph layout cannot contain duplicate node identities.", nameof(nodes)); + } + changes += m_byId.Keys.Count(id => !byId.ContainsKey(id)); + if (changes == 0 && m_nodes.SequenceEqual(candidate)) + return 0; + m_nodes = Array.AsReadOnly(candidate); + m_byId = byId; + return changes; + } + + /// + /// Resolves a node in this display snapshot without consulting domain or live-object registries. + /// + /// + /// The graph node identity to resolve. + /// + /// + /// The current neutral node, or null when it is absent. + /// + /// + /// True when the current snapshot contains the node. + /// + public bool TryGetNode( + GraphNodeId id, + [NotNullWhen(true)] out GraphCanvasNode? node + ) => m_byId.TryGetValue(id, out node); + + private static bool Equivalent( + GraphCanvasNode left, + GraphCanvasNode right + ) => ReferenceEquals(left, right) || left.position == right.position && left.size == right.size + && left.headerHeight == right.headerHeight && left.title == right.title && left.ports.SequenceEqual(right.ports); +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs new file mode 100644 index 000000000..61df6f786 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasNode.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Inno.Core.Graphs; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Freezes one node's geometric and textual presentation without owning its domain record. +/// +public sealed class GraphCanvasNode +{ + /// + /// Creates immutable display data that can safely outlive the extension that described it. + /// + /// + /// The existing graph node identity. + /// + /// + /// The node's document position in graph coordinates. + /// + /// + /// Finite positive width and height in graph coordinates. + /// + /// + /// The header extent, within the node height. + /// + /// + /// A display title already interpreted by its domain owner. + /// + /// + /// Immutable neutral endpoint descriptions; the collection is copied. + /// + /// + /// The title or port collection is null. + /// + /// + /// A node or port identity is invalid, duplicated or owned by a different node. + /// + /// + /// The node's geometry is nonfinite or has an invalid extent. + /// + public GraphCanvasNode( + GraphNodeId id, + GraphPosition position, + Vector2 size, + float headerHeight, + string title, + IEnumerable ports + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(id.value); + ArgumentNullException.ThrowIfNull(title); + ArgumentNullException.ThrowIfNull(ports); + if (!float.IsFinite(position.x) || !float.IsFinite(position.y)) + throw new ArgumentOutOfRangeException(nameof(position)); + if (!float.IsFinite(size.X) || !float.IsFinite(size.Y) || size.X <= 0f || size.Y <= 0f) + throw new ArgumentOutOfRangeException(nameof(size)); + if (!float.IsFinite(headerHeight) || headerHeight < 0f || headerHeight > size.Y) + throw new ArgumentOutOfRangeException(nameof(headerHeight)); + GraphCanvasPort[] frozen = ports.ToArray(); + if (frozen.Any(port => port.endpoint.nodeId != id) + || frozen.Select(static port => (port.endpoint, port.direction)).Distinct().Count() != frozen.Length) + throw new ArgumentException("Node ports require unique endpoint/direction pairs owned by the same node.", nameof(ports)); + this.id = id; + this.position = position; + this.size = size; + this.headerHeight = headerHeight; + this.title = title; + this.ports = Array.AsReadOnly(frozen); + } + + /// + /// Gets the stable graph identity represented by this display snapshot. + /// + public GraphNodeId id { get; } + + /// + /// Gets the document position, before transient gesture previews. + /// + public GraphPosition position { get; } + + /// + /// Gets the unscaled node extent. + /// + public Vector2 size { get; } + + /// + /// Gets the unscaled header extent used for node movement hit testing. + /// + public float headerHeight { get; } + + /// + /// Gets the domain-supplied display title. + /// + public string title { get; } + + /// + /// Gets copied neutral port descriptions; no callback or domain object is retained. + /// + public IReadOnlyList ports { get; } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs new file mode 100644 index 000000000..9dc09c562 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerController.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Coordinates neutral selection, movement previews, box selection and connection intents for one canvas. +/// +public sealed class GraphCanvasPointerController +{ + private readonly GraphCanvasState m_state; + private readonly Dictionary m_starts = []; + private readonly Dictionary m_preview = []; + private readonly IReadOnlyDictionary m_previewView; + private Vector2 m_pointerStart; + private bool m_dragging; + private bool m_boxSelecting; + private GraphNodeId[] m_boxSelection = []; + + /// + /// Creates a pointer owner over existing document navigation and selection state. + /// + /// + /// The existing canvas state, which remains the selection and pending-connection authority. + /// + /// + /// The state is null. + /// + public GraphCanvasPointerController(GraphCanvasState state) + { + ArgumentNullException.ThrowIfNull(state); + m_state = state; + m_previewView = new ReadOnlyDictionary(m_preview); + } + + /// + /// Gets frame-local movement previews; callers must not persist this changing view. + /// + public IReadOnlyDictionary previewPositions => m_previewView; + + /// + /// Gets whether a node movement preview is active. + /// + public bool isMoving => m_dragging; + + /// + /// Gets whether a selection box is active. + /// + public bool isBoxSelecting => m_boxSelecting; + + /// + /// Gets the initial screen-space corner of the current selection box. + /// + public Vector2 boxStart => m_pointerStart; + + /// + /// Cancels transient presentation state without applying or undoing document changes. + /// + public void Cancel() + { + m_dragging = m_boxSelecting = false; + m_starts.Clear(); + m_preview.Clear(); + m_boxSelection = []; + m_state.CancelConnection(); + } + + /// + /// Starts movement for explicit node IDs, supporting domain-owned group-header composition. + /// + /// + /// The neutral snapshot supplying initial positions. + /// + /// + /// Selected stable graph identities; absent nodes remain selected but are not moved. + /// + /// + /// The screen-space gesture start. + /// + public void BeginMove( + GraphCanvasLayout layout, + IEnumerable nodes, + Vector2 pointer + ) { + ArgumentNullException.ThrowIfNull(layout); + ArgumentNullException.ThrowIfNull(nodes); + var starts = new Dictionary(); + foreach (GraphNodeId id in nodes) + if (layout.TryGetNode(id, out GraphCanvasNode? node)) + starts.TryAdd(id, node.position); + Cancel(); + foreach (var entry in starts) + m_starts.Add(entry.Key, entry.Value); + m_dragging = m_starts.Count != 0; + m_pointerStart = pointer; + } + + /// + /// Consumes one routed frame and returns data-editing intents without invoking domain code. + /// + /// + /// The current canvas projection. + /// + /// + /// The immutable neutral node snapshot used by drawing and hit testing. + /// + /// + /// Current pointer input after the caller's focus, navigation and overlay routing. + /// + /// + /// Completed neutral intents. The feature owns validation, atomic mutation and History recording. + /// + /// + /// The pointer position is nonfinite. + /// + public GraphCanvasPointerResult Step( + GraphCanvasView view, + GraphCanvasLayout layout, + GraphCanvasPointerInput input + ) { + ArgumentNullException.ThrowIfNull(layout); + if (!float.IsFinite(input.position.X) || !float.IsFinite(input.position.Y)) + throw new ArgumentOutOfRangeException(nameof(input)); + if (input.cancel) + { + Cancel(); + return default; + } + if (input.readOnly && (m_dragging || m_state.pendingConnection is not null)) + Cancel(); + GraphNodeId? pressed = null; + if (input.isPressed && input.isHovered) + { + GraphCanvasPort? port = view.HitPort(layout, input.position); + if (!input.readOnly && port is { isMissing: false } endpoint) + m_state.BeginConnection(endpoint.endpoint, endpoint.direction); + else + { + GraphCanvasNode? node = view.HitNode(layout, input.position); + if (node is not null && input.position.Y <= view.NodeBounds(node).minimum.Y + node.headerHeight * view.zoom) + { + pressed = node.id; + if (input.shift) + m_state.ToggleNode(node.id); + else if (!m_state.selectedNodes.Contains(node.id)) + m_state.SelectNodes([node.id]); + if (!input.readOnly) + BeginMove(layout, m_state.selectedNodes, input.position); + } + else if (node is null) + { + if (!input.shift) + m_state.ClearSelection(); + m_boxSelecting = true; + m_boxSelection = input.shift ? m_state.selectedNodes.ToArray() : []; + m_pointerStart = input.position; + } + } + } + if (m_dragging && input.isDown) + { + GraphPosition start = view.ToGraph(m_pointerStart); + GraphPosition current = view.ToGraph(input.position); + foreach ((GraphNodeId id, GraphPosition position) in m_starts) + m_preview[id] = new(position.x + current.x - start.x, position.y + current.y - start.y); + } + if (!input.isReleased) + return new(pressed, null, null, null, null); + + IReadOnlyDictionary? moved = null; + if (m_dragging && m_preview.Any(entry => m_starts[entry.Key] != entry.Value)) + moved = new ReadOnlyDictionary(new Dictionary(m_preview)); + if (m_boxSelecting) + m_state.SelectNodes(m_boxSelection.Concat(view.BoxNodes(layout, m_pointerStart, input.position)).Distinct()); + GraphEndpoint? output = null; + GraphEndpoint? targetInput = null; + GraphEndpoint? released = null; + if (!input.readOnly && input.isHovered && m_state.pendingConnection is GraphEndpoint source + && m_state.pendingConnectionDirection is GraphPortDirection direction) + { + GraphCanvasPort? destination = view.HitPort(layout, input.position); + if (destination is { isMissing: false } target && target.direction != direction) + { + output = direction == GraphPortDirection.Output ? source : target.endpoint; + targetInput = direction == GraphPortDirection.Input ? source : target.endpoint; + } + else if (destination is null && direction == GraphPortDirection.Output + && input.isHovered && view.HitNode(layout, input.position) is null) + released = source; + } + Cancel(); + return new(pressed, moved, output, targetInput, released); + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs new file mode 100644 index 000000000..9581dd6fd --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerInput.cs @@ -0,0 +1,49 @@ +using System.Numerics; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Carries one frame's already-routed pointer input without depending on a native backend. +/// +public readonly struct GraphCanvasPointerInput +{ + /// + /// Gets the screen-space pointer position. + /// + public Vector2 position { get; init; } + + /// + /// Gets whether this canvas owns the pointer for starting an interaction. + /// + public bool isHovered { get; init; } + + /// + /// Gets whether the primary pointer button was pressed this frame. + /// + public bool isPressed { get; init; } + + /// + /// Gets whether the primary pointer button remains held. + /// + public bool isDown { get; init; } + + /// + /// Gets whether the primary pointer button was released this frame. + /// + public bool isReleased { get; init; } + + /// + /// Gets whether selection extension is requested. + /// + public bool shift { get; init; } + + /// + /// Gets whether the current preview must be canceled without modifying document data. + /// + public bool cancel { get; init; } + + /// + /// Gets whether selection is allowed while data-editing intents are prohibited. + /// + public bool readOnly { get; init; } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs new file mode 100644 index 000000000..3ffdca15c --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPointerResult.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using Inno.Core.Graphs; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Reports neutral interaction intents for the feature controller to validate and apply through History. +/// +public readonly struct GraphCanvasPointerResult +{ + internal GraphCanvasPointerResult( + GraphNodeId? nodePressed, + IReadOnlyDictionary? movedNodes, + GraphEndpoint? output, + GraphEndpoint? input, + GraphEndpoint? releasedOutput + ) { + this.nodePressed = nodePressed; + this.movedNodes = movedNodes; + this.output = output; + this.input = input; + this.releasedOutput = releasedOutput; + } + + /// + /// Gets the pressed node header, or null when no header was pressed. + /// + public GraphNodeId? nodePressed { get; } + + /// + /// Gets completed movement positions, or null when no data edit was requested. + /// + public IReadOnlyDictionary? movedNodes { get; } + + /// + /// Gets a requested connection's output endpoint; its domain type still requires validation. + /// + public GraphEndpoint? output { get; } + + /// + /// Gets a requested connection's input endpoint, paired with the output endpoint. + /// + public GraphEndpoint? input { get; } + + /// + /// Gets an output released over empty canvas, allowing the feature to offer a creation workflow. + /// + public GraphEndpoint? releasedOutput { get; } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs new file mode 100644 index 000000000..59fe75f61 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs @@ -0,0 +1,108 @@ +using System; +using System.Numerics; +using Inno.Core.Graphs; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Describes a neutral port's node-relative geometry and display state without retaining extensions. +/// +public readonly record struct GraphCanvasPort +{ + /// + /// Creates a port presentation whose position is measured in unscaled node coordinates. + /// + /// + /// The existing document endpoint represented by this port. + /// + /// + /// The connection direction, independent of any domain-specific type system. + /// + /// + /// Node-relative graph coordinates of the port center. + /// + /// + /// The current display label, without domain objects or callbacks. + /// + /// + /// Whether to distinguish an optional input visually. + /// + /// + /// Whether retained document structure currently lacks its domain implementation. + /// + /// + /// Optional node-relative label bounds used by drawing and row hit testing; null limits hits to the socket. + /// + /// + /// An endpoint identity is empty. + /// + /// + /// The label is null. + /// + /// + /// The position or direction is invalid. + /// + public GraphCanvasPort( + GraphEndpoint endpoint, + GraphPortDirection direction, + Vector2 position, + string label, + bool isOptional = false, + bool isMissing = false, + (Vector2 minimum, Vector2 maximum)? labelBounds = null + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(endpoint.nodeId.value); + ArgumentException.ThrowIfNullOrWhiteSpace(endpoint.portId.value); + ArgumentNullException.ThrowIfNull(label); + if (!float.IsFinite(position.X) || !float.IsFinite(position.Y)) + throw new ArgumentOutOfRangeException(nameof(position)); + if (direction is not GraphPortDirection.Input and not GraphPortDirection.Output) + throw new ArgumentOutOfRangeException(nameof(direction)); + if (labelBounds is { } bounds && (!float.IsFinite(bounds.minimum.X) || !float.IsFinite(bounds.minimum.Y) + || !float.IsFinite(bounds.maximum.X) || !float.IsFinite(bounds.maximum.Y) + || bounds.maximum.X < bounds.minimum.X || bounds.maximum.Y < bounds.minimum.Y)) + throw new ArgumentOutOfRangeException(nameof(labelBounds)); + this.endpoint = endpoint; + this.direction = direction; + this.position = position; + this.label = label; + this.isOptional = isOptional; + this.isMissing = isMissing; + this.labelBounds = labelBounds; + } + + /// + /// Gets the document endpoint; it does not identify a live engine object. + /// + public GraphEndpoint endpoint { get; } + + /// + /// Gets the neutral connection direction. + /// + public GraphPortDirection direction { get; } + + /// + /// Gets the node-relative center in graph coordinates. + /// + public Vector2 position { get; } + + /// + /// Gets the current display label. + /// + public string label { get; } + + /// + /// Gets whether the input is visually optional. + /// + public bool isOptional { get; } + + /// + /// Gets whether this retained endpoint currently lacks an implementation. + /// + public bool isMissing { get; } + + /// + /// Gets optional node-relative label geometry shared by rendering and row hit testing. + /// + public (Vector2 minimum, Vector2 maximum)? labelBounds { get; } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs new file mode 100644 index 000000000..ee280002b --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.ImGui; +using CanvasDrawing = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.Graph.ImGui; + +/// +/// Draws and hit-tests neutral graph nodes using the frame's frozen viewport projection. +/// +public readonly partial struct GraphCanvasView +{ + /// + /// Projects neutral node geometry, optionally substituting a transient drag position. + /// + /// + /// The immutable display node. + /// + /// + /// An uncommitted gesture position, or null to use the document position. + /// + /// + /// The screen-space node bounds. + /// + public (Vector2 minimum, Vector2 maximum) NodeBounds( + GraphCanvasNode node, + GraphPosition? previewPosition = null + ) { + ArgumentNullException.ThrowIfNull(node); + Vector2 minimum = ToScreen(previewPosition ?? node.position); + return (minimum, minimum + node.size * zoom); + } + + /// + /// Finds the frontmost node beneath a pointer using the same geometry as drawing. + /// + /// + /// The current document presentation snapshot. + /// + /// + /// The screen-space pointer. + /// + /// + /// The frontmost neutral node, or null over empty canvas. + /// + public GraphCanvasNode? HitNode( + GraphCanvasLayout layout, + Vector2 point + ) { + ArgumentNullException.ThrowIfNull(layout); + for (int index = layout.nodes.Count - 1; index >= 0; index--) + { + GraphCanvasNode node = layout.nodes[index]; + var bounds = NodeBounds(node); + if (Contains(bounds, point)) + return node; + } + return null; + } + + /// + /// Finds a port by its displayed socket or label row, without interpreting its domain type. + /// + /// + /// The current document presentation snapshot. + /// + /// + /// The screen-space pointer. + /// + /// + /// The nonnegative minimum socket hit radius in screen pixels. + /// + /// + /// The hit neutral port, including Missing state, or null over non-port content. + /// + /// + /// The radius is negative or nonfinite. + /// + public GraphCanvasPort? HitPort( + GraphCanvasLayout layout, + Vector2 point, + float radius = 8f + ) { + ArgumentNullException.ThrowIfNull(layout); + if (!float.IsFinite(radius) || radius < 0f) + throw new ArgumentOutOfRangeException(nameof(radius)); + float hitRadius = MathF.Max(radius, 7f * zoom); + for (int index = layout.nodes.Count - 1; index >= 0; index--) + { + GraphCanvasNode node = layout.nodes[index]; + Vector2 minimum = ToScreen(node.position); + foreach (GraphCanvasPort port in node.ports) + if (Vector2.DistanceSquared(point, minimum + port.position * m_zoom) <= hitRadius * hitRadius) + return port; + if (!Contains(NodeBounds(node), point)) + continue; + foreach (GraphCanvasPort port in node.ports) + if (port.labelBounds is { } label && Contains( + (ToScreen(node.position) + label.minimum * m_zoom, ToScreen(node.position) + label.maximum * m_zoom), point)) + return port; + // Covered nodes must not receive interaction through the frontmost node's body. + return null; + } + return null; + } + + /// + /// Finds nodes intersecting a screen-space selection rectangle in document draw order. + /// + /// + /// The current neutral display snapshot. + /// + /// + /// One corner of the selection box. + /// + /// + /// The opposite corner, in any drag direction. + /// + /// + /// Stable graph node IDs; no live domain objects are returned. + /// + public IReadOnlyList BoxNodes( + GraphCanvasLayout layout, + Vector2 first, + Vector2 second + ) { + ArgumentNullException.ThrowIfNull(layout); + Vector2 minimum = Vector2.Min(first, second); + Vector2 maximum = Vector2.Max(first, second); + var nodes = new List(); + foreach (GraphCanvasNode node in layout.nodes) + { + var bounds = NodeBounds(node); + if (bounds.maximum.X >= minimum.X && bounds.minimum.X <= maximum.X + && bounds.maximum.Y >= minimum.Y && bounds.minimum.Y <= maximum.Y) + nodes.Add(node.id); + } + return nodes.AsReadOnly(); + } + + /// + /// Draws a visible neutral node and its ports, borrowing optional content only for this call. + /// + /// + /// The immutable geometric and textual node input. + /// + /// + /// The domain-selected packed header color; the presenter does not classify node types. + /// + /// + /// Whether to display the current selection outline. + /// + /// + /// Optional frame-local custom drawing. Exceptions propagate after restoring the node clip. + /// + /// + /// An uncommitted movement position, or null to draw the document position. + /// + /// + /// True when the node intersects the canvas and effective window clip and was submitted; false when culled. + /// + public bool DrawNode( + GraphCanvasNode node, + uint headerColor, + bool selected, + Action? drawContent = null, + GraphPosition? previewPosition = null + ) { + var bounds = NodeBounds(node, previewPosition); + Vector2 minimum = bounds.minimum; + Vector2 maximum = bounds.maximum; + if (m_size.X == 0f || m_size.Y == 0f || maximum.X < m_origin.X || minimum.X > m_origin.X + m_size.X + || maximum.Y < m_origin.Y || minimum.Y > m_origin.Y + m_size.Y) + return false; + var clip = CanvasDrawing.GetClipBounds(); + if (maximum.X < clip.minimum.X || minimum.X > clip.maximum.X || maximum.Y < clip.minimum.Y || minimum.Y > clip.maximum.Y) + return false; + float zoom = this.zoom; + EditorDrawList draw = CanvasDrawing.GetDrawList(); + draw.PushClip(Vector2.Max(m_origin, minimum - new Vector2(6f * zoom)), + Vector2.Min(m_origin + m_size, maximum + new Vector2(6f * zoom)), true); + try + { + draw.FilledRectangle(minimum + new Vector2(3, 5), maximum + new Vector2(3, 5), Pack(0f, 0f, 0f, EditorPalette.opacityMuted), 6f * zoom, false); + draw.FilledRectangle(minimum, maximum, Pack(0.115f, 0.12f, 0.14f), 6f * zoom, false); + draw.FilledRectangle(minimum, new(maximum.X, minimum.Y + node.headerHeight * zoom), headerColor, 6f * zoom, true); + draw.Rectangle(minimum, maximum, selected ? Pack(0.65f, 0.47f, 0.88f) : Pack(0.26f, 0.27f, 0.31f), 6f * zoom, selected ? 2f : 1f); + draw.Text(minimum + new Vector2(10f, 7f) * zoom, CanvasDrawing.GetStyleColor(ImGuiCol.Text), node.title, zoom); + foreach (GraphCanvasPort port in node.ports) + { + Vector2 point = minimum + port.position * zoom; + uint color = port.isMissing ? Pack(0.95f, 0.35f, 0.3f) : Pack(0.60f, 0.48f, 0.84f); + draw.FilledCircle(point, 5f * zoom, color); + if (port.isOptional && !port.isMissing) + draw.FilledCircle(point, 2.75f * zoom, Pack(0.115f, 0.12f, 0.14f)); + if (port.labelBounds is not { } labelBounds) + continue; + Vector2 labelMinimum = minimum + labelBounds.minimum * zoom; + Vector2 labelMaximum = minimum + labelBounds.maximum * zoom; + float padding = 12f * zoom; + string label = FitLabel(port.label, labelMaximum.X - labelMinimum.X - padding * 2f, zoom); + float textX = port.direction == GraphPortDirection.Input ? labelMinimum.X + padding + : labelMaximum.X - padding - CanvasDrawing.MeasureText(label).X * zoom; + draw.PushClip(labelMinimum, labelMaximum, true); + try + { + draw.Text(new Vector2(textX, point.Y - 8f * zoom), + CanvasDrawing.ToPackedColor(port.isOptional ? EditorPalette.textDisabled : EditorPalette.text), label, zoom); + } + finally + { + draw.PopClip(); + } + } + drawContent?.Invoke(); + } + finally + { + draw.PopClip(); + } + return true; + } + + private static string FitLabel( + string label, + float width, + float zoom + ) { + if (CanvasDrawing.MeasureText(label).X * zoom <= width) + return label; + const string ellipsis = "…"; + if (CanvasDrawing.MeasureText(ellipsis).X * zoom > width) + return string.Empty; + int low = 0; + int high = label.Length; + while (low < high) + { + int length = (low + high + 1) / 2; + if (CanvasDrawing.MeasureText(label[..length] + ellipsis).X * zoom <= width) + low = length; + else + high = length - 1; + } + // Avoid splitting a UTF-16 surrogate pair when a label contains supplementary characters. + if (low > 0 && low < label.Length && char.IsHighSurrogate(label[low - 1])) + low--; + return label[..low] + ellipsis; + } + + private static bool Contains( + (Vector2 minimum, Vector2 maximum) bounds, + Vector2 point + ) => point.X >= bounds.minimum.X && point.X <= bounds.maximum.X + && point.Y >= bounds.minimum.Y && point.Y <= bounds.maximum.Y; + + private static uint Pack( + float red, + float green, + float blue, + float alpha = 1f + ) => CanvasDrawing.ToPackedColor(new Vector4(red, green, blue, alpha)); +} diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs index 3a2a94147..aeb330536 100644 --- a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs @@ -8,9 +8,11 @@ namespace Inno.Editor.Graph.ImGui; /// /// Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. /// -public readonly struct GraphCanvasView +public readonly partial struct GraphCanvasView { - private readonly GraphCanvasState m_state; + private const int C_MAX_GRID_LINES_PER_AXIS = 2048; + private readonly GraphPosition m_pan; + private readonly float m_zoom; private readonly Vector2 m_origin; private readonly Vector2 m_size; /// @@ -38,11 +40,20 @@ Vector2 size throw new ArgumentOutOfRangeException(nameof(origin)); if (!float.IsFinite(size.X) || !float.IsFinite(size.Y) || size.X < 0f || size.Y < 0f) throw new ArgumentOutOfRangeException(nameof(size)); - m_state = state; + m_pan = state.pan; + m_zoom = state.zoom; m_origin = origin; m_size = size; } + /// + /// Gets the graph-to-screen zoom captured for this frame, independently of later navigation changes. + /// + /// + /// The view was not initialized through its constructor. + /// + public float zoom => m_zoom > 0f ? m_zoom : throw new InvalidOperationException("A graph canvas view requires an initialized projection."); + /// /// Projects a graph-space location into the current screen rectangle. /// @@ -52,7 +63,7 @@ Vector2 size /// /// The corresponding screen coordinate. /// - public Vector2 ToScreen(GraphPosition point) => m_origin + new Vector2(m_state.pan.x, m_state.pan.y) + new Vector2(point.x, point.y) * m_state.zoom; + public Vector2 ToScreen(GraphPosition point) => m_origin + new Vector2(m_pan.x, m_pan.y) + new Vector2(point.x, point.y) * zoom; /// /// Converts a screen location back into graph coordinates. /// @@ -62,7 +73,7 @@ Vector2 size /// /// The graph coordinate under the pointer. /// - public GraphPosition ToGraph(Vector2 point) => new((point.X - m_origin.X - m_state.pan.x) / m_state.zoom, (point.Y - m_origin.Y - m_state.pan.y) / m_state.zoom); + public GraphPosition ToGraph(Vector2 point) => new((point.X - m_origin.X - m_pan.x) / zoom, (point.Y - m_origin.Y - m_pan.y) / zoom); /// /// Draws a bounded graph-space grid through the current UI drawing context. /// @@ -81,10 +92,10 @@ public void DrawGrid( ) { if (!float.IsFinite(spacing) || spacing <= 0f) throw new ArgumentOutOfRangeException(nameof(spacing)); - float step = MathF.Max(4f, spacing * m_state.zoom); - for (float x = ((m_state.pan.x % step) + step) % step; x < m_size.X; x += step) + float step = MathF.Max(MathF.Max(4f, spacing * zoom), MathF.Max(m_size.X, m_size.Y) / C_MAX_GRID_LINES_PER_AXIS); + for (float x = ((m_pan.x % step) + step) % step; x < m_size.X; x += step) CanvasDrawing.DrawLine(m_origin + new Vector2(x, 0f), m_origin + new Vector2(x, m_size.Y), color); - for (float y = ((m_state.pan.y % step) + step) % step; y < m_size.Y; y += step) + for (float y = ((m_pan.y % step) + step) % step; y < m_size.Y; y += step) CanvasDrawing.DrawLine(m_origin + new Vector2(0f, y), m_origin + new Vector2(m_size.X, y), color); } @@ -103,14 +114,43 @@ public void DrawGrid( /// /// The positive screen-space stroke width. /// + /// + /// An endpoint or derived control point is nonfinite, or the stroke width is not positive and finite. + /// public void DrawConnection( Vector2 output, Vector2 input, uint color, float thickness = 2f ) { + _ = zoom; + if (!float.IsFinite(thickness) || thickness <= 0f) + throw new ArgumentOutOfRangeException(nameof(thickness)); + if (!float.IsFinite(output.X) || !float.IsFinite(output.Y) || !float.IsFinite(input.X) || !float.IsFinite(input.Y)) + throw new ArgumentOutOfRangeException(nameof(output), "Connection endpoints must be finite screen coordinates."); + if (m_size.X == 0f || m_size.Y == 0f) + return; float tangent = Tangent(output, input); - CanvasDrawing.DrawCubicCurve(output, output + new Vector2(tangent, 0f), input - new Vector2(tangent, 0f), input, color, thickness); + Vector2 first = output + new Vector2(tangent, 0f); + Vector2 second = input - new Vector2(tangent, 0f); + if (!float.IsFinite(first.X) || !float.IsFinite(second.X)) + throw new ArgumentOutOfRangeException(nameof(input), "Connection controls exceed finite screen coordinates."); + var clip = CanvasDrawing.GetClipBounds(); + Vector2 minimum = Vector2.Min(Vector2.Min(output, input), Vector2.Min(first, second)) - new Vector2(thickness); + Vector2 maximum = Vector2.Max(Vector2.Max(output, input), Vector2.Max(first, second)) + new Vector2(thickness); + if (maximum.X < MathF.Max(m_origin.X, clip.minimum.X) || minimum.X > MathF.Min(m_origin.X + m_size.X, clip.maximum.X) + || maximum.Y < MathF.Max(m_origin.Y, clip.minimum.Y) || minimum.Y > MathF.Min(m_origin.Y + m_size.Y, clip.maximum.Y)) + return; + var draw = CanvasDrawing.GetDrawList(); + draw.PushClip(m_origin, m_origin + m_size, true); + try + { + CanvasDrawing.DrawCubicCurve(output, first, second, input, color, thickness); + } + finally + { + draw.PopClip(); + } } /// diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs index db9164f54..0fc54eb12 100644 --- a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Properties/ScriptingApi.cs @@ -3,3 +3,9 @@ [assembly: ScriptingApiNamespace("InnoEditor.Graph", "Inno.Editor.Graph.ImGui", ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(GraphCanvasView), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasNode), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasPort), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasLayout), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasPointerInput), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasPointerResult), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(GraphCanvasPointerController), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs index 60448fd4e..8ba5a9a2a 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/EditorDrawList.cs @@ -15,11 +15,9 @@ public readonly unsafe struct EditorDrawList private readonly uint m_window; internal EditorDrawList(EditorUiSession session) { - session.EnsureDrawing(); - if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope == 0) - throw new InvalidOperationException("Drawing requires an active native frame."); + ImGuiNativeContext* context = session.RequireDrawingFrame(); m_session = session; - m_frame = NativeImGui.GetFrameCount(); + m_frame = context->FrameCount; m_window = NativeImGui.Raw.GetCurrentWindow()->ID; } @@ -196,8 +194,8 @@ public void SplitChannels(int count) { if (m_session is null) throw new InvalidOperationException("A default drawing borrow has no owner."); - m_session.EnsureDrawing(); - if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope == 0 || NativeImGui.GetFrameCount() != m_frame || NativeImGui.Raw.GetCurrentWindow()->ID != m_window) + ImGuiNativeContext* context = m_session.RequireDrawingFrame(); + if (context->FrameCount != m_frame || context->CurrentWindow->ID != m_window) throw new InvalidOperationException("The drawing borrow belongs to a different window or frame."); return NativeImGui.Raw.GetWindowDrawList(); } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs index 6d815af16..85cc436ef 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Drawing/ImGui.Drawing.cs @@ -65,9 +65,12 @@ public static (uint id, Vector2 position) GetViewport() /// /// The table's horizontal bounds and current row's vertical bounds. /// + /// + /// No valid drawing frame or active table exists. + /// public static (float minimumX, float maximumX, float rowMinimum, float rowMaximum) GetTableBounds() { - EditorUiSession.current.EnsureDrawing(); + _ = EditorUiSession.current.RequireDrawingFrame(); var table = NativeImGui.Raw.GetCurrentTable(); if (table is null) throw new System.InvalidOperationException("A table row layout requires an active table."); @@ -80,13 +83,32 @@ public static (float minimumX, float maximumX, float rowMinimum, float rowMaximu /// /// Screen-space horizontal bounds and work maximum. /// + /// + /// No valid drawing session or native frame is active. + /// public static (float minimumX, float maximumX, Vector2 workMaximum, Vector2 padding) GetWindowBounds() { - EditorUiSession.current.EnsureDrawing(); + _ = EditorUiSession.current.RequireDrawingFrame(); var window = NativeImGui.Raw.GetCurrentWindow(); return (window->InnerRect.Min.X, window->InnerRect.Max.X, FromNative(window->WorkRect.Max), FromNative(window->WindowPadding)); } + /// + /// Copies the current window's effective clip after parent and viewport clipping. + /// + /// + /// Screen-space minimum and maximum values for bounding frame-local submission work. + /// + /// + /// No valid drawing session or native frame is active. + /// + public static (Vector2 minimum, Vector2 maximum) GetClipBounds() + { + _ = EditorUiSession.current.RequireDrawingFrame(); + var clip = NativeImGui.Raw.GetCurrentWindow()->ClipRect; + return (FromNative(clip.Min), FromNative(clip.Max)); + } + /// /// Borrows drawing commands for the current window and frame without exposing native memory. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs index ab32102f4..ab913acd0 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs @@ -708,6 +708,21 @@ public static void CloseCurrentPopup() RawImGui.CloseCurrentPopup(); } + /// + /// Reads whether an open popup or modal currently prevents underlying canvas and list input. + /// + /// + /// True when any popup is open in this native context, independently of its identifier or nesting. + /// + /// + /// No valid drawing session or native frame is active. + /// + public static bool IsAnyPopupOpen() + { + EditorUiSession.current.EnsureDrawing(); + return RawImGui.IsPopupOpen(string.Empty, (int)(ImGuiPopupFlags.AnyPopupId | ImGuiPopupFlags.AnyPopupLevel)); + } + /// /// Begins a disabled block. /// @@ -926,6 +941,18 @@ public static void SetNextItemWidth(float width) RawImGui.SetNextItemWidth(width); } + /// + /// Requests keyboard focus for the next submitted item in the current frame and window. + /// + /// + /// No matching owner-thread drawing session is active. + /// + public static void FocusNextItem() + { + EditorUiSession.current.EnsureDrawing(); + RawImGui.SetKeyboardFocusHere(); + } + /// /// Gets the remaining content size in the current region. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs index 2ab2d7265..10f5afa90 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Properties/ScriptingApi.cs @@ -8,6 +8,7 @@ [assembly: ScriptingApiExport(typeof(EditorUiSession), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorPalette), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorStyleMetrics), ScriptingApiScope.Editor)] +[assembly: ScriptingApiExport(typeof(ListNavigationResult), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(Inno.Editor.ImGui.ImGui), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(EditorWidget), ScriptingApiScope.Editor)] [assembly: ScriptingApiExport(typeof(InlineRenameResult), ScriptingApiScope.Editor)] diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs index 547b723f8..7b9add853 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs @@ -18,6 +18,7 @@ public sealed unsafe class EditorUiSession : IDisposable private Scope? m_scope; private OwnerScope? m_ownerScope; private nint m_nativeContext; + private int m_styleInputFrame = -1; private bool m_disposed; private string m_owner = "root"; /// @@ -52,6 +53,19 @@ public EditorUiSession() public bool isDisposed => m_disposed; internal string owner => m_owner; + internal bool shouldDeferStyleChanges + { + get + { + EnsureOwner(); + ObjectDisposedException.ThrowIf(m_disposed, this); + if (m_nativeContext == 0) + return false; + ImGuiNativeContext* context = NativeImGui.Raw.GetCurrentContext(); + return context is null || (nint)context != m_nativeContext || context->WithinFrameScope != 0; + } + } + /// /// Activates this session for a synchronous callback while preserving the enclosing scope. /// @@ -120,7 +134,37 @@ internal void RetireOwner(string owner) menus.RetireOwner(owner); } - internal void EnsureDrawing() + internal void EnsureDrawing() => _ = RequireContext(); + + internal void PrepareStyleInput() + { + ImGuiNativeContext* context = RequireContext(); + if (context->WithinFrameScope != 0) + { + if (m_styleInputFrame == context->FrameCount) + return; + m_styleInputFrame = context->FrameCount; + } + else + m_styleInputFrame = context->FrameCount + 1; + widgets.style.CommitPending(); + } + + internal void RequireFramePreparation() + { + if (RequireContext()->WithinFrameScope != 0) + throw new InvalidOperationException("Style preparation must run before native frame creation."); + } + + internal ImGuiNativeContext* RequireDrawingFrame() + { + ImGuiNativeContext* context = RequireContext(); + if (context->WithinFrameScope == 0 || context->CurrentWindow is null) + throw new InvalidOperationException("Drawing requires an active native frame and window."); + return context; + } + + private ImGuiNativeContext* RequireContext() { EnsureOwner(); ObjectDisposedException.ThrowIf(m_disposed, this); @@ -133,6 +177,7 @@ internal void EnsureDrawing() m_nativeContext = context; else if (m_nativeContext != context) throw new InvalidOperationException("The UI session belongs to a different ImGui context."); + return (ImGuiNativeContext*)context; } private void EnsureOwner() diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs index baeb3650e..3b3f5f53d 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/ImGuiEditorRuntime.cs @@ -131,6 +131,23 @@ public void PrepareShutdown() m_runtime.PrepareShutdown(); } + /// + /// Applies pending style inputs with the owned context before the backend creates its native frame. + /// + /// + /// This runtime has retired. + /// + /// + /// The owning native context is unavailable, incorrect, or already drawing. + /// + public void PrepareFrame() + { + using IDisposable uiScope = m_ui.Enter(); + ObjectDisposedException.ThrowIf(m_disposed, this); + m_ui.RequireFramePreparation(); + m_ui.widgets.ApplyPendingStyle(); + } + /// /// Draws the complete editor frame through ImGui. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs index d28bf94fc..5dfe8ceca 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorPalette.cs @@ -100,21 +100,6 @@ public static unsafe class EditorPalette /// public static Vector4 warning { get; } = new(0.9f, 0.65f, 0.25f, opacityOpaque); - /// - /// Gets the neutral Shader node heading surface. - /// - public static Vector4 shaderNodeHeader { get; } = new(0.16f, 0.17f, 0.20f, opacityOpaque); - - /// - /// Gets the orange heading surface shared by Shader input nodes. - /// - public static Vector4 shaderInputNodeHeader { get; } = new(0.43f, 0.245f, 0.085f, opacityOpaque); - - /// - /// Gets the purple heading surface shared by Shader output nodes. - /// - public static Vector4 shaderOutputNodeHeader { get; } = new(0.25f, 0.19f, 0.34f, opacityOpaque); - /// /// Gets the editor window background. /// @@ -296,79 +281,44 @@ public static unsafe class EditorPalette public static Vector4 modalDim { get; } = new(0.8f, 0.8f, 0.8f, opacityMuted); /// - /// Gets inspector card header background. - /// - public static Vector4 inspectorCardHeader { get; } = new(0.17f, 0.18f, 0.205f, opacityOpaque); - - /// - /// Gets the persistent Inspector target header background. - /// - public static Vector4 inspectorTargetHeader { get; } = new(0.14f, 0.14f, 0.16f, opacityOpaque); - - /// - /// Gets the persistent Inspector target header border. - /// - public static Vector4 inspectorTargetHeaderBorder { get; } = new(0.30f, 0.29f, 0.34f, opacityOpaque); - - /// - /// Gets the subdued background of Tag labels in Inspector target headers. - /// - public static Vector4 inspectorTagLabel { get; } = new(0.34f, 0.29f, 0.44f, opacityOpaque); - - /// - /// Gets the subdued background of Layer labels in Inspector target headers. - /// - public static Vector4 inspectorLayerLabel { get; } = new(0.24f, 0.34f, 0.40f, opacityOpaque); - - /// - /// Gets inspector card body background. + /// Gets card header background. /// - public static Vector4 inspectorCardBody { get; } = new(0.12f, 0.12f, 0.14f, opacityOpaque); + public static Vector4 cardHeader { get; } = new(0.17f, 0.18f, 0.205f, opacityOpaque); /// - /// Gets inspector card body border. + /// Gets the persistent target header background. /// - public static Vector4 inspectorCardBodyBorder { get; } = new(0.28f, 0.27f, 0.32f, opacityOpaque); + public static Vector4 targetHeader { get; } = new(0.14f, 0.14f, 0.16f, opacityOpaque); /// - /// Gets the outline color of framed Inspector sections. + /// Gets the persistent target header border. /// - public static Vector4 inspectorSectionBorder { get; } = new(0.34f, 0.33f, 0.39f, opacityOpaque); + public static Vector4 targetHeaderBorder { get; } = new(0.30f, 0.29f, 0.34f, opacityOpaque); /// - /// Gets the accent used by numeric Shader type badges. + /// Gets card body background. /// - public static Vector4 shaderTypeNumeric { get; } = new(0.53f, 0.72f, 0.96f, opacityOpaque); + public static Vector4 cardBody { get; } = new(0.12f, 0.12f, 0.14f, opacityOpaque); /// - /// Gets the accent used by integer Shader type badges. + /// Gets card body border. /// - public static Vector4 shaderTypeInteger { get; } = new(0.58f, 0.82f, 0.72f, opacityOpaque); + public static Vector4 cardBodyBorder { get; } = new(0.28f, 0.27f, 0.32f, opacityOpaque); /// - /// Gets the accent used by Boolean Shader type badges. + /// Gets the outline color of framed sections. /// - public static Vector4 shaderTypeBoolean { get; } = new(0.91f, 0.69f, 0.38f, opacityOpaque); + public static Vector4 sectionBorder { get; } = new(0.34f, 0.33f, 0.39f, opacityOpaque); /// - /// Gets the accent used by Shader resource type badges. + /// Gets disabled card text. /// - public static Vector4 shaderTypeResource { get; } = new(0.58f, 0.78f, 0.86f, opacityOpaque); + public static Vector4 cardDisabledText { get; } = new(0.52f, 0.52f, 0.54f, opacityOpaque); /// - /// Gets the accent used by unclassified Shader type badges. + /// Gets card disclosure hover background. /// - public static Vector4 shaderTypeOther { get; } = new(0.72f, 0.66f, 0.84f, opacityOpaque); - - /// - /// Gets disabled inspector card text. - /// - public static Vector4 inspectorCardDisabledText { get; } = new(0.52f, 0.52f, 0.54f, opacityOpaque); - - /// - /// Gets inspector disclosure hover background. - /// - public static Vector4 inspectorCardDisclosureHovered { get; } = new(0.24f, 0.22f, 0.31f, opacityOpaque); + public static Vector4 cardDisclosureHovered { get; } = new(0.24f, 0.22f, 0.31f, opacityOpaque); /// /// Gets compact control hover color. @@ -391,49 +341,39 @@ public static unsafe class EditorPalette public static Vector4 collectionRowAlternate { get; } = new(0.135f, 0.144f, 0.160f, opacityOpaque); /// - /// Gets the translucent primary background of a complete Settings field. - /// - public static Vector4 settingsField { get; } = new(0.36f, 0.36f, 0.38f, opacityTrace); - - /// - /// Gets the translucent alternate background of a complete Settings field. - /// - public static Vector4 settingsFieldAlternate { get; } = new(0.52f, 0.52f, 0.56f, opacityTrace); - - /// - /// Gets the asset browser field background. + /// Gets the content field background. /// - public static Vector4 assetField { get; } = new(0.235f, 0.22f, 0.27f, opacityOpaque); + public static Vector4 contentField { get; } = new(0.235f, 0.22f, 0.27f, opacityOpaque); /// - /// Gets the asset browser border. + /// Gets the content border. /// - public static Vector4 assetBorder { get; } = new(0.31f, 0.30f, 0.35f, opacityOpaque); + public static Vector4 contentBorder { get; } = new(0.31f, 0.30f, 0.35f, opacityOpaque); /// - /// Gets the soft asset browser border. + /// Gets the soft content border. /// - public static Vector4 assetBorderSoft { get; } = new(0.24f, 0.24f, 0.27f, opacityOpaque); + public static Vector4 contentBorderSoft { get; } = new(0.24f, 0.24f, 0.27f, opacityOpaque); /// - /// Gets the asset browser text color. + /// Gets the content text color. /// - public static Vector4 assetText { get; } = new(0.86f, 0.86f, 0.86f, opacityOpaque); + public static Vector4 contentText { get; } = new(0.86f, 0.86f, 0.86f, opacityOpaque); /// - /// Gets the muted asset browser text color. + /// Gets the muted content text color. /// - public static Vector4 assetTextMuted { get; } = new(0.54f, 0.54f, 0.56f, opacityOpaque); + public static Vector4 contentTextMuted { get; } = new(0.54f, 0.54f, 0.56f, opacityOpaque); /// - /// Gets the subdued text color used by asset browser breadcrumb paths. + /// Gets the subdued contextual text color used by paths and origin hints. /// - public static Vector4 assetBreadcrumbText { get; } = new(0.86f, 0.86f, 0.86f, opacityMedium); + public static Vector4 contextText { get; } = new(0.86f, 0.86f, 0.86f, opacityMedium); /// - /// Gets the opaque asset browser accent. + /// Gets the opaque content accent. /// - public static Vector4 assetAccent { get; } = new(0.50f, 0.45f, 0.62f, opacityOpaque); + public static Vector4 contentAccent { get; } = new(0.50f, 0.45f, 0.62f, opacityOpaque); /// /// Gets a hover treatment derived from a base theme color. @@ -458,17 +398,7 @@ public static unsafe class EditorPalette public static Vector4 GetActive(Vector4 color) => Lerp(color, text, 0.24f); /// - /// Gets scene row background. - /// - public static Vector4 hierarchySceneRow => title; - - /// - /// Gets inactive hierarchy text. - /// - public static Vector4 hierarchyInactiveText { get; } = new(0.52f, 0.52f, 0.54f, opacityOpaque); - - /// - /// Gets hierarchy tree guide color. + /// Gets the guide color of reusable tree controls. /// public static Vector4 treeGuide { get; } = new(0.62f, 0.62f, 0.66f, opacityOpaque); @@ -492,99 +422,6 @@ public static unsafe class EditorPalette /// public static Vector4 axisW { get; } = new(0.48f, 0.48f, 0.50f, opacityOpaque); - /// - /// Gets debug log color. - /// - public static Vector4 logDebug { get; } = new(0.80f, 0.90f, 0.85f, opacityOpaque); - - /// - /// Gets informational log color. - /// - public static Vector4 logInfo { get; } = new(0.20f, 1f, 0.20f, opacityOpaque); - - /// - /// Gets warning log color. - /// - public static Vector4 logWarning { get; } = new(1f, 1f, 0.20f, opacityOpaque); - - /// - /// Gets error log color. - /// - public static Vector4 logError { get; } = new(1f, 0.20f, 0.20f, opacityOpaque); - - /// - /// Gets fatal log color. - /// - public static Vector4 logFatal { get; } = new(1f, 0.20f, 1f, opacityOpaque); - - /// - /// Gets collapsed log card background. - /// - public static Vector4 logCollapsedCard { get; } = new(0.61960787f, 0.5764706f, 0.76862746f, opacityMuted); - - /// - /// Gets collapsed log card border. - /// - public static Vector4 logCollapsedBorder { get; } = new(0.32f, 0.34f, 0.37f, opacityMedium); - - /// - /// Gets the base expanded log card background. - /// - public static Vector4 logExpandedBase { get; } = new(0.10f, 0.10f, 0.10f, opacityOpaque); - - /// - /// Gets the base expanded log card border. - /// - public static Vector4 logExpandedBorderBase { get; } = new(0.24f, 0.24f, 0.24f, opacityOpaque); - - /// - /// Gets log header button background. - /// - public static Vector4 logToggle { get; } = new(0.61960787f, 0.5764706f, 0.76862746f, opacityNone); - - /// - /// Gets hovered log header button background. - /// - public static Vector4 logToggleHovered { get; } = new(0.7372549f, 0.69411767f, 0.8862745f, opacityMedium); - - /// - /// Gets active log header button background. - /// - public static Vector4 logToggleActive { get; } = new(0.8156863f, 0.77254903f, 0.9647059f, opacityMedium); - - /// - /// Gets the expanded log card background derived from a severity color. - /// - /// - /// The palette color representing the log severity. - /// - /// - /// The standard expanded-card base blended with the severity color. - /// - public static Vector4 GetLogExpandedCard(Vector4 severityColor) => Lerp(logExpandedBase, severityColor, 0.12f); - - /// - /// Gets the expanded log card border derived from a severity color. - /// - /// - /// The palette color representing the log severity. - /// - /// - /// The standard expanded-border base blended with the severity color. - /// - public static Vector4 GetLogExpandedBorder(Vector4 severityColor) => Lerp(logExpandedBorderBase, severityColor, 0.20f); - - /// - /// Gets the separator color used inside an expanded log card. - /// - /// - /// The resolved background color of the expanded card. - /// - /// - /// The card color blended toward the palette text color. - /// - public static Vector4 GetLogSeparator(Vector4 cardColor) => Lerp(cardColor, text, 0.12f); - /// /// Linearly interpolates two palette colors using a clamped amount. /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs index 24eb4bb96..03af0b790 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Styling/EditorStyleMetrics.cs @@ -4,8 +4,13 @@ namespace Inno.Editor.ImGui; /// -/// Defines named editor layout metrics used by widgets and feature panels. +/// Defines reusable layout metrics and separates owned frame values from requested style changes. /// +/// +/// Standalone instances apply changes immediately. Session-owned instances defer changes requested +/// during native drawing until the next style preparation. They are mutable only on the owner thread +/// and must not be modified after their session retires. +/// public sealed class EditorStyleMetrics { /// @@ -23,11 +28,25 @@ public sealed class EditorStyleMetrics /// public const float C_ZOOM_STEP = 0.10f; + private readonly WeakReference? m_owner; + private float m_requestedZoom = 1f; + private bool m_requestedCompactMode; private float m_zoom = 1f; private bool m_compactMode; /// - /// Gets the current editor UI zoom multiplier. + /// Creates independent metrics whose changes apply immediately without a native context. + /// + public EditorStyleMetrics() { } + + internal EditorStyleMetrics(EditorUiSession owner) + { + ArgumentNullException.ThrowIfNull(owner); + m_owner = new(owner); + } + + /// + /// Gets the effective zoom used by the current presentation frame. /// public float zoom => m_zoom; @@ -37,66 +56,87 @@ public sealed class EditorStyleMetrics public bool isCompact => m_compactMode; /// - /// Switches between comfortable and compact editor density. + /// Gets the scaled height of a compact sampled-value trend plot. + /// + public float trendHeight => Scale(64f); + + /// + /// Requests comfortable or compact density without changing an active session frame. /// /// /// for compact density. /// /// - /// when the density changed. + /// when the requested density changed; owned drawing applies it at the next frame preparation. /// + /// + /// The owning session has retired or is no longer available. + /// + /// + /// The caller is not the owning session's thread. + /// public bool SetCompactMode(bool value) { - if (m_compactMode == value) - return false; - m_compactMode = value; - return true; + bool defer = ShouldDeferChanges(); + bool changed = m_requestedCompactMode != value; + m_requestedCompactMode = value; + if (!defer) + CommitPending(); + return changed; } /// - /// Sets the editor UI zoom after clamping it to the supported range. + /// Requests a bounded zoom without changing metrics partway through an owned native frame. /// /// /// The requested UI zoom multiplier. /// /// - /// when the effective zoom changed. + /// when the requested zoom changed; owned drawing applies it at the next frame preparation. /// /// /// Thrown when is not finite. /// + /// + /// The owning session has retired or is no longer available. + /// + /// + /// The caller is not the owning session's thread. + /// public bool SetZoom(float value) { if (!float.IsFinite(value)) throw new ArgumentOutOfRangeException(nameof(value), value, "UI zoom must be finite."); + bool defer = ShouldDeferChanges(); float clamped = Math.Clamp(value, C_MIN_ZOOM, C_MAX_ZOOM); - if (MathF.Abs(m_zoom - clamped) < 0.0001f) - return false; - m_zoom = clamped; - return true; + bool changed = MathF.Abs(m_requestedZoom - clamped) >= 0.0001f; + m_requestedZoom = clamped; + if (!defer) + CommitPending(); + return changed; } /// /// Increases editor UI zoom by one bounded increment. /// /// - /// when the zoom changed. + /// when the requested zoom changed. /// - public bool ZoomIn() => SetZoom(m_zoom + C_ZOOM_STEP); + public bool ZoomIn() => SetZoom(m_requestedZoom + C_ZOOM_STEP); /// /// Decreases editor UI zoom by one bounded increment. /// /// - /// when the zoom changed. + /// when the requested zoom changed. /// - public bool ZoomOut() => SetZoom(m_zoom - C_ZOOM_STEP); + public bool ZoomOut() => SetZoom(m_requestedZoom - C_ZOOM_STEP); /// /// Restores the baseline editor UI zoom. /// /// - /// when the zoom changed. + /// when the requested zoom changed. /// public bool ResetZoom() => SetZoom(1f); @@ -206,11 +246,6 @@ public bool SetZoom(float value) /// public Vector2 compactItemSpacing => ScaleVector(new(4f, 2f)); - /// - /// Gets hierarchy item spacing. - /// - public Vector2 hierarchyItemSpacing => ScaleVector(new(3f, 2f)); - /// /// Gets standard inner item spacing. /// @@ -221,31 +256,6 @@ public bool SetZoom(float value) /// public Vector2 cellPadding => ScaleVector(m_compactMode ? new(3f, 1f) : new(5f, 3f)); - /// - /// Gets the inner padding applied to one complete Settings field. - /// - public Vector2 settingsFieldPadding => ScaleVector(new(8f, 6f)); - - /// - /// Gets asset browser window padding. - /// - public Vector2 assetWindowPadding => ScaleVector(new(2f, 1f)); - - /// - /// Gets asset browser cell padding. - /// - public Vector2 assetCellPadding => ScaleVector(new(5f, 2f)); - - /// - /// Gets asset browser item spacing. - /// - public Vector2 assetItemSpacing => ScaleVector(new(2f, 2f)); - - /// - /// Gets asset browser frame rounding. - /// - public float assetFrameRounding => Scale(1f); - /// /// Gets tree indentation. /// @@ -272,79 +282,44 @@ public bool SetZoom(float value) public float grabMinimumSize => Scale(m_compactMode ? 10f : 12f); /// - /// Gets the minimum visible width retained for either asset browser pane while its splitter is dragged. - /// - public float assetPaneMinimumVisibleWidth => Scale(16f); - - /// - /// Gets the minimum draggable asset browser splitter width. - /// - public float assetSplitterMinimumWidth => Scale(2f); - - /// - /// Gets asset breadcrumb bar height. - /// - public float assetBreadcrumbHeight => Scale(25f); - - /// - /// Gets minimum asset grid cell size. - /// - public float assetGridMinimumCellSize => Scale(32f); - - /// - /// Gets asset grid cell padding. - /// - public float assetGridCellPadding => Scale(2f); - - /// - /// Gets default asset grid scale. - /// - public float assetGridDefaultScale => 3f; - - /// - /// Gets minimum asset grid scale. - /// - public float assetGridMinimumScale => 1f; - - /// - /// Gets maximum asset grid scale. + /// Gets the minimum visible width retained for either resizable content pane while its splitter is dragged. /// - public float assetGridMaximumScale => 10f; + public float paneMinimumVisibleWidth => Scale(16f); /// - /// Gets spacing between asset list rows. + /// Gets the minimum draggable resizable content splitter width. /// - public float assetListRowSpacing => Scale(2f); + public float splitterMinimumWidth => Scale(2f); /// - /// Gets spacing between inspector cards. + /// Gets spacing between cards. /// - public float inspectorCardSpacing => Scale(3f); + public float cardSpacing => Scale(3f); /// - /// Gets the inner padding of the persistent Inspector target header. + /// Gets the inner padding of the persistent target header. /// - public Vector2 inspectorTargetHeaderPadding => ScaleVector(new(6f, 4f)); + public Vector2 targetHeaderPadding => ScaleVector(new(6f, 4f)); /// - /// Gets spacing between the two rows of the persistent Inspector target header. + /// Gets spacing between the two rows of the persistent target header. /// - public float inspectorTargetHeaderRowSpacing => Scale(1f); + public float targetHeaderRowSpacing => Scale(1f); /// - /// Gets the font-size multiplier used by the large Inspector target icon. + /// Gets the font-size multiplier used by the large target icon. /// - public float inspectorTargetIconScale => 1.65f; + public float targetIconScale => 1.65f; /// - /// Gets inspector card body padding. + /// Gets card body padding. /// - public Vector2 inspectorCardBodyPadding => ScaleVector(new(7f, 5f)); + public Vector2 cardBodyPadding => ScaleVector(new(7f, 5f)); /// - /// Gets inspector card header padding. + /// Gets card header padding. /// - public Vector2 inspectorCardHeaderPadding => ScaleVector(new(4f, 1f)); + public Vector2 cardHeaderPadding => ScaleVector(new(4f, 1f)); /// /// Gets the shared padding around separator-style section titles. @@ -354,17 +329,17 @@ public bool SetZoom(float value) /// /// Gets the inner padding of a framed Inspector section. /// - public Vector2 inspectorSectionPadding => ScaleVector(new(7f, 5f)); + public Vector2 sectionPadding => ScaleVector(new(7f, 5f)); /// /// Gets the gap between an Inspector section legend and its interrupted top border. /// - public float inspectorSectionLegendGap => itemSpacing.X; + public float sectionLegendGap => itemSpacing.X; /// /// Gets the length of each centered end cap on a collapsed Inspector section. /// - public float inspectorCollapsedSectionCapLength => Scale(10f); + public float collapsedSectionCapLength => Scale(10f); /// /// Gets the spacing around the separator between inline metadata and its value. @@ -374,15 +349,15 @@ public bool SetZoom(float value) /// /// Gets the vertical distance after a framed Inspector section. /// - public float inspectorSectionSpacing => Scale(4f); + public float sectionSpacing => Scale(4f); /// /// Gets the subtle corner rounding used by framed content sections inside editor containers. /// - public float inspectorSectionRounding => Scale(3f); + public float sectionRounding => Scale(3f); /// - /// Gets inspector disclosure inset. + /// Gets card disclosure inset. /// public float disclosureButtonInset => Scale(2f); @@ -391,16 +366,6 @@ public bool SetZoom(float value) /// public float propertyLabelRatio => 0.40f; - /// - /// Gets the distance from the bottom treated as auto-scroll. - /// - public float logAutoScrollTolerance => Scale(1f); - - /// - /// Gets log disclosure padding. - /// - public Vector2 logDisclosurePadding => ScaleVector(new(6f, 1f)); - /// /// Gets the default width of searchable editor popups. /// @@ -419,7 +384,7 @@ public bool SetZoom(float value) /// /// Gets spacing between leading controls inside card headers. /// - public float inspectorHeaderControlSpacing => Scale(4f); + public float headerControlSpacing => Scale(4f); /// /// Gets the inner padding of compact colored label chips. @@ -447,119 +412,29 @@ public bool SetZoom(float value) public float typeBadgeSpacing => Scale(6f); /// - /// Gets the vertical padding inside one statistics row. + /// Gets spacing between distinct control groups in the target header row. /// - public float statisticRowPadding => Scale(3f); + public float headerSectionSpacing => Scale(10f); /// - /// Gets spacing between distinct control groups in an Inspector target header row. + /// Gets top padding above trailing add buttons. /// - public float inspectorHeaderSectionSpacing => Scale(10f); + public float addButtonTopPadding => Scale(7f); /// - /// Gets top padding above inspector add buttons. + /// Gets the tight resizable content toolbar spacing. /// - public float inspectorAddButtonTopPadding => Scale(7f); + public float toolbarTightSpacing => Scale(2f); /// - /// Gets the minimum inline hierarchy rename width. + /// Gets the regular resizable content toolbar spacing. /// - public float hierarchyRenameMinimumWidth => Scale(48f); + public float toolbarSpacing => Scale(4f); /// - /// Gets the hierarchy rename trailing-control gap. + /// Gets spacing between resizable content toolbar sections. /// - public float hierarchyRenameTrailingGap => Scale(8f); - - /// - /// Gets the minimum height of the hierarchy blank drop area. - /// - public float hierarchyBlankMinimumHeight => Scale(24f); - - /// - /// Gets the tight asset browser toolbar spacing. - /// - public float assetToolbarTightSpacing => Scale(2f); - - /// - /// Gets the regular asset browser toolbar spacing. - /// - public float assetToolbarSpacing => Scale(4f); - - /// - /// Gets spacing between asset browser toolbar sections. - /// - public float assetToolbarSectionSpacing => Scale(5f); - - /// - /// Gets the default normalized position of the asset list name/type separator. - /// - public float assetListNameSeparatorPosition => 0.4f; - - /// - /// Gets the default normalized position of the asset list type/source separator. - /// - public float assetListTypeSeparatorPosition => 0.7f; - - /// - /// Gets the minimum normalized width reserved for each asset list column. - /// - public float assetListMinimumColumnRatio => 0.1f; - - /// - /// Gets the horizontal hit width of an asset list column separator. - /// - public float assetListSeparatorHitWidth => Scale(8f); - - /// - /// Gets the horizontal inset between an asset list separator and column content. - /// - public float assetListContentHorizontalPadding => Scale(6f); - - /// - /// Gets horizontal padding for asset grid labels. - /// - public float assetGridLabelHorizontalPadding => Scale(6f); - - /// - /// Gets bottom padding for asset grid labels. - /// - public float assetGridLabelBottomPadding => Scale(3f); - - /// - /// Gets the additional vertical spacing between asset grid label lines. - /// - public float assetGridLabelLineSpacing => Scale(-2f); - - /// - /// Gets fixed padding added to calculated asset grid cells. - /// - public float assetGridFixedCellPadding => Scale(8f); - - /// - /// Gets the scale bias added to asset grid icons. - /// - public float assetGridScaleBias => 2f; - - /// - /// Gets the top inset reserved above an asset grid icon. - /// - public float assetGridIconTopPadding => Scale(6f); - - /// - /// Gets the horizontal inset that constrains an asset grid icon inside its card. - /// - public float assetGridIconHorizontalPadding => Scale(6f); - - /// - /// Gets the vertical spacing between an asset grid icon and its label. - /// - public float assetGridIconLabelSpacing => Scale(4f); - - /// - /// Gets spacing around breadcrumb separators. - /// - public float assetBreadcrumbSpacing => Scale(4f); + public float toolbarSectionSpacing => Scale(5f); /// /// Gets the minimum width of one axis value field. @@ -626,6 +501,21 @@ public bool SetZoom(float value) /// public float textDecorationOffset => Scale(2f); + internal void CommitPending() + { + m_zoom = m_requestedZoom; + m_compactMode = m_requestedCompactMode; + } + + private bool ShouldDeferChanges() + { + if (m_owner is null) + return false; + if (!m_owner.TryGetTarget(out EditorUiSession? owner)) + throw new ObjectDisposedException(nameof(EditorUiSession)); + return owner.shouldDeferStyleChanges; + } + private float Scale(float value) => value * m_zoom; private Vector2 ScaleVector(Vector2 value) => value * m_zoom; diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs index 50c2a2335..d99726228 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Card.cs @@ -60,11 +60,11 @@ public bool CollapsingCard( ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.SpanAvailWidth | ImGuiTreeNodeFlags.AllowOverlap | ImGuiTreeNodeFlags.OpenOnArrow; if (defaultOpen) flags |= ImGuiTreeNodeFlags.DefaultOpen; - NativeImGui.PushStyleColor((int)(ImGuiCol.Header), EditorPalette.inspectorCardHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderHovered), EditorPalette.inspectorCardHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderActive), EditorPalette.inspectorCardHeader); + NativeImGui.PushStyleColor((int)(ImGuiCol.Header), EditorPalette.cardHeader); + NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderHovered), EditorPalette.cardHeader); + NativeImGui.PushStyleColor((int)(ImGuiCol.HeaderActive), EditorPalette.cardHeader); NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.transparent); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inspectorCardHeaderPadding); + NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.cardHeaderPadding); Vector2 itemHeaderMin; Vector2 headerMin; Vector2 headerMax; @@ -80,7 +80,7 @@ public bool CollapsingCard( NativeImGui.Raw.GetWindowDrawList(), persistentHeaderMin, persistentHeaderMax, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardHeader), + NativeImGui.ColorConvertFloat4ToU32(EditorPalette.cardHeader), 0f, 0 ); @@ -94,7 +94,7 @@ public bool CollapsingCard( NativeImGui.Raw.GetWindowDrawList(), headerMin, headerMax, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardBodyBorder), + NativeImGui.ColorConvertFloat4ToU32(EditorPalette.cardBodyBorder), 0f, (int)(ImDrawFlags.RoundCornersNone), style.borderSize @@ -115,7 +115,7 @@ public bool CollapsingCard( float trailingWidth = drawTrailingControl is null ? 0f : trailingControlWidth > 0f ? trailingControlWidth : NativeImGui.GetFrameHeight(); float titleRight = headerMax.X - trailingWidth; if (drawTrailingControl is not null) - titleRight -= style.inspectorHeaderControlSpacing; + titleRight -= style.headerControlSpacing; NativeImGui.SetCursorScreenPos(new Vector2(contentX, contentY)); bool dimmedColorPushed = false; bool groupStarted = false; @@ -123,7 +123,7 @@ public bool CollapsingCard( { if (dimmed) { - NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.inspectorCardDisabledText); + NativeImGui.PushStyleColor((int)(ImGuiCol.Text), EditorPalette.cardDisabledText); dimmedColorPushed = true; } @@ -132,7 +132,7 @@ public bool CollapsingCard( if (drawLeadingControl is not null) { drawLeadingControl(); - NativeImGui.SameLine(0f, style.inspectorHeaderControlSpacing); + NativeImGui.SameLine(0f, style.headerControlSpacing); } NativeImGui.AlignTextToFramePadding(); @@ -210,7 +210,7 @@ public void DrawDisclosureIndicator( drawList, buttonMin, buttonMax, - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardDisclosureHovered), + NativeImGui.ColorConvertFloat4ToU32(EditorPalette.cardDisclosureHovered), NativeImGui.Raw.GetStyle()->FrameRounding, 0 ); @@ -219,7 +219,7 @@ public void DrawDisclosureIndicator( string indicator = open ? "▼" : "▶"; Vector2 indicatorSize = NativeImGui.CalcTextSize(indicator); Vector2 indicatorPosition = buttonMin + (new Vector2(buttonSize) - indicatorSize) * 0.5f; - uint color = NativeImGui.ColorConvertFloat4ToU32(dimmed ? EditorPalette.inspectorCardDisabledText : EditorPalette.text); + uint color = NativeImGui.ColorConvertFloat4ToU32(dimmed ? EditorPalette.cardDisabledText : EditorPalette.text); ImGui.DrawNativeText(drawList, indicatorPosition, color, indicator); } @@ -246,11 +246,11 @@ public void CardBody( Vector2 originalCursor = NativeImGui.GetCursorScreenPos(); (float cardLeft, float cardRight) = GetFullWidthCardBounds(originalCursor); NativeImGui.SetCursorScreenPos(new Vector2(cardLeft, originalCursor.Y)); - NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), EditorPalette.inspectorCardBody); - NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.inspectorCardBodyBorder); + NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), EditorPalette.cardBody); + NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.cardBodyBorder); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameRounding), NativeImGui.Raw.GetStyle()->FrameRounding); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), style.borderSize); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inspectorCardBodyPadding); + NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.cardBodyPadding); ImGuiChildFlags childFlags = ImGuiChildFlags.FrameStyle | ImGuiChildFlags.AutoResizeY; ImGuiWindowFlags windowFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings; bool visible = NativeImGui.BeginChild($"##card_body_{id}", new Vector2(MathF.Max(1f, cardRight - cardLeft), 0f), (int)(childFlags), (int)(windowFlags)); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Clipping.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Clipping.cs new file mode 100644 index 000000000..a4d40f645 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Clipping.cs @@ -0,0 +1,79 @@ +using System; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui.ImGuiWidget; + +/// +/// Submits fixed-height content through the native clipper while retaining explicitly active rows. +/// +public sealed unsafe partial class ImGuiWidget +{ + /// + /// Draws visible row ranges and preserves native scrolling, navigation and table cursor bookkeeping. + /// + /// + /// Call-scoped drawing data, passed directly so static callbacks need no captured closure. + /// + /// + /// The nonnegative count in the caller's current filtered and sorted view. + /// + /// + /// The complete fixed row advance in screen pixels, or -1 to measure the first row through the native clipper. + /// + /// + /// Data borrowed only for this call; it is never stored by the widget. + /// + /// + /// A synchronous drawing callback receiving inclusive start and exclusive end indices. + /// + /// + /// Optional rows that must still submit an active edit, drag source or requested focus. + /// + /// + /// The drawing callback is null. + /// + /// + /// A count, row height or preserved index is invalid. + /// + /// + /// No native frame is active in this UI session. + /// + public void ClippedRows( + int itemCount, + float itemHeight, + TState state, + Action drawRange, + ReadOnlySpan preservedRows = default + ) { + m_session.EnsureDrawing(); + ArgumentNullException.ThrowIfNull(drawRange); + if (itemCount < 0) + throw new ArgumentOutOfRangeException(nameof(itemCount)); + if (!float.IsFinite(itemHeight) || (itemHeight <= 0f && itemHeight != -1f)) + throw new ArgumentOutOfRangeException(nameof(itemHeight)); + foreach (int row in preservedRows) + if (row < 0 || row >= itemCount) + throw new ArgumentOutOfRangeException(nameof(preservedRows)); + if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope == 0) + throw new InvalidOperationException("Row clipping requires an active native frame."); + if (itemCount == 0) + return; + ImGuiListClipper* clipper = NativeImGui.Raw.ImGuiListClipper(); + if (clipper == null) + throw new OutOfMemoryException("The native row clipper could not be allocated."); + try + { + NativeImGui.Raw.Begin(clipper, itemCount, itemHeight); + foreach (int row in preservedRows) + NativeImGui.Raw.IncludeItemByIndex(clipper, row); + while (NativeImGui.Raw.Step(clipper) != 0) + drawRange(state, clipper->DisplayStart, clipper->DisplayEnd); + } + finally + { + // The official destructor ends an unfinished clipper, including callback failures. + NativeImGui.Raw.Destroy(clipper); + } + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs index 818aee0d6..d29200af9 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.ContextMenu.cs @@ -205,7 +205,7 @@ public void EndContextMenu() EndMenuPopup(); } - private bool IsPopupBlockingInteraction() => NativeImGui.IsPopupOpen(string.Empty, (int)(ImGuiPopupFlags.AnyPopupId | ImGuiPopupFlags.AnyPopupLevel)); + private bool IsPopupBlockingInteraction() => ImGui.IsAnyPopupOpen(); private void ConstrainContextMenu() { Vector2 workSize = ImGui.FromNative(NativeImGui.Raw.GetWindowViewport()->WorkSize); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs index e7f17b691..5a159493a 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Controls.cs @@ -163,7 +163,7 @@ public bool CompactCheckbox( if (value) { ImGui.DrawNativeFilledRectangle(drawList, min, max, textColor, 1f, 0); - uint checkColor = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorCardHeader); + uint checkColor = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.cardHeader); ImGui.DrawNativeLine(drawList, min + new Vector2(size * 0.22f, size * 0.52f), min + new Vector2(size * 0.43f, size * 0.72f), checkColor, 1.5f); ImGui.DrawNativeLine(drawList, min + new Vector2(size * 0.43f, size * 0.72f), min + new Vector2(size * 0.80f, size * 0.28f), checkColor, 1.5f); } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs index 36f11df63..eebd74b85 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.HelpBox.cs @@ -83,11 +83,11 @@ public void CollectionSectionHeader(string title) NativeImGui.Raw.GetWindowDrawList(), origin, origin + new Vector2(width, height), - NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow), + NativeImGui.ColorConvertFloat4ToU32(EditorPalette.title), 0, 0 ); - NativeImGui.PushStyleColor((int)(ImGuiCol.Separator), EditorPalette.inspectorSectionBorder); + NativeImGui.PushStyleColor((int)(ImGuiCol.Separator), EditorPalette.sectionBorder); try { NativeImGui.SeparatorText(title); @@ -200,7 +200,7 @@ public bool SectionHeader( Vector2 origin = NativeImGui.GetCursorScreenPos(); float width = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X); float frameHeight = NativeImGui.GetFrameHeight(); - float paddingX = style.inspectorSectionPadding.X; + float paddingX = style.sectionPadding.X; float legendInset = style.sectionHeaderPadding.X; int sectionIndex = layout.sectionIndex++; uint stateId = NativeImGui.GetID($"##inspector_section_{sectionIndex}"); @@ -215,7 +215,7 @@ public bool SectionHeader( if (drawLeadingControl is not null) { drawLeadingControl(); - NativeImGui.SameLine(0f, style.inspectorHeaderControlSpacing); + NativeImGui.SameLine(0f, style.headerControlSpacing); } titlePressed = ClickableText($"inspector_section_title_{sectionIndex}", title, new Vector2(MathF.Max(1f, titleSize.X), frameHeight), description); @@ -236,18 +236,18 @@ public bool SectionHeader( Vector2 contentCursor = NativeImGui.GetCursorScreenPos(); contentCursor.X = origin.X; float top = origin.Y + frameHeight * 0.5f; - float legendLeft = legendMin.X - style.inspectorSectionLegendGap; - float legendRight = legendMax.X + style.inspectorSectionLegendGap; + float legendLeft = legendMin.X - style.sectionLegendGap; + float legendRight = legendMax.X + style.sectionLegendGap; NativeImGui.SetCursorScreenPos(contentCursor); layout.contentVisible = open; if (!open) { DrawCollapsedSectionBorder(NativeImGui.Raw.GetWindowDrawList(), origin.X, origin.X + width, top, legendLeft, legendRight); - NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionSpacing)); + NativeImGui.Dummy(new Vector2(0f, style.sectionSpacing)); return false; } - NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionPadding.Y)); + NativeImGui.Dummy(new Vector2(0f, style.sectionPadding.Y)); NativeImGui.Indent(paddingX); layout.active = true; layout.left = origin.X; @@ -266,7 +266,7 @@ private void DrawCollapsedSectionBorder( float legendLeft, float legendRight ) { - uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorSectionBorder); + uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.sectionBorder); DrawCappedSectionSegment(draw, left, MathF.Max(left, legendLeft), top, capAtStart: true, color); DrawCappedSectionSegment(draw, MathF.Min(right, legendRight), right, top, capAtStart: false, color); } @@ -281,7 +281,7 @@ uint color ) { float thickness = style.borderSize; float halfThickness = thickness * 0.5f; - float halfCap = style.inspectorCollapsedSectionCapLength * 0.5f; + float halfCap = style.collapsedSectionCapLength * 0.5f; float capX = capAtStart ? from : to; float innerX = capAtStart ? MathF.Min(to, capX + halfThickness) : MathF.Max(from, capX - halfThickness); ImGui.DrawNativeLine(draw, new Vector2(capX, centerY - halfCap), new Vector2(capX, centerY + halfCap), color, thickness); @@ -292,12 +292,12 @@ private void CompleteSection(SectionLayoutState layout) { if (!layout.active) return; - NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionPadding.Y)); - NativeImGui.Unindent(style.inspectorSectionPadding.X); + NativeImGui.Dummy(new Vector2(0f, style.sectionPadding.Y)); + NativeImGui.Unindent(style.sectionPadding.X); float bottom = MathF.Max(NativeImGui.GetCursorScreenPos().Y, layout.top + NativeImGui.GetFrameHeight()); - uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.inspectorSectionBorder); + uint color = NativeImGui.ColorConvertFloat4ToU32(EditorPalette.sectionBorder); ImDrawList* draw = NativeImGui.Raw.GetWindowDrawList(); - float radius = MathF.Min(style.inspectorSectionRounding, MathF.Max(0f, MathF.Min(layout.right - layout.left, bottom - layout.top) * 0.5f)); + float radius = MathF.Min(style.sectionRounding, MathF.Max(0f, MathF.Min(layout.right - layout.left, bottom - layout.top) * 0.5f)); float leftLegend = MathF.Max(layout.left + radius, layout.legendLeft); float rightLegend = MathF.Min(layout.right - radius, layout.legendRight); ImGui.DrawNativePathLine(draw, new Vector2(leftLegend, layout.top)); @@ -311,7 +311,7 @@ private void CompleteSection(SectionLayoutState layout) ImGui.DrawNativePathQuadratic(draw, new Vector2(layout.right, layout.top), new Vector2(layout.right - radius, layout.top), 0); ImGui.DrawNativePathLine(draw, new Vector2(rightLegend, layout.top)); NativeImGui.Raw.PathStroke(draw, color, (int)(ImDrawFlags.None), style.borderSize); - NativeImGui.Dummy(new Vector2(0f, style.inspectorSectionSpacing)); + NativeImGui.Dummy(new Vector2(0f, style.sectionSpacing)); layout.active = false; layout.contentVisible = true; } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs index ec76258fe..fb6934f99 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Lifetime.cs @@ -11,6 +11,7 @@ public sealed unsafe partial class ImGuiWidget internal ImGuiWidget(EditorUiSession session) { m_session = session ?? throw new ArgumentNullException(nameof(session)); + style = new EditorStyleMetrics(session); } internal void RetireOwner(string owner) diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs index e26b27f68..c6e85ce7a 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Panel.cs @@ -170,9 +170,9 @@ public void HeaderSurface( Vector2 headerOrigin = contentCursor - parentPadding; float width = MathF.Max(1f, NativeImGui.GetContentRegionAvail().X + parentPadding.X * 2f); NativeImGui.SetCursorScreenPos(headerOrigin); - NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), EditorPalette.inspectorTargetHeader); - NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.inspectorTargetHeaderBorder); - NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.inspectorTargetHeaderPadding); + NativeImGui.PushStyleColor((int)(ImGuiCol.FrameBg), EditorPalette.targetHeader); + NativeImGui.PushStyleColor((int)(ImGuiCol.Border), EditorPalette.targetHeaderBorder); + NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FramePadding), style.targetHeaderPadding); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameBorderSize), style.borderSize); NativeImGui.PushStyleVar((int)(ImGuiStyleVar.FrameRounding), 0f); try diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs index 11d479d53..692b20460 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Search.cs @@ -11,6 +11,9 @@ public sealed partial class ImGuiWidget /// /// Draws a single-line search field with a stable identifier. /// + /// + /// Escape clears a nonempty query before leaving text input on a subsequent press. + /// /// /// The stable control identifier. /// @@ -39,7 +42,7 @@ public bool SearchInput( m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); NativeImGui.SetNextItemWidth(width < 0f ? MathF.Max(1f, NativeImGui.GetContentRegionAvail().X) : width); - return ImGuiUtf8Buffer.InputText($"##search_{id}", hint, ref query, capacity, ImGuiInputTextFlags.None); + return ImGuiUtf8Buffer.InputText($"##search_{id}", hint, ref query, capacity, ImGuiInputTextFlags.EscapeClearsAll); } /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs index 993ecc401..5091dbb35 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Inno.Core.Identity; +using Inno.Core.Input; +using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Native.ImGui; using NativeImGui = Inno.Native.ImGui.ImGui; @@ -21,19 +23,19 @@ public sealed unsafe partial class ImGuiWidget /// The freshly resolved clicked target. /// /// - /// Current view order; collapsed or filtered items must be omitted. + /// Current runtime-identity view order; collapsed or filtered items must be omitted. /// public void SelectTarget( EditorInteractions interactions, IdentityObject target, - IReadOnlyList visibleTargets + IReadOnlyList visibleTargets ) { m_session.EnsureDrawing(); ArgumentNullException.ThrowIfNull(interactions); ArgumentNullException.ThrowIfNull(target); ArgumentNullException.ThrowIfNull(visibleTargets); ImGuiIO* io = NativeImGui.Raw.GetIO(); - bool additive = io->KeyCtrl != 0 || io->KeySuper != 0; + bool additive = HasPrimaryModifier(interactions.keyboard, io); if (io->KeyShift != 0) interactions.SelectRange(visibleTargets, target, additive); else if (additive) @@ -41,4 +43,178 @@ IReadOnlyList visibleTargets else interactions.SetSelection(target); } + + /// + /// Reads one focused list's keyboard navigation without retaining its entries or changing domain selection. + /// + /// + /// The authoritative interaction owner supplying the product modifier policy. + /// + /// + /// The number of currently visible, ordered entries. + /// + /// + /// The current visible focus index, or minus one when absent. + /// + /// + /// The positive number of visible rows advanced by Page Up or Page Down. + /// + /// + /// Neutral focus and selection intents; text editing, active controls and popups produce no intent. + /// + /// + /// The interaction owner is null. + /// + /// + /// The count, focus index or page size is invalid. + /// + /// + /// No matching owner-thread drawing session is active. + /// + public ListNavigationResult NavigateList( + EditorInteractions interactions, + int itemCount, + int focusedIndex, + int pageSize = 10 + ) { + m_session.EnsureDrawing(); + ArgumentNullException.ThrowIfNull(interactions); + ArgumentOutOfRangeException.ThrowIfNegative(itemCount); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(pageSize); + if (focusedIndex < -1 || focusedIndex >= itemCount && focusedIndex != -1) + throw new ArgumentOutOfRangeException(nameof(focusedIndex)); + ImGuiIO* io = NativeImGui.Raw.GetIO(); + ImGuiNativeContext* context = NativeImGui.Raw.GetCurrentContext(); + bool ownsActiveRow = RouteTreeNavigationKeys(context); + if (!NativeImGui.IsWindowFocused((int)Inno.Native.ImGui.ImGuiFocusedFlags.ChildWindows) + || io->WantTextInput != 0 || (context->ActiveId != 0 && !ownsActiveRow) || ImGui.IsAnyPopupOpen()) + return new(focusedIndex, false, false, false, false, false, false, false); + int next = focusedIndex; + bool navigation = false; + if (itemCount > 0) + { + navigation = true; + if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.DownArrow)) + next = focusedIndex < 0 ? 0 : Math.Min(itemCount - 1, focusedIndex + 1); + else if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.UpArrow)) + next = focusedIndex < 0 ? itemCount - 1 : Math.Max(0, focusedIndex - 1); + else if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.Home)) + next = 0; + else if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.End)) + next = itemCount - 1; + else if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.PageDown)) + next = focusedIndex < 0 ? 0 : (int)Math.Min(itemCount - 1L, (long)focusedIndex + pageSize); + else if (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.PageUp)) + next = focusedIndex < 0 ? 0 : Math.Max(0, focusedIndex - pageSize); + else + navigation = false; + } + bool primary = HasPrimaryModifier(interactions.keyboard, io); + bool extend = io->KeyShift != 0; + bool space = itemCount > 0 && NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.Space, false); + bool activate = itemCount > 0 && (NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.Enter, false) + || NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.KeypadEnter, false)); + bool cancel = NativeImGui.IsKeyPressed(Inno.Native.ImGui.ImGuiKey.Escape, false); + if ((space || activate) && next < 0) + next = 0; + return new(next, next != focusedIndex, space || navigation && (!primary || extend), + extend, primary, space && primary, activate, cancel); + } + + private bool RouteTreeNavigationKeys(ImGuiNativeContext* context) + { + uint window = NativeImGui.Raw.GetCurrentWindow()->ID; + if (!m_treeStatesByWindow.TryGetValue((m_session.owner, window), out TreeWidgetWindowState? state) + || state.lastFrame != NativeImGui.GetFrameCount() + || !NativeImGui.IsWindowFocused((int)Inno.Native.ImGui.ImGuiFocusedFlags.ChildWindows) + || NativeImGui.Raw.GetIO()->WantTextInput != 0 || ImGui.IsAnyPopupOpen()) + return false; + bool ownsActiveRow = context->ActiveId != 0 + && context->ActiveIdSource == ImGuiInputSource.Keyboard && state.navigationItemIds.Contains(context->ActiveId); + if (state.navigationItemIds.Contains(context->NavId) && (context->ActiveId == 0 || ownsActiveRow)) + { + // Native navigation must not activate the disclosure while the list owns selection/activation. + // A distinct owner preserves text fields and overlay buttons, including their native navigation. + uint owner = NativeImGui.GetID("##inno_list_navigation"); + NativeImGui.SetKeyOwner(Inno.Native.ImGui.ImGuiKey.Space, owner, 0); + NativeImGui.SetKeyOwner(Inno.Native.ImGui.ImGuiKey.Enter, owner, 0); + NativeImGui.SetKeyOwner(Inno.Native.ImGui.ImGuiKey.KeypadEnter, owner, 0); + } + return ownsActiveRow; + } + + private static bool HasPrimaryModifier( + EditorKeyboardPolicy keyboard, + ImGuiIO* io + ) { + KeyModifier modifiers = KeyModifier.None; + if (io->KeyCtrl != 0) + modifiers |= KeyModifier.Control; + if (io->KeySuper != 0) + modifiers |= KeyModifier.Super; + if (io->KeyAlt != 0) + modifiers |= KeyModifier.Alt; + if (io->KeyShift != 0) + modifiers |= KeyModifier.Shift; + return (modifiers & keyboard.primaryModifier) == keyboard.primaryModifier; + } +} + +/// +/// Carries frame-local list navigation intents over visible indices without owning objects or selection state. +/// +public readonly struct ListNavigationResult +{ + internal ListNavigationResult( + int focusedIndex, + bool focusChanged, + bool selectionRequested, + bool extendRange, + bool additive, + bool toggle, + bool activate, + bool cancel + ) { + this.focusedIndex = focusedIndex; + this.focusChanged = focusChanged; + this.selectionRequested = selectionRequested; + this.extendRange = extendRange; + this.additive = additive; + this.toggle = toggle; + this.activate = activate; + this.cancel = cancel; + } + + /// + /// Gets the resulting visible focus index, or minus one for no focus. + /// + public int focusedIndex { get; } + /// + /// Gets whether the visible focus index changed. + /// + public bool focusChanged { get; } + /// + /// Gets whether the caller should apply a domain selection operation. + /// + public bool selectionRequested { get; } + /// + /// Gets whether the selection should extend from its existing anchor. + /// + public bool extendRange { get; } + /// + /// Gets whether the product's primary modifier requests preserving other selected entries. + /// + public bool additive { get; } + /// + /// Gets whether Space with the primary modifier requests toggling the focused entry. + /// + public bool toggle { get; } + /// + /// Gets whether Enter requests activation of the focused entry. + /// + public bool activate { get; } + /// + /// Gets whether Escape requests canceling the current list workflow. + /// + public bool cancel { get; } } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs index 31c078b1e..0f0ca7bbd 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Style.cs @@ -14,7 +14,7 @@ public sealed unsafe partial class ImGuiWidget /// /// Gets the centralized editor layout metrics shared by every widget and feature panel. /// - public EditorStyleMetrics style { get; } = new(); + public EditorStyleMetrics style { get; } /// /// Applies the centralized editor layout metrics and palette to the current ImGui context. @@ -22,6 +22,7 @@ public sealed unsafe partial class ImGuiWidget public void SetupStyle() { m_session.EnsureDrawing(); + m_session.PrepareStyleInput(); ImGuiStyle* nativeStyle = NativeImGui.Raw.GetStyle(); ApplyLayoutStyle(nativeStyle); EditorPalette.Apply(nativeStyle); @@ -31,6 +32,7 @@ public void SetupStyle() internal void ApplyPendingStyle() { + m_session.PrepareStyleInput(); if (float.IsNaN(m_appliedZoom) || MathF.Abs(m_appliedZoom - style.zoom) > 0.0001f || m_appliedCompactMode != style.isCompact) SetupStyle(); } diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs index acedaa69b..b2bc23807 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Tree.cs @@ -28,7 +28,7 @@ public sealed unsafe partial class ImGuiWidget /// Stable row identifier. /// /// - /// Content drawing callback that receives the native row geometry established by ImGui. + /// Frame-local content drawing for visible or explicitly preserved rows; clipped ordinary rows do not invoke it. /// /// /// Tree row options. @@ -36,22 +36,128 @@ public sealed unsafe partial class ImGuiWidget /// /// Interaction and row geometry for the submitted item. /// + /// + /// An explicit flattened depth is negative. + /// public TreeNodeResult TreeNode( string id, Action onDraw, in TreeNodeOptions options + ) { + m_session.EnsureDrawing(); + if (options.depth < 0) + throw new ArgumentOutOfRangeException(nameof(options), "A flattened tree depth cannot be negative."); + float indentation = (options.depth ?? 0) * NativeImGui.Raw.GetStyle()->IndentSpacing; + if (indentation > 0f) + NativeImGui.Indent(indentation); + try + { + return DrawTreeRow(id, onDraw, options); + } + finally + { + if (indentation > 0f) + NativeImGui.Unindent(indentation); + } + } + + /// + /// Gets this window and owner's expansion change counter for invalidating neutral flattened views. + /// + /// + /// No valid drawing session or native frame is active. + /// + public ulong treeExpansionRevision + { + get + { + m_session.EnsureDrawing(); + return GetTreeWindowState().expansionRevision; + } + } + + /// + /// Reads a retained expansion value without submitting a native row or retaining domain objects. + /// + /// + /// The stable row identifier within this window and owner. + /// + /// + /// True when the row was explicitly expanded; unknown rows are collapsed. + /// + /// + /// The row identifier is empty or whitespace. + /// + /// + /// No valid drawing session or native frame is active. + /// + public bool IsTreeNodeOpen(string id) + { + m_session.EnsureDrawing(); + ArgumentException.ThrowIfNullOrWhiteSpace(id); + return m_openStatesById.GetValueOrDefault(id); + } + + /// + /// Changes retained expansion before a flattened view is computed, without submitting a row. + /// + /// + /// The stable row identifier within this window and owner. + /// + /// + /// The requested disclosure state. + /// + /// + /// The row identifier is empty or whitespace. + /// + /// + /// No valid drawing session or native frame is active. + /// + public void SetTreeNodeOpen( + string id, + bool open + ) { + m_session.EnsureDrawing(); + ArgumentException.ThrowIfNullOrWhiteSpace(id); + if (m_openStatesById.GetValueOrDefault(id) == open) + return; + m_openStatesById[id] = open; + GetTreeWindowState().expansionRevision++; + } + + private TreeNodeResult DrawTreeRow( + string id, + Action onDraw, + in TreeNodeOptions options ) { m_session.EnsureDrawing(); ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(onDraw); bool isLeaf = options.isLeaf; BeginTreeFrameIfNeeded(); + ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); + Vector2 cursor = NativeImGui.GetCursorScreenPos(); + TreeWidgetWindowState state = GetTreeWindowState(); + bool visible = cursor.Y <= window->ClipRect.Max.Y + && cursor.Y + NativeImGui.GetFrameHeight() >= window->ClipRect.Min.Y; + bool drawContent = visible || options.requestFocus || options.keepContentActive + || state.activeContentOwner == id; + uint activeBefore = NativeImGui.Raw.GetCurrentContext()->ActiveId; + if (!drawContent) + return DrawTreeNode(id, onDraw, options, isLeaf, null, false); ImDrawList* drawList = NativeImGui.Raw.GetWindowDrawList(); NativeImGui.Raw.ChannelsSplit(drawList, 2); NativeImGui.Raw.ChannelsSetCurrent(drawList, 1); try { - return DrawTreeNode(id, onDraw, options, isLeaf, drawList); + TreeNodeResult result = DrawTreeNode(id, onDraw, options, isLeaf, drawList, true); + uint active = NativeImGui.Raw.GetCurrentContext()->ActiveId; + if (active != 0 && (active != activeBefore || active == NativeImGui.Raw.GetCurrentContext()->LastItemData.ID)) + { + state.activeContentOwner = id; + state.activeContentId = active; + } + return result; } finally { @@ -64,13 +170,14 @@ private TreeNodeResult DrawTreeNode( Action onDraw, in TreeNodeOptions options, bool isLeaf, - ImDrawList* drawList + ImDrawList* drawList, + bool drawContent ) { bool open = !isLeaf && m_openStatesById.TryGetValue(id, out bool storedOpen) && storedOpen; if (!isLeaf && m_hasNextTreeNodeOpen) { open = m_nextTreeNodeOpen; - m_openStatesById[id] = open; + SetTreeNodeOpen(id, open); } m_hasNextTreeNodeOpen = false; @@ -82,33 +189,49 @@ private TreeNodeResult DrawTreeNode( flags |= ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen; else flags |= ImGuiTreeNodeFlags.OpenOnArrow; - int depth = Math.Max(0, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); + if (options.depth is not null) + flags |= ImGuiTreeNodeFlags.NoTreePushOnOpen; + int depth = options.depth ?? Math.Max(0, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); PruneTreeNodeStack(depth); bool hasNextSibling = TrackSiblingState(id, depth); - if (!options.hideGuideLines) + if (drawContent && !options.hideGuideLines) DrawTreeGuideLines(nodeCursor, hasNextSibling, !isLeaf); - PushTransparentTreeNodeHeaderColors(); + if (drawContent) + PushTransparentTreeNodeHeaderColors(); bool isOpen; try { + if (options.requestFocus && NativeImGui.IsWindowFocused((int)Inno.Native.ImGui.ImGuiFocusedFlags.ChildWindows)) + NativeImGui.SetKeyboardFocusHere(); isOpen = NativeImGui.TreeNodeEx($"##{id}", (int)(flags)); + GetTreeWindowState().navigationItemIds.Add(NativeImGui.Raw.GetCurrentContext()->LastItemData.ID); } finally { - NativeImGui.PopStyleColor(3); + if (drawContent) + NativeImGui.PopStyleColor(3); } float nativeRowMaxY = NativeImGui.GetItemRectMax().Y; + if (!drawContent) + { + ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); + if (isOpen && !isLeaf) + m_treeNodeStack.Add(new TreeWidgetNodeState { id = id, cursor = nodeCursor, rowMaxY = nativeRowMaxY, hasNextSibling = hasNextSibling }); + return new TreeNodeResult(isOpen && !isLeaf, false, false, false, + new Vector2(window->Pos.X, nodeCursor.Y), new Vector2(window->WorkRect.Max.X, nativeRowMaxY), + new Vector2(nodeCursor.X + NativeImGui.GetTreeNodeToLabelSpacing(), nodeCursor.Y), false); + } try { if (!isLeaf && NativeImGui.IsItemToggledOpen()) - m_openStatesById[id] = isOpen; + SetTreeNodeOpen(id, isOpen); TreeWidgetHighlightRect contentRect = DrawTreeNodeContentContainer(id, nodeCursor, nativeRowMaxY, onDraw, options.drawViewportOverlay, out Vector2 interactionMin); bool hovered = NativeImGui.IsItemHovered((int)(ImGuiHoveredFlags.AllowWhenOverlappedByItem)); bool clicked = hovered && NativeImGui.IsItemClicked((int)(ImGuiMouseButton.Left)); bool doubleClicked = hovered && NativeImGui.IsMouseDoubleClicked((int)(ImGuiMouseButton.Left)); if (!isLeaf && doubleClicked) - m_openStatesById[id] = !isOpen; + SetTreeNodeOpen(id, !isOpen); bool showHoverHighlight = hovered && !options.suppressHoverHighlight && NativeImGui.Raw.IsDragDropActive() == 0; if (options.selected || showHoverHighlight) DrawTreeRowBackground(drawList, contentRect, NativeImGui.GetColorU32((int)(options.selected ? ImGuiCol.Header : ImGuiCol.HeaderHovered))); @@ -119,11 +242,11 @@ private TreeNodeResult DrawTreeNode( m_treeNodeStack.Add(new TreeWidgetNodeState { id = id, cursor = nodeCursor, rowMaxY = contentRect.max.Y, hasNextSibling = hasNextSibling }); } - return new TreeNodeResult(isOpen && !isLeaf, clicked, doubleClicked, hovered, contentRect.min, contentRect.max, interactionMin); + return new TreeNodeResult(isOpen && !isLeaf, clicked, doubleClicked, hovered, contentRect.min, contentRect.max, interactionMin, true); } catch { - if (isOpen && !isLeaf) + if (isOpen && !isLeaf && options.depth is null) NativeImGui.TreePop(); throw; } @@ -236,6 +359,12 @@ private void BeginTreeFrameIfNeeded() state.treeNodeStack.Clear(); state.lastNodeIdsByDepth.Clear(); state.lineSegments.Clear(); + state.navigationItemIds.Clear(); + if (state.activeContentId != NativeImGui.Raw.GetCurrentContext()->ActiveId) + { + state.activeContentId = 0; + state.activeContentOwner = null; + } m_lastFrame = frame; } @@ -350,6 +479,21 @@ internal TreeNodeDrawContext(float rowHeight) /// public readonly struct TreeNodeOptions { + /// + /// Gets explicit indentation for a flattened row, or null for a nested native tree. + /// Explicit-depth rows never push a native tree scope; callers must not call TreePop for them. + /// + public int? depth { get; init; } + /// + /// Gets whether this row should receive keyboard focus during this submission when its tree window is focused. + /// + public bool requestFocus { get; init; } + + /// + /// Gets whether an active edit requires content submission even while its row is clipped. + /// + public bool keepContentActive { get; init; } + /// /// Gets whether the row is selected. /// @@ -393,7 +537,8 @@ internal TreeNodeResult( bool isHovered, Vector2 min, Vector2 max, - Vector2 contentMin + Vector2 contentMin, + bool isSubmitted ) { this.isOpen = isOpen; this.isClicked = isClicked; @@ -402,8 +547,14 @@ Vector2 contentMin this.min = min; this.max = max; this.contentMin = contentMin; + this.isSubmitted = isSubmitted; } + /// + /// Gets whether row controls were submitted; ordinary clipped rows are false, while preserved active edits remain true. + /// + public bool isSubmitted { get; } + /// /// Gets whether child content should be rendered. /// @@ -437,6 +588,10 @@ Vector2 contentMin internal sealed class TreeWidgetWindowState { + internal ulong expansionRevision; + internal uint activeContentId; + internal string? activeContentOwner; + internal readonly HashSet navigationItemIds = []; internal readonly List treeNodeStack = []; internal readonly List lastNodeIdsByDepth = []; internal readonly List lineSegments = []; diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Trend.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Trend.cs new file mode 100644 index 000000000..a62cd51f7 --- /dev/null +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Trend.cs @@ -0,0 +1,72 @@ +using System; +using System.Numerics; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.ImGui.ImGuiWidget; + +/// +/// Provides session-owned numeric trend presentation over borrowed sample buffers. +/// +public sealed unsafe partial class ImGuiWidget +{ + /// + /// Draws a bounded line plot from borrowed numeric samples without copying their buffer. + /// + /// + /// The visible plot title and stable item identifier in its current owner. + /// + /// + /// Finite samples borrowed only for this call; an empty span draws an empty plot. + /// + /// + /// The first chronological sample in a circular buffer, or zero for empty input. + /// + /// + /// The label is empty or a sample is not finite. + /// + /// + /// The offset is outside the supplied sample span. + /// + /// + /// No matching owner-thread drawing session is active. + /// + public void Trend( + string label, + ReadOnlySpan values, + int offset = 0 + ) { + m_session.EnsureDrawing(); + ArgumentException.ThrowIfNullOrWhiteSpace(label); + if (offset < 0 || (values.Length == 0 ? offset != 0 : offset >= values.Length)) + throw new ArgumentOutOfRangeException(nameof(offset)); + float maximum = 0f; + float minimum = 0f; + foreach (float value in values) + { + if (!float.IsFinite(value)) + throw new ArgumentException("Trend samples must be finite.", nameof(values)); + maximum = MathF.Max(maximum, value); + minimum = MathF.Min(minimum, value); + } + NativeImGui.TextUnformatted(label); + Vector2 origin = NativeImGui.GetCursorScreenPos(); + Vector2 size = new(MathF.Max(1f, NativeImGui.GetContentRegionAvail().X), style.trendHeight); + _ = NativeImGui.InvisibleButton(label, size); + EditorDrawList draw = ImGui.GetDrawList(); + draw.FilledRectangle(origin, origin + size, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.collectionRow)); + double range = maximum > minimum ? (double)maximum - minimum : 1d; + uint color = NativeImGui.GetColorU32((int)ImGuiCol.PlotLines); + Vector2 previous = default; + for (int index = 0; index < values.Length; index++) + { + float value = values[(offset + index) % values.Length]; + float x = values.Length <= 1 ? 0f : (float)index / (values.Length - 1); + float normalized = (float)(((double)value - minimum) / range); + Vector2 point = origin + new Vector2(x * size.X, (1f - normalized) * size.Y); + if (index > 0) + draw.Line(previous, point, color); + previous = point; + } + } +} diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs index 1d589689e..92f399fc5 100644 --- a/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Zoom/EditorZoomModule.cs @@ -25,8 +25,8 @@ EditorUiSession ui private float m_actualSize = 1f; private int m_zoomStep; internal float zoom => ui.widgets.style.zoom; - internal bool canZoomIn => ResolveZoom(m_zoomStep + 1) > zoom + 0.0001f; - internal bool canZoomOut => ResolveZoom(m_zoomStep - 1) < zoom - 0.0001f; + internal bool canZoomIn => ResolveZoom(m_zoomStep + 1) > ResolveZoom(m_zoomStep) + 0.0001f; + internal bool canZoomOut => ResolveZoom(m_zoomStep - 1) < ResolveZoom(m_zoomStep) - 0.0001f; internal bool isActualSize => m_zoomStep == 0; internal bool ZoomIn() => SetStep(m_zoomStep + 1); @@ -94,7 +94,7 @@ private float ResolveZoom(int step) private bool SetStep(int step) { float resolved = ResolveZoom(step); - if (MathF.Abs(resolved - zoom) < 0.0001f) + if (MathF.Abs(resolved - ResolveZoom(m_zoomStep)) < 0.0001f) return false; m_zoomStep = step; return ui.widgets.style.SetZoom(resolved); diff --git a/src/content/scene/Inno.Scene/Core/GameScene.cs b/src/content/scene/Inno.Scene/Core/GameScene.cs index d22cd05a0..3a3fc8cf1 100644 --- a/src/content/scene/Inno.Scene/Core/GameScene.cs +++ b/src/content/scene/Inno.Scene/Core/GameScene.cs @@ -69,6 +69,15 @@ public string name /// public bool isLoaded => m_isLoaded; + /// + /// Gets the owner-local change counter for object membership, parentage and sibling ordering. + /// + /// + /// Use this counter only to invalidate derived views of this live scene. It is not a persistent + /// identity, data format or guarantee that object properties remain unchanged. + /// + public ulong hierarchyRevision => m_hierarchy.revision; + internal SceneTypeCatalog typeCatalog => m_types; internal long structureRevision => m_store.structureRevision; diff --git a/src/content/scene/Inno.Scene/Service/HierarchyService.cs b/src/content/scene/Inno.Scene/Service/HierarchyService.cs index 60f929fad..0308770d5 100644 --- a/src/content/scene/Inno.Scene/Service/HierarchyService.cs +++ b/src/content/scene/Inno.Scene/Service/HierarchyService.cs @@ -13,26 +13,33 @@ internal sealed class HierarchyService { private readonly GameScene m_scene; private readonly List m_roots = []; + private readonly Dictionary m_rootIndices = new(ReferenceEqualityComparer.Instance); internal HierarchyService(GameScene scene) { m_scene = scene; } + internal ulong revision { get; private set; } + internal void Register(Transform transform) { - if (!m_roots.Contains(transform)) + if (m_rootIndices.TryAdd(transform, m_roots.Count)) + { m_roots.Add(transform); + revision++; + } } internal void Unregister(Transform transform) { Transform? parent = transform.parent; if (parent is null) - m_roots.Remove(transform); + RemoveRoot(transform); else parent.RemoveChildDirect(transform); transform.SetParentDirect(null); + revision++; } internal void SetParent( @@ -66,13 +73,13 @@ bool worldPositionStays Transform? previousParent = transform.parent; if (previousParent is null) - m_roots.Remove(transform); + RemoveRoot(transform); else previousParent.RemoveChildDirect(transform); transform.SetParentDirect(parent); if (parent is null) - m_roots.Add(transform); + Register(transform); else parent.AddChildDirect(transform); @@ -89,13 +96,14 @@ bool worldPositionStays } m_scene.RecomputeActiveSubtree(transform.gameObject); + revision++; } internal int GetSiblingIndex(Transform transform) { if (transform.parent is Transform parent) return parent.IndexOfChild(transform); - return m_roots.IndexOf(transform); + return m_rootIndices.GetValueOrDefault(transform, -1); } internal void SetSiblingIndex( @@ -109,11 +117,14 @@ int siblingIndex throw new InvalidOperationException("Transform is missing from its parent child collection."); int target = Math.Clamp(siblingIndex, 0, parent.children.Count - 1); if (current != target) + { parent.MoveChild(current, target); + revision++; + } return; } - int rootIndex = m_roots.IndexOf(transform); + int rootIndex = m_rootIndices.GetValueOrDefault(transform, -1); if (rootIndex < 0) throw new InvalidOperationException("Transform is missing from the scene hierarchy index."); int rootTarget = Math.Clamp(siblingIndex, 0, m_roots.Count - 1); @@ -121,9 +132,30 @@ int siblingIndex return; m_roots.RemoveAt(rootIndex); m_roots.Insert(rootTarget, transform); + RefreshRootIndices(Math.Min(rootIndex, rootTarget), Math.Max(rootIndex, rootTarget) + 1); + revision++; + } + + internal void Clear() + { + m_roots.Clear(); + m_rootIndices.Clear(); + revision++; } - internal IReadOnlyList GetRoots() => m_roots.ToArray(); + private void RemoveRoot(Transform transform) + { + if (!m_rootIndices.Remove(transform, out int index)) + return; + m_roots.RemoveAt(index); + RefreshRootIndices(index, m_roots.Count); + } - internal void Clear() => m_roots.Clear(); + private void RefreshRootIndices( + int start, + int end + ) { + for (int index = start; index < end; index++) + m_rootIndices[m_roots[index]] = index; + } } diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/FrameworkReferenceResolver.cs b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/FrameworkReferenceResolver.cs index adc3ba6f1..8e51a2565 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/FrameworkReferenceResolver.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/FrameworkReferenceResolver.cs @@ -14,9 +14,11 @@ namespace Inno.Scripting.Compiler; internal static class FrameworkReferenceResolver { - internal static ScriptCompilationEnvironment Resolve(CancellationToken cancellationToken = default) - { - string referenceDirectory = FindReferenceDirectory(); + internal static ScriptCompilationEnvironment Resolve( + IScriptCompilationEnvironmentProvider provider, + CancellationToken cancellationToken = default + ) { + ScriptCompilationInputs inputs = provider.Capture(cancellationToken); var references = new List(); var names = new HashSet(StringComparer.OrdinalIgnoreCase) { "System.Private.CoreLib" }; using var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); @@ -26,14 +28,14 @@ internal static ScriptCompilationEnvironment Resolve(CancellationToken cancellat ScriptCompilationEnvironment.C_LANGUAGE_VERSION, typeof(CSharpCompilation).Module.ModuleVersionId.ToString("N"), typeof(FrameworkReferenceResolver).Module.ModuleVersionId.ToString("N"), - RuntimeInformation.FrameworkDescription + inputs.sdkIdentity, + string.Join(";", inputs.predefinedSymbols) }) { hash.AppendData(Encoding.UTF8.GetBytes(identity)); hash.AppendData([0]); } - foreach (string path in Directory.EnumerateFiles(referenceDirectory, "*.dll") - .OrderBy(static path => Path.GetFileName(path), StringComparer.Ordinal)) + foreach (string path in inputs.frameworkReferences) { cancellationToken.ThrowIfCancellationRequested(); hash.AppendData(Encoding.UTF8.GetBytes(Path.GetFileName(path))); @@ -45,61 +47,7 @@ internal static ScriptCompilationEnvironment Resolve(CancellationToken cancellat names.Add(Path.GetFileNameWithoutExtension(path)); } return new ScriptCompilationEnvironment( - references.AsReadOnly(), names, Convert.ToHexStringLower(hash.GetHashAndReset())); + references.AsReadOnly(), names, Convert.ToHexStringLower(hash.GetHashAndReset()), inputs); } - private static string FindReferenceDirectory() - { - const string targetFramework = ScriptCompilationEnvironment.C_TARGET_FRAMEWORK; - foreach (string dotnetRoot in GetDotnetRoots()) - { - string packRoot = Path.Combine(dotnetRoot, "packs", "Microsoft.NETCore.App.Ref"); - if (!Directory.Exists(packRoot)) - continue; - string? directory = Directory - .EnumerateDirectories(packRoot) - .Select(path => new - { - path, - version = ParseVersion(Path.GetFileName(path)) - }) - .Where(candidate => candidate.version is not null && - candidate.version.Major == 10 && candidate.version.Minor == 0 && - !Path.GetFileName(candidate.path).Contains('-', StringComparison.Ordinal)) - .OrderByDescending(static candidate => candidate.version) - .Select(candidate => Path.Combine(candidate.path, "ref", targetFramework)) - .FirstOrDefault(Directory.Exists); - if (directory is not null) - return directory; - } - throw new InvalidOperationException( - $"The {targetFramework} reference pack is required to generate script API assemblies. " + - "Install the matching .NET SDK, publish the Editor with its reference pack, or configure DOTNET_ROOT."); - } - - private static IEnumerable GetDotnetRoots() - { - var roots = new List(); - var seen = new HashSet(StringComparer.OrdinalIgnoreCase); - Add(AppContext.BaseDirectory); - Add(Environment.GetEnvironmentVariable("DOTNET_ROOT")); - Add(Environment.GetEnvironmentVariable("DOTNET_ROOT_X64")); - Add(Environment.GetEnvironmentVariable("DOTNET_ROOT_ARM64")); - string runtimeDirectory = Path.GetFullPath(RuntimeEnvironment.GetRuntimeDirectory()); - Add(Path.GetFullPath(Path.Combine(runtimeDirectory, "..", "..", ".."))); - return roots; - - void Add(string? path) - { - if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path)) - { - string root = Path.TrimEndingDirectorySeparator(Path.GetFullPath(path)); - if (seen.Add(root)) - roots.Add(root); - } - } - } - - private static Version? ParseVersion(string value) - => Version.TryParse(value, out Version? result) ? result : null; } diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationEnvironment.cs b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationEnvironment.cs index ae9decba8..e599f4306 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationEnvironment.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilationEnvironment.cs @@ -14,14 +14,17 @@ internal sealed class ScriptCompilationEnvironment internal ScriptCompilationEnvironment( IReadOnlyList frameworkReferences, IReadOnlySet frameworkAssemblyNames, - string fingerprint + string fingerprint, + ScriptCompilationInputs inputs ) { this.frameworkReferences = frameworkReferences; this.frameworkAssemblyNames = frameworkAssemblyNames; this.fingerprint = fingerprint; + this.inputs = inputs; } internal IReadOnlyList frameworkReferences { get; } internal IReadOnlySet frameworkAssemblyNames { get; } internal string fingerprint { get; } + internal ScriptCompilationInputs inputs { get; } } diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilerEngine.cs b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilerEngine.cs index 34d0b8ba6..d290c3fa5 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilerEngine.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptCompilerEngine.cs @@ -83,7 +83,7 @@ CancellationToken cancellationToken : null; progress.Complete("Script API profile built."); progress.Begin("Resolving script references..."); - ScriptCompilationEnvironment environment = FrameworkReferenceResolver.Resolve(cancellationToken); + ScriptCompilationEnvironment environment = FrameworkReferenceResolver.Resolve(options.compilationEnvironment, cancellationToken); ScriptApiReferenceSet runtimeApiReferences = ScriptApiReferenceBuilder.Build( options, runtimeApi, environment, cancellationToken: cancellationToken); ScriptApiReferenceSet? editorApiReferences = editorApi is null @@ -110,7 +110,7 @@ CancellationToken cancellationToken ? editorApiReferences ?? throw new InvalidOperationException( "A runtime deployment cannot contain an editor script assembly.") : runtimeApiReferences; - IReadOnlyList defines = GetCompilationDefines(assembly, includeEditor); + IReadOnlyList defines = GetCompilationDefines(assembly, includeEditor, environment.inputs.predefinedSymbols); assemblyKeys.Add( assembly.name, ComputeAssemblyBuildKey( @@ -199,7 +199,7 @@ CancellationToken cancellationToken string.IsNullOrEmpty(assembly.ownerPluginId) ? AssetSourceId.project.value : assembly.ownerPluginId, - GetCompilationDefines(assembly, includeEditor), + GetCompilationDefines(assembly, includeEditor, environment.inputs.predefinedSymbols), assembly.nullable, assembly.allowUnsafe, progress, @@ -581,16 +581,17 @@ IReadOnlyList defines private static IReadOnlyList GetCompilationDefines( ScriptAssemblyInput assembly, - bool includeEditor + bool includeEditor, + IReadOnlyList predefinedSymbols ) { ArgumentNullException.ThrowIfNull(assembly); return includeEditor - ? assembly.defines + ? assembly.defines.Concat(predefinedSymbols) .Append(C_EDITOR_COMPILATION_SYMBOL) .Distinct(StringComparer.Ordinal) .OrderBy(static value => value, StringComparer.Ordinal) .ToArray() - : assembly.defines; + : assembly.defines.Concat(predefinedSymbols).Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal).ToArray(); } private static string ComputeGenerationBuildKey( diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptProjectGenerator.cs b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptProjectGenerator.cs index a3d1a5826..d81f4b087 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptProjectGenerator.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/Compilation/ScriptProjectGenerator.cs @@ -33,7 +33,7 @@ PluginEnvironment plugins .ToArray(); ScriptApiProfile runtimeApi = ScriptApiCatalog.Build(includeEditor: false); ScriptApiProfile editorApi = ScriptApiCatalog.Build(includeEditor: true); - ScriptCompilationEnvironment environment = FrameworkReferenceResolver.Resolve(); + ScriptCompilationEnvironment environment = FrameworkReferenceResolver.Resolve(options.compilationEnvironment); ScriptApiReferenceSet runtimeApiReferences = ScriptApiReferenceBuilder.Build(options, runtimeApi, environment); ScriptApiReferenceSet editorApiReferences = ScriptApiReferenceBuilder.Build( options, @@ -162,6 +162,7 @@ private static XDocument CreateProject( bool allowUnsafe, string sourcePathMap ) { + string frozenDefines = string.Join(";", defines.Concat(apiReferences.environment.inputs.predefinedSymbols).Distinct(StringComparer.Ordinal)); var earlyPropertyGroup = new XElement("PropertyGroup", new XElement("ImportDirectoryBuildProps", "false"), new XElement("ImportDirectoryBuildTargets", "false"), @@ -178,7 +179,11 @@ string sourcePathMap new XElement("ImplicitUsings", "disable"), new XElement("Nullable", nullable ? "enable" : "disable"), new XElement("AllowUnsafeBlocks", allowUnsafe ? "true" : "false"), - new XElement("DefineConstants", string.Join(";", defines)), + new XElement("DisableImplicitFrameworkReferences", "true"), + new XElement("DisableImplicitFrameworkDefines", "true"), + new XElement("DisableImplicitConfigurationDefines", "true"), + new XElement("NoStdLib", "true"), + new XElement("DefineConstants", frozenDefines), new XElement("PathMap", sourcePathMap), new XElement("LangVersion", ScriptCompilationEnvironment.C_LANGUAGE_VERSION)); var compileGroup = new XElement( @@ -191,7 +196,7 @@ string sourcePathMap new XElement("None", new XAttribute("Include", $"Assets/**/*{extension}")))); var referenceGroup = new XElement("ItemGroup"); - foreach (string path in apiReferences.ideReferencePaths) + foreach (string path in apiReferences.ideReferencePaths.Concat(apiReferences.environment.inputs.frameworkReferences)) AddReference(referenceGroup, path); foreach (string path in plugins .Distinct(StringComparer.OrdinalIgnoreCase) @@ -219,6 +224,28 @@ string sourcePathMap contentGroup, referenceGroup, codeAnalysisGroup); + project.Add(new XElement("Target", new XAttribute("Name", "ValidateInnoScriptSdk"), + new XAttribute("BeforeTargets", "ResolveReferences"), + new XElement("Error", new XAttribute("Condition", "'$(NETCoreSdkVersion)' != '" + apiReferences.environment.inputs.sdkIdentity + "'"), + new XAttribute("Text", "The IDE selected a different SDK from the active script compilation. Regenerate project files with the intended SDK selection.")))); + XElement validation = project.Elements("Target").Single(); + foreach ((string property, string expected) in new[] + { + ("TargetFramework", ScriptCompilationEnvironment.C_TARGET_FRAMEWORK), + ("LangVersion", ScriptCompilationEnvironment.C_LANGUAGE_VERSION), + ("ImplicitUsings", "disable"), ("DisableImplicitFrameworkReferences", "true"), + ("DisableImplicitFrameworkDefines", "true"), ("NoStdLib", "true"), + ("DisableImplicitConfigurationDefines", "true"), + ("Nullable", nullable ? "enable" : "disable"), ("AllowUnsafeBlocks", allowUnsafe ? "true" : "false"), + ("DefineConstants", frozenDefines) + }) + { + string condition = property == "DefineConstants" + ? "$([System.String]::CompareOrdinal('$(DefineConstants)', '" + expected + "')) != 0" + : "'$(" + property + ")' != '" + expected + "'"; + validation.Add(new XElement("Error", new XAttribute("Condition", condition), + new XAttribute("Text", "The IDE compiler input '" + property + "' differs from the active script contract. Actual: '$(" + property + ")'. Expected: '" + expected + "'. Regenerate project files instead of overriding derived compiler inputs."))); + } project.Add(folderGroup); if (projectReferences.Count > 0) { diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs b/src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs new file mode 100644 index 000000000..3a7e7b5dd --- /dev/null +++ b/src/runtime/scripting/Inno.Scripting.Compiler/IScriptCompilationEnvironmentProvider.cs @@ -0,0 +1,20 @@ +using System.Threading; + +namespace Inno.Scripting.Compiler; + +/// +/// Supplies the host-selected framework contract for one compilation or IDE projection operation. +/// +public interface IScriptCompilationEnvironmentProvider +{ + /// + /// Captures current SDK inputs without choosing a framework inside the scripting compiler. + /// + /// + /// Cancels input preparation before any script artifact is published. + /// + /// + /// Complete immutable inputs; missing or inconsistent SDK inputs must fail explicitly. + /// + ScriptCompilationInputs Capture(CancellationToken cancellationToken = default); +} diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs new file mode 100644 index 000000000..d3cbdc0ed --- /dev/null +++ b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilationInputs.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Inno.Scripting.Compiler; + +/// +/// Describes framework references and predefined symbols selected by the authoring host's SDK. +/// +public sealed class ScriptCompilationInputs +{ + /// + /// Freezes a complete C# 14 and .NET 10 reference contract without retaining SDK objects. + /// + /// + /// The exact SDK identity that evaluated this contract. + /// + /// + /// Absolute framework assembly paths selected by SDK reference resolution. + /// + /// + /// Framework and configuration symbols produced by SDK target execution. + /// + /// + /// The identity, reference set or a reference path is invalid. + /// + /// + /// A collection is null. + /// + public ScriptCompilationInputs( + string sdkIdentity, + IEnumerable frameworkReferences, + IEnumerable predefinedSymbols + ) { + ArgumentException.ThrowIfNullOrWhiteSpace(sdkIdentity); + ArgumentNullException.ThrowIfNull(frameworkReferences); + ArgumentNullException.ThrowIfNull(predefinedSymbols); + string[] references = frameworkReferences.Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal).ToArray(); + if (references.Length == 0 || references.Any(static path => !Path.IsPathFullyQualified(path))) + throw new ArgumentException("SDK framework references must be a nonempty set of absolute paths.", nameof(frameworkReferences)); + this.sdkIdentity = sdkIdentity; + this.frameworkReferences = Array.AsReadOnly(references); + this.predefinedSymbols = Array.AsReadOnly(predefinedSymbols.Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal).ToArray()); + } + + /// + /// Gets the exact SDK identity used for IDE consistency checks and artifact identity. + /// + public string sdkIdentity { get; } + + /// + /// Gets the SDK-selected framework paths, whose content is frozen before Roslyn compilation. + /// + public IReadOnlyList frameworkReferences { get; } + + /// + /// Gets SDK-produced symbols shared by runtime compilation and IDE projects. + /// + public IReadOnlyList predefinedSymbols { get; } +} diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs index b9e2fb043..403b31616 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompiler.cs @@ -45,10 +45,12 @@ PluginEnvironment plugins ArgumentNullException.ThrowIfNull(options); ArgumentNullException.ThrowIfNull(assets); ArgumentNullException.ThrowIfNull(plugins); + ArgumentNullException.ThrowIfNull(options.compilationEnvironment); ArgumentException.ThrowIfNullOrWhiteSpace(options.projectRootDirectory); m_options = new ScriptCompilerOptions { - projectRootDirectory = System.IO.Path.GetFullPath(options.projectRootDirectory) + projectRootDirectory = System.IO.Path.GetFullPath(options.projectRootDirectory), + compilationEnvironment = options.compilationEnvironment }; m_assets = assets; m_plugins = plugins; diff --git a/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs index 3b6af6e92..38069b56b 100644 --- a/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs +++ b/src/runtime/scripting/Inno.Scripting.Compiler/ScriptCompilerOptions.cs @@ -12,6 +12,11 @@ public sealed class ScriptCompilerOptions /// public required string projectRootDirectory { get; init; } + /// + /// Gets the host-owned source of SDK framework inputs for each operation. + /// + public required IScriptCompilationEnvironmentProvider compilationEnvironment { get; init; } + internal string ideDirectory => Path.Combine(projectRootDirectory, "Library", "IDE"); internal string outputDirectory => Path.Combine(projectRootDirectory, "Library", "Artifacts", "ScriptAssemblies"); diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 new file mode 100644 index 000000000..a2c044a3d --- /dev/null +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -0,0 +1,139 @@ +param( + [Alias('Profile')][ValidateSet('Contracts', 'Workflows', 'Visual', 'Lifecycle', 'Performance', 'Products', 'All')] + [string]$AcceptanceProfile = 'Contracts', + [ValidateSet('Debug', 'Release')][string]$Configuration = 'Debug', + [string]$Dotnet = 'dotnet', + [Parameter(Mandatory = $true)][string]$ResultsDirectory, + [Parameter(Mandatory = $true)][string]$ArtifactsPath, + [string]$ProductProject, + [string[]]$GraphicsApis = @(), + [string]$BackendPattern = 'BGFX', + [switch]$NoBuild +) + +$ErrorActionPreference = 'Stop' +$repository = Split-Path $PSScriptRoot -Parent +$Dotnet = (Get-Command $Dotnet -ErrorAction Stop).Source +$results = Join-Path ([IO.Path]::GetFullPath($ResultsDirectory, $repository)) ([Guid]::NewGuid().ToString('N')) +$artifacts = [IO.Path]::GetFullPath($ArtifactsPath, $repository) +New-Item -ItemType Directory -Force -Path $results | Out-Null +$records = [Collections.Generic.List[object]]::new() +$revision = (& git -C $repository rev-parse HEAD).Trim() +if ($LASTEXITCODE -ne 0) { throw 'The candidate revision could not be recorded.' } +$dirty = @(& git -C $repository status --porcelain=v1) +if ($LASTEXITCODE -ne 0) { throw 'The candidate working state could not be recorded.' } +$sdk = @(& $Dotnet --info) +if ($LASTEXITCODE -ne 0) { throw 'The selected SDK could not be inspected.' } +$sdk | Set-Content -LiteralPath (Join-Path $results 'sdk.txt') -Encoding utf8 + +function Save-Result( + [string]$Stage, + [string]$Status, + [string]$Diagnostic +) { + $records.Add([pscustomobject]@{ stage = $Stage; status = $Status; diagnostic = $Diagnostic }) + [pscustomobject]@{ + revision = $revision + workingChanges = $dirty + profile = $AcceptanceProfile + configuration = $Configuration + buildRequested = -not $NoBuild + scope = 'Selected automated suites; issue closure additionally requires the complete tracked acceptance matrix.' + results = @($records.ToArray()) + } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $results 'summary.json') -Encoding utf8 +} + +$entry = Join-Path $repository 'entry/InnoEngine.Editor/InnoEngine.Editor.csproj' +$evaluated = @(& $Dotnet msbuild $entry -nologo "-p:Configuration=$Configuration" "-p:ArtifactsPath=$artifacts" ` + '-getProperty:InnoNativeTarget,InnoProductBuildProperties,TargetPath') +if ($LASTEXITCODE -ne 0) { throw 'The official Editor entry could not select its product owner.' } +$product = ($evaluated -join [Environment]::NewLine | ConvertFrom-Json).Properties +if (-not $product.InnoNativeTarget -or -not (Test-Path -LiteralPath $product.InnoProductBuildProperties)) { + throw 'The selected development host does not provide an Editor product.' +} +$buildArguments = @("-p:ArtifactsPath=$artifacts", "-p:InnoNativeTarget=$($product.InnoNativeTarget)", + "-p:InnoProductBuildProperties=$($product.InnoProductBuildProperties)", + "-p:InnoTestNativeTarget=$($product.InnoNativeTarget)", '-p:InnoTestNativeProduct=editor') +$profiles = if ($AcceptanceProfile -eq 'All') { @('Contracts', 'Workflows', 'Visual', 'Lifecycle', 'Performance', 'Products') } else { @($AcceptanceProfile) } + +foreach ($selected in $profiles) { + $stageRoot = Join-Path $results $selected + New-Item -ItemType Directory -Force -Path $stageRoot | Out-Null + try { + if ($selected -eq 'Products') { + if (-not $ProductProject -or -not (Test-Path -LiteralPath (Join-Path $ProductProject 'Assets'))) { + Save-Result $selected 'Blocked' 'Supply an independent FlappyBird acceptance copy using -ProductProject.' + continue + } + if ($GraphicsApis.Count -eq 0) { + Save-Result $selected 'Blocked' 'Supply the explicit graphics API matrix for the selected product using -GraphicsApis.' + continue + } + if (-not $NoBuild) { + & $Dotnet build $entry -c $Configuration @buildArguments *> (Join-Path $stageRoot 'build.log') + if ($LASTEXITCODE -ne 0) { throw 'The official Editor build failed.' } + } + if (-not (Test-Path -LiteralPath $product.TargetPath)) { throw 'The SDK-selected Editor output is unavailable.' } + foreach ($api in $GraphicsApis) { + & (Join-Path $PSScriptRoot 'Invoke-EditorSmoke.ps1') -Dotnet $Dotnet -EditorAssembly $product.TargetPath ` + -ProjectDirectory $ProductProject -GraphicsApi $api -BackendPattern $BackendPattern -Frames 300 ` + *> (Join-Path $stageRoot "$api.log") + } + Save-Result $selected 'Passed' 'Editor rendering smoke completed; interactive journeys remain separately tracked.' + continue + } + $names = switch ($selected) { + 'Contracts' { @('Inno.Editor.Interactions.Tests', 'Inno.Editor.Graph.Tests', 'Inno.Editor.Inspection.Tests', 'Inno.Scripting.Compiler.Tests', 'Inno.Build.Tests', 'Inno.Tooling.Architecture.Tests') } + 'Workflows' { @('Inno.Editor.PlayMode.Tests', 'Inno.Editor.Scripting.Tests', 'Inno.Editor.Hosting.Tests') } + 'Visual' { @('Inno.Editor.ImGui.Tests', 'Inno.Editor.PlayMode.Tests', 'Inno.Editor.Scripting.Tests') } + 'Lifecycle' { @('Inno.Editor.Graph.Tests', 'Inno.Editor.PlayMode.Tests', 'Inno.Editor.Scripting.Tests') } + 'Performance' { @('Inno.Editor.Interactions.Tests', 'Inno.Editor.PlayMode.Tests', 'Inno.Editor.Scripting.Tests') } + } + $filter = switch ($selected) { + 'Visual' { 'FullyQualifiedName~WidgetLayoutTests|FullyQualifiedName~ViewportMetricsTests|Acceptance=Visual' } + 'Lifecycle' { 'Acceptance=Lifecycle' } + 'Performance' { 'Acceptance=Performance|Category=Performance' } + default { '(Acceptance!=Performance)&(Category!=Performance)' } + } + $projects = foreach ($name in $names) { + $projectFiles = @(Get-ChildItem -LiteralPath (Join-Path $repository 'tests') -Filter "$name.csproj" -Recurse) + if ($projectFiles.Count -ne 1) { throw "The suite '$name' does not resolve to one test project." } + [IO.Path]::GetRelativePath($repository, $projectFiles[0].FullName) + } + $selection = Join-Path $stageRoot 'tests.slnf' + @{ solution = @{ path = (Join-Path $repository 'InnoEngine.sln'); projects = @($projects) } } | + ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $selection -Encoding utf8 + if (-not $NoBuild) { + & $Dotnet build $selection -c $Configuration @buildArguments *> (Join-Path $stageRoot 'build.log') + if ($LASTEXITCODE -ne 0) { throw 'The selected test graph did not build.' } + } + & (Join-Path $PSScriptRoot 'Invoke-IsolatedTests.ps1') -Dotnet $Dotnet -Solution $selection ` + -Configuration $Configuration -ResultsDirectory $stageRoot -ArtifactsPath $artifacts ` + -Target $product.InnoNativeTarget -ProductProperties $product.InnoProductBuildProperties -Filter $filter ` + *> (Join-Path $stageRoot 'execution.log') + $executed = 0 + $observedSuites = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) + foreach ($trx in Get-ChildItem -LiteralPath $stageRoot -Filter '*.trx') { + $report = [xml](Get-Content -LiteralPath $trx.FullName -Raw) + $executed += [int]$report.TestRun.ResultSummary.Counters.executed + if ([int]$report.TestRun.ResultSummary.Counters.executed -gt 0) { + foreach ($method in $report.TestRun.TestDefinitions.UnitTest.TestMethod) { + [void]$observedSuites.Add([IO.Path]::GetFileNameWithoutExtension($method.codeBase)) + } + } + if ([int]$report.TestRun.ResultSummary.Counters.failed -ne 0 -or $report.TestRun.ResultSummary.outcome -eq 'Aborted') { + throw "The test result '$($trx.Name)' did not pass." + } + } + if ($executed -le 0) { throw 'The selected suite executed no tests.' } + foreach ($name in $names) { + if (-not $observedSuites.Contains($name)) { throw "The selected project '$name' executed no tests." } + } + Save-Result $selected 'Passed' "$executed test cases executed; consult TRX and isolation logs." + } catch { + Save-Result $selected 'Failed' $_.Exception.Message + } +} +if (@($records | Where-Object status -ne 'Passed').Count -ne 0) { + throw "Acceptance has failed or blocked stages. See $results/summary.json." +} diff --git a/tests/Invoke-IsolatedTests.ps1 b/tests/Invoke-IsolatedTests.ps1 index bbedacead..5d66a8efb 100644 --- a/tests/Invoke-IsolatedTests.ps1 +++ b/tests/Invoke-IsolatedTests.ps1 @@ -22,6 +22,12 @@ $repositoryRoot = Split-Path $PSScriptRoot -Parent $solutionPath = [System.IO.Path]::GetFullPath($Solution, $repositoryRoot) $resultsRoot = [System.IO.Path]::GetFullPath($ResultsDirectory, $repositoryRoot) New-Item -ItemType Directory -Force -Path $resultsRoot | Out-Null +$processTemp = Join-Path $resultsRoot 'process-temp' +New-Item -ItemType Directory -Force -Path $processTemp | Out-Null +$tempEnvironment = @('--environment', "TMP=$processTemp", '--environment', "TEMP=$processTemp", + '--environment', "TMPDIR=$processTemp") +$sdkEnvironment += $tempEnvironment +$assemblyEnvironment += $tempEnvironment $checks = [System.Collections.Generic.List[object]]::new() function Add-CheckResult( diff --git a/tests/Invoke-WikiChecks.ps1 b/tests/Invoke-WikiChecks.ps1 index 094f3ed9c..e6cd2758b 100644 --- a/tests/Invoke-WikiChecks.ps1 +++ b/tests/Invoke-WikiChecks.ps1 @@ -6,7 +6,8 @@ param( [string]$Dotnet = 'dotnet', [ValidateSet('Debug', 'Release')] [string]$Configuration = 'Debug', - [string]$ResultsDirectory = 'artifacts/acceptance/wiki-gate' + [string]$ResultsDirectory = 'artifacts/acceptance/wiki-gate', + [string]$ArtifactsPath ) $ErrorActionPreference = 'Stop' @@ -16,7 +17,14 @@ $project = [IO.Path]::GetFullPath($ProjectDirectory, $repository) $results = [IO.Path]::GetFullPath($ResultsDirectory, $repository) New-Item -ItemType Directory -Force -Path $results | Out-Null $entryProject = Join-Path $repository 'entry/InnoEngine.Build/InnoEngine.Build.csproj' -$entry = @(& $Dotnet msbuild $entryProject -nologo -m:1 -nr:false "-p:Configuration=$Configuration" -getProperty:TargetPath) +$outputArguments = @() +$verificationArguments = @() +if ($ArtifactsPath) { + $outputOwner = [IO.Path]::GetFullPath($ArtifactsPath, $repository) + $outputArguments = @("-p:ArtifactsPath=$outputOwner") + $verificationArguments = @('--artifacts-path', $outputOwner) +} +$entry = @(& $Dotnet msbuild $entryProject -nologo -m:1 -nr:false "-p:Configuration=$Configuration" -getProperty:TargetPath @outputArguments) if ($LASTEXITCODE -ne 0 -or $entry.Count -ne 1 -or -not (Test-Path -LiteralPath $entry[0])) { throw 'Build the selected Build entry and product references before running the Wiki gate.' } @@ -38,7 +46,7 @@ $runtimeReference = Read-Reference 'Inno.ScriptApi.Runtime' $editorReference = Read-Reference 'Inno.ScriptApi.Editor' @{ runtime = $runtimeReference; editor = $editorReference } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $results 'script-references.json') -Encoding utf8 -& $Dotnet $entry[0] verify $repository --dotnet $Dotnet --configuration $Configuration --wiki --wiki-target $Target ` +& $Dotnet $entry[0] verify $repository --dotnet $Dotnet --configuration $Configuration --wiki --wiki-target $Target @verificationArguments ` --wiki-runtime-reference $runtimeReference --wiki-editor-reference $editorReference ` --wiki-api (Join-Path $results 'api.json') --wiki-examples (Join-Path $results 'examples.json') 2>&1 | Tee-Object -FilePath (Join-Path $results 'wiki.log') diff --git a/tests/audio/Inno.Audio.Tests/AudioScriptingApiTests.cs b/tests/audio/Inno.Audio.Tests/AudioScriptingApiTests.cs index ddff1b12c..f4c64dedc 100644 --- a/tests/audio/Inno.Audio.Tests/AudioScriptingApiTests.cs +++ b/tests/audio/Inno.Audio.Tests/AudioScriptingApiTests.cs @@ -72,8 +72,15 @@ public async Task FacadeMixerProviderAndSceneComponentsCompileThroughLogicalName Path.Combine(projectRoot, "Library"), scan, host.modules.generations); + string dotnetHost = Path.Combine( + Path.GetFullPath("../../..", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); var compiler = new ScriptCompiler( - new ScriptCompilerOptions { projectRootDirectory = projectRoot }, + new ScriptCompilerOptions + { + projectRootDirectory = projectRoot, + compilationEnvironment = new Inno.Build.SdkScriptCompilationEnvironment(dotnetHost, projectRoot) + }, assets, plugins); File.WriteAllText(Path.Combine(projectRoot, "Assets", "AudioProbe.cs"), """ diff --git a/tests/audio/Inno.Audio.Tests/Inno.Audio.Tests.csproj b/tests/audio/Inno.Audio.Tests/Inno.Audio.Tests.csproj index 17c203d97..09d6d4a3a 100644 --- a/tests/audio/Inno.Audio.Tests/Inno.Audio.Tests.csproj +++ b/tests/audio/Inno.Audio.Tests/Inno.Audio.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/build/Inno.Build.Tests/BuildPipelineTests.cs b/tests/build/Inno.Build.Tests/BuildPipelineTests.cs index 5b32b046f..0e2430fa9 100644 --- a/tests/build/Inno.Build.Tests/BuildPipelineTests.cs +++ b/tests/build/Inno.Build.Tests/BuildPipelineTests.cs @@ -103,7 +103,8 @@ public BuildPipelineTests() m_compiler = new ScriptCompiler( new ScriptCompilerOptions { - projectRootDirectory = m_projectRoot + projectRootDirectory = m_projectRoot, + compilationEnvironment = new Inno.Build.SdkScriptCompilationEnvironment(Path.Combine(Path.GetFullPath("../../..", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()), OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"), m_projectRoot) }, m_assets, m_plugins); diff --git a/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs index 1342f6217..3fea948f9 100644 --- a/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs +++ b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs @@ -22,6 +22,7 @@ public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild() Directory.CreateDirectory(root); try { + File.Copy(Path.Combine(repository, "global.json"), Path.Combine(root, "global.json")); foreach (string suffix in new[] { "props", "targets" }) File.WriteAllText(Path.Combine(root, "Directory.Build." + suffix), ""); diff --git a/tests/build/Inno.Build.Tests/DevelopmentAppHostSdkTests.cs b/tests/build/Inno.Build.Tests/DevelopmentAppHostSdkTests.cs new file mode 100644 index 000000000..fa2d84c8d --- /dev/null +++ b/tests/build/Inno.Build.Tests/DevelopmentAppHostSdkTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Inno.Build.Composition; +using Inno.Build.Distribution.Standard; +using Inno.Build.Toolchains; +using Inno.Core.IO; +using Xunit; + +namespace Inno.Build.Tests; + +public sealed class DevelopmentAppHostSdkTests +{ + [Fact] + public async Task DirectAppHostUsesItsLoadedRuntimeBeforeAnUnrelatedPath() + { + string executable = OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"; + string host = Path.GetFullPath(Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "..", "..", "..", executable)); + Assert.True(File.Exists(host)); + using ProcessOwnedDirectory owner = await ProcessOwnedDirectory.CreateAsync(Path.Combine(AppContext.BaseDirectory, "SdkHostFixtures")); + string project = Path.Combine(owner.directory, "SdkHostProbe.csproj"); + Type[] references = [typeof(StandardBuildEnvironment), typeof(BuildCompositionContext), typeof(BuildTargetId)]; + var items = new XElement("ItemGroup"); + foreach (Type reference in references) + items.Add(new XElement("Reference", new XAttribute("Include", reference.Assembly.GetName().Name!), + new XElement("HintPath", reference.Assembly.Location))); + new XDocument(new XElement("Project", new XAttribute("Sdk", "Microsoft.NET.Sdk"), + new XElement("PropertyGroup", new XElement("TargetFramework", "net10.0"), new XElement("OutputType", "Exe"), + new XElement("ImplicitUsings", "disable"), new XElement("UseAppHost", "true")), items)).Save(project); + await File.WriteAllTextAsync(Path.Combine(owner.directory, "Program.cs"), """ + using System; + using Inno.Build; + using Inno.Build.Distribution.Standard; + Console.Write(StandardBuildEnvironment.Capture(AppContext.BaseDirectory, new BuildTargetId("sdk-probe")).dotnetHost); + """); + using var buildOutput = new StringWriter(); + using var buildErrors = new StringWriter(); + try + { + await ToolchainEnvironment.RunAsync(host, ["build", project, "-nologo", "-nodeReuse:false"], + owner.directory, CancellationToken.None, DotNetSdkEnvironment.Create(host), buildOutput, buildErrors); + } + catch (InvalidOperationException failure) + { + throw new InvalidOperationException(buildOutput + Environment.NewLine + buildErrors, failure); + } + string output = Path.Combine(owner.directory, "bin", "Debug", "net10.0"); + foreach (string dependency in Directory.EnumerateFiles(AppContext.BaseDirectory, "*.dll")) + File.Copy(dependency, Path.Combine(output, Path.GetFileName(dependency)), overwrite: true); + var environment = new Dictionary + { + ["DOTNET_HOST_PATH"] = null, + ["DOTNET_ROOT"] = Path.GetDirectoryName(host), + ["PATH"] = owner.directory + }; + string appHost = Path.Combine(output, OperatingSystem.IsWindows() ? "SdkHostProbe.exe" : "SdkHostProbe"); + string selected = await ToolchainEnvironment.CaptureOutputAsync(appHost, [], owner.directory, + CancellationToken.None, environment); + Assert.Equal(host, selected.Trim()); + } +} diff --git a/tests/build/Inno.Build.Tests/NuGetLoadedIdentityTests.cs b/tests/build/Inno.Build.Tests/NuGetLoadedIdentityTests.cs new file mode 100644 index 000000000..454b87ebd --- /dev/null +++ b/tests/build/Inno.Build.Tests/NuGetLoadedIdentityTests.cs @@ -0,0 +1,132 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Loader; +using System.Security.Cryptography; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using BGCS.Runtime; +using Inno.Build.Toolchains; +using Inno.Testing.Source; +using Xunit; + +namespace Inno.Build.Tests; + +public sealed class NuGetLoadedIdentityTests +{ + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(true, true)] + public Task SdkOutputScopesMatchTheActuallyLoadedRuntime( + bool isolated, + bool staleDefault + ) => VerifyScope(isolated, staleDefault); + + [Fact] + [Trait("ProcessIsolation", "Required")] + public Task IsolatedHostRejectsARealLoadedPayloadMismatch() => VerifyScope(true, true); + + private static async Task VerifyScope( + bool isolated, + bool staleDefault + ) { + string root = Path.Combine(Path.GetTempPath(), "InnoLoadedPackageIdentity", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(root); + try + { + string repository = TestRepository.GetRoot(typeof(NuGetLoadedIdentityTests).Assembly); + string sdk = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(3)); + string version = (await ToolchainEnvironment.CaptureOutputAsync(sdk, + ["msbuild", Path.Combine(repository, "backends/ImGui/native/Inno.Native.ImGui/Inno.Native.ImGui.csproj"), + "-nologo", "-nr:false", "-getProperty:InnoBgcsPackageVersion"], + repository, cancellation.Token, DotNetSdkEnvironment.Create(sdk))).Trim(); + string project = Path.Combine(root, "Consumer.csproj"); + new XDocument(new XElement("Project", new XAttribute("Sdk", "Microsoft.NET.Sdk"), + new XElement("PropertyGroup", new XElement("TargetFramework", "net10.0"), + new XElement("LangVersion", "14.0"), new XElement("ImplicitUsings", "disable")), + new XElement("ItemGroup", new XElement("PackageReference", new XAttribute("Include", "BGCS.Runtime"), + new XAttribute("Version", "[" + version + "]"))))).Save(project); + string defaultAssets = Path.Combine(root, "obj", "project.assets.json"); + const string stale = "unrelated stale default assets must never be consumed"; + if (staleDefault) + { + Directory.CreateDirectory(Path.GetDirectoryName(defaultAssets)!); + File.WriteAllText(defaultAssets, stale); + } + string[] owner = isolated ? ["-p:ArtifactsPath=" + Path.Combine(root, "owned-output")] : []; + string evaluated = await ToolchainEnvironment.CaptureOutputAsync(sdk, + ["msbuild", project, "-t:Restore", "-nologo", "-nr:false", "-v:quiet", "-getProperty:ProjectAssetsFile", .. owner], + root, cancellation.Token, DotNetSdkEnvironment.Create(sdk)); + string assetsPath = evaluated.Trim(); + Assert.True(File.Exists(assetsPath), evaluated); + if (isolated) + { + Assert.StartsWith(Path.Combine(root, "owned-output"), assetsPath, StringComparison.OrdinalIgnoreCase); + if (staleDefault) + Assert.Equal(stale, File.ReadAllText(defaultAssets)); + else + Assert.False(File.Exists(defaultAssets)); + } + else + Assert.Equal(defaultAssets, assetsPath); + using JsonDocument assets = JsonDocument.Parse(File.ReadAllBytes(assetsPath)); + string library = "BGCS.Runtime/" + version; + JsonElement package = assets.RootElement.GetProperty("libraries").GetProperty(library); + string packageRoot = assets.RootElement.GetProperty("packageFolders").EnumerateObject() + .Select(folder => Path.Combine(folder.Name, package.GetProperty("path").GetString()!)).Single(Directory.Exists); + JsonElement target = assets.RootElement.GetProperty("targets").EnumerateObject().Single().Value.GetProperty(library); + string relative = target.GetProperty("runtime").EnumerateObject().Single(item => item.Name.EndsWith(".dll", StringComparison.Ordinal)).Name; + string runtime = Path.Combine(packageRoot, relative); + Assembly loaded = typeof(Utf8Codec).Assembly; + AssertLoadedContent(runtime, loaded); + + string mismatch = Path.Combine(root, "mismatched", "BGCS.Runtime.dll"); + Directory.CreateDirectory(Path.GetDirectoryName(mismatch)!); + File.Copy(runtime, mismatch); + await using (FileStream altered = new(mismatch, FileMode.Append, FileAccess.Write)) + await altered.WriteAsync(new byte[] { 0x49, 0x4E, 0x4E, 0x4F }, cancellation.Token); + WeakReference retired = LoadMismatchedContent(runtime, mismatch, loaded); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(retired.IsAlive); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static WeakReference LoadMismatchedContent( + string expected, + string mismatch, + Assembly loaded + ) { + var context = new AssemblyLoadContext("package-content-mismatch", isCollectible: true); + try + { + Assembly different = context.LoadFromAssemblyPath(mismatch); + Assert.Equal(loaded.GetName().FullName, different.GetName().FullName); + Assert.Equal(loaded.ManifestModule.ModuleVersionId, different.ManifestModule.ModuleVersionId); + Assert.Throws(() => AssertLoadedContent(expected, different)); + return new WeakReference(context); + } + finally + { + context.Unload(); + } + } + + private static void AssertLoadedContent( + string expected, + Assembly loaded + ) => Assert.Equal(Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(expected))), + Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(loaded.Location)))); +} diff --git a/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs b/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs index 2c6413c26..39948b7ef 100644 --- a/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs +++ b/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs @@ -160,7 +160,15 @@ params System.Reflection.Assembly[] roots AssetSerializationContext.Create(assets)); using var plugins = new PluginEnvironment(assets, settings, host.serialization, pluginsRoot, libraryRoot, scan, host.modules.generations); - var compiler = new ScriptCompiler(new ScriptCompilerOptions { projectRootDirectory = project }, assets, plugins); + string dotnetHost = Path.Combine( + Path.GetFullPath("../../..", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + var compiler = new ScriptCompiler( + new ScriptCompilerOptions + { + projectRootDirectory = project, + compilationEnvironment = new Inno.Build.SdkScriptCompilationEnvironment(dotnetHost, project) + }, assets, plugins); compiler.GenerateProjectFiles(); } diff --git a/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPointerTests.cs b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPointerTests.cs new file mode 100644 index 000000000..2cdeaca75 --- /dev/null +++ b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPointerTests.cs @@ -0,0 +1,121 @@ +using System; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using Inno.Editor.Graph.ImGui; +using Xunit; + +namespace Inno.Editor.Graph.Tests; + +public sealed class GraphCanvasPointerTests +{ + [Fact] + public void ShiftBoxSelectionKeepsTheExistingSelectionAndAddsIntersectingNodes() + { + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + GraphNodeId original = new("workflow.original"); + GraphNodeId added = new("workflow.added"); + _ = layout.SetNodes([ + new(original, new(500f, 300f), new(100f, 80f), 24f, "Original", []), + new(added, new(50f, 50f), new(100f, 80f), 24f, "Added", []) + ]); + state.SelectNodes([original]); + _ = pointer.Step(view, layout, new() { position = new(10f, 10f), isHovered = true, isPressed = true, isDown = true, shift = true }); + _ = pointer.Step(view, layout, new() { position = new(200f, 200f), isHovered = true, isReleased = true, shift = true }); + Assert.Equal(2, state.selectedNodes.Count); + Assert.Contains(original, state.selectedNodes); + Assert.Contains(added, state.selectedNodes); + Assert.False(pointer.isBoxSelecting); + } + + [Fact] + public void ConnectionReleaseBehindAnotherInputOwnerDoesNotConnect() + { + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + var output = new GraphEndpoint(new("source"), new("out")); + var input = new GraphEndpoint(new("target"), new("in")); + _ = layout.SetNodes([ + new(new("source"), new(0f, 0f), new(100f, 100f), 24f, "Source", [new(output, GraphPortDirection.Output, new(100f, 50f), "Output")]), + new(new("target"), new(200f, 0f), new(100f, 100f), 24f, "Target", [new(input, GraphPortDirection.Input, new(0f, 50f), "Input")]) + ]); + _ = pointer.Step(view, layout, new() { position = new(100f, 50f), isHovered = true, isPressed = true, isDown = true }); + GraphCanvasPointerResult result = pointer.Step(view, layout, new() { position = new(200f, 50f), isReleased = true, isHovered = false }); + Assert.Null(result.output); + Assert.Null(result.input); + Assert.Null(state.pendingConnection); + } + + [Fact] + public void DragReturnsOneNeutralEditAndCancelLeavesTheDocumentUntouched() + { + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + var node = new GraphCanvasNode(new("flow.step"), new(10f, 10f), new(200f, 100f), 24f, "Workflow", []); + _ = layout.SetNodes([node]); + Assert.Equal(node.id, pointer.Step(view, layout, new() { position = new(20f, 15f), isHovered = true, isPressed = true, isDown = true }).nodePressed); + _ = pointer.Step(view, layout, new() { position = new(70f, 45f), isDown = true }); + Assert.Equal(new GraphPosition(60f, 40f), pointer.previewPositions[node.id]); + GraphCanvasPointerResult completed = pointer.Step(view, layout, new() { position = new(70f, 45f), isReleased = true }); + Assert.Equal(new GraphPosition(60f, 40f), completed.movedNodes![node.id]); + Assert.False(pointer.isMoving); + Assert.Empty(pointer.previewPositions); + Assert.Equal(new GraphPosition(10f, 10f), node.position); + _ = pointer.Step(view, layout, new() { position = new(20f, 15f), isHovered = true, isPressed = true, isDown = true }); + _ = pointer.Step(view, layout, new() { position = new(90f, 90f), isDown = true }); + Assert.Null(pointer.Step(view, layout, new() { cancel = true }).movedNodes); + Assert.Empty(pointer.previewPositions); + Assert.Equal(new GraphPosition(60f, 40f), completed.movedNodes[node.id]); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ConnectionsAreNeutralIntentsAndMissingEndpointsCannotBeConnected(bool missing) + { + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + GraphEndpoint output = new(new("flow.source"), new("next")); + GraphEndpoint input = new(new("flow.target"), new("previous")); + _ = layout.SetNodes([ + new(output.nodeId, default, new(200f, 100f), 24f, "Source", [new(output, GraphPortDirection.Output, new(200f, 40f), "Next")]), + new(input.nodeId, new(300f, 0f), new(200f, 100f), 24f, "Target", [new(input, GraphPortDirection.Input, new(0f, 40f), "Previous", isMissing: missing)]) + ]); + _ = pointer.Step(view, layout, new() { position = new(200f, 40f), isHovered = true, isPressed = true, isDown = true }); + Assert.Equal(output, state.pendingConnection); + GraphCanvasPointerResult result = pointer.Step(view, layout, new() { position = new(300f, 40f), isHovered = true, isReleased = true }); + Assert.Equal(missing ? null : output, result.output); + Assert.Equal(missing ? null : input, result.input); + Assert.Null(state.pendingConnection); + } + + [Fact] + public void ReadOnlyChangesCancelEditsButStillAllowBoxSelection() + { + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + GraphNodeId id = new("flow.read-only"); + _ = layout.SetNodes([new(id, new(20f, 20f), new(200f, 100f), 24f, "Read only", [])]); + _ = pointer.Step(view, layout, new() { position = new(30f, 25f), isHovered = true, isPressed = true, isDown = true }); + _ = pointer.Step(view, layout, new() { position = new(100f, 100f), isDown = true }); + Assert.Null(pointer.Step(view, layout, new() { position = new(100f, 100f), isReleased = true, readOnly = true }).movedNodes); + Assert.False(pointer.isMoving); + _ = pointer.Step(view, layout, new() { position = new(5f, 150f), isHovered = true, isPressed = true, isDown = true, readOnly = true }); + Assert.True(pointer.isBoxSelecting); + _ = pointer.Step(view, layout, new() { position = new(240f, 10f), isReleased = true, readOnly = true }); + Assert.Equal(id, Assert.Single(state.selectedNodes)); + Assert.False(pointer.isBoxSelecting); + Assert.Throws(() => pointer.Step(view, layout, new() { position = new(float.NaN, 0f) })); + } +} diff --git a/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPresentationTests.cs b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPresentationTests.cs new file mode 100644 index 000000000..e9c5dda4e --- /dev/null +++ b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasPresentationTests.cs @@ -0,0 +1,78 @@ +using System; +using System.Linq; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using Inno.Editor.Graph.ImGui; +using Xunit; + +namespace Inno.Editor.Graph.Tests; + +public sealed class GraphCanvasPresentationTests +{ + [Fact] + public void NonShaderNodesUseTheSameOrderedHitAndBoxGeometry() + { + var state = new GraphCanvasState(); + state.SetViewport(new(17f, -9f), 1.5f); + var view = new GraphCanvasView(state, new(100f, 200f), new(800f, 600f)); + GraphCanvasNode first = Node("workflow.start", new(0f, 0f)); + GraphCanvasNode second = Node("workflow.finish", new(50f, 0f)); + var layout = new GraphCanvasLayout(); + Assert.Equal(2, layout.SetNodes([first, second])); + Assert.Same(second, view.HitNode(layout, view.ToScreen(new(60f, 10f)))); + Assert.Equal(new[] { first.id, second.id }, view.BoxNodes(layout, view.ToScreen(new(-5f, -5f)), view.ToScreen(new(300f, 100f)))); + Assert.Equal(0, layout.SetNodes([second, first])); + Assert.Same(first, view.HitNode(layout, view.ToScreen(new(60f, 10f)))); + } + + [Fact] + public void PortHitGeometryPreservesMissingAndExplicitLabelBounds() + { + var state = new GraphCanvasState(); + var view = new GraphCanvasView(state, Vector2.Zero, new(800f, 600f)); + GraphNodeId id = new("workflow.output"); + var port = new GraphCanvasPort(new(id, new("next")), GraphPortDirection.Output, new(200f, 40f), "Continue", + isMissing: true, labelBounds: (new(100f, 28f), new(200f, 52f))); + var node = new GraphCanvasNode(id, new(10f, 10f), new(200f, 100f), 24f, "Flow step", [port]); + var layout = new GraphCanvasLayout(); + _ = layout.SetNodes([node]); + Assert.Equal(port, view.HitPort(layout, new(210f, 50f))); + Assert.Equal(port, view.HitPort(layout, new(150f, 50f))); + Assert.Null(view.HitPort(layout, new(150f, 90f))); + Assert.Throws(() => view.HitPort(layout, Vector2.Zero, -1f)); + } + + [Fact] + public void ChangingOneOfTwoThousandNodesPreservesOtherGeometry() + { + GraphCanvasNode[] nodes = Enumerable.Range(0, 2000) + .Select(index => Node("workflow." + index, new(index * 220f, 0f))).ToArray(); + var layout = new GraphCanvasLayout(); + Assert.Equal(2000, layout.SetNodes(nodes)); + GraphCanvasNode retained = nodes[999]; + nodes[1000] = Node(nodes[1000].id.value, new(100f, 200f)); + Assert.Equal(1, layout.SetNodes(nodes)); + Assert.True(layout.TryGetNode(retained.id, out GraphCanvasNode? current)); + Assert.Same(retained, current); + Assert.Equal(0, layout.SetNodes(nodes)); + } + + [Fact] + public void InvalidCandidatesDoNotReplaceTheCurrentSnapshot() + { + var layout = new GraphCanvasLayout(); + GraphCanvasNode node = Node("workflow.valid", default); + _ = layout.SetNodes([node]); + Assert.Throws(() => layout.SetNodes([node, node])); + Assert.Same(node, Assert.Single(layout.nodes)); + Assert.Throws(() => new GraphCanvasNode(node.id, default, new(float.NaN, 10f), 2f, "Invalid", [])); + Assert.Throws(() => new GraphCanvasNode(node.id, default, new(200f, 100f), 24f, "Invalid", + [new GraphCanvasPort(new(new("foreign"), new("port")), GraphPortDirection.Input, Vector2.Zero, "Foreign")])); + } + + private static GraphCanvasNode Node( + string id, + GraphPosition position + ) => new(new(id), position, new(200f, 100f), 24f, id, []); +} diff --git a/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs index 6d24cb67c..2b2112261 100644 --- a/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs +++ b/tests/editor/Inno.Editor.Graph.Tests/GraphCanvasViewTests.cs @@ -8,6 +8,19 @@ namespace Inno.Editor.Graph.Tests; public sealed class GraphCanvasViewTests { + [Fact] + public void FrameProjectionDoesNotChangeWhenLaterNavigationMutatesItsSource() + { + var state = new GraphCanvasState(); + var view = new GraphCanvasView(state, new(100f, 200f), new(800f, 600f)); + state.SetViewport(new(50f, 75f), 2f); + Assert.Equal(new Vector2(110f, 220f), view.ToScreen(new(10f, 20f))); + Assert.Equal(1f, view.zoom); + var next = new GraphCanvasView(state, new(100f, 200f), new(800f, 600f)); + Assert.Equal(new Vector2(170f, 315f), next.ToScreen(new(10f, 20f))); + Assert.Equal(2f, next.zoom); + } + [Theory] [InlineData(0.1f)] [InlineData(1f)] diff --git a/tests/editor/Inno.Editor.Graph.Tests/GraphDocumentControllerTests.cs b/tests/editor/Inno.Editor.Graph.Tests/GraphDocumentControllerTests.cs index 702c65340..b6c9d4bfb 100644 --- a/tests/editor/Inno.Editor.Graph.Tests/GraphDocumentControllerTests.cs +++ b/tests/editor/Inno.Editor.Graph.Tests/GraphDocumentControllerTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using Inno.Extensibility.Modules; using Inno.Extensibility.Reload; using Inno.Core.Graphs; @@ -62,6 +63,7 @@ public void Dispose() [Theory] [InlineData(false)] [InlineData(true)] + [Trait("Acceptance", "Lifecycle")] public void AssemblyPublicationPreservesNeutralGraphAndRollsBackLaterParticipantFailure(bool reject) { GraphEditorModule module = Assert.IsType(m_sink.module); @@ -73,16 +75,22 @@ public void AssemblyPublicationPreservesNeutralGraphAndRollsBackLaterParticipant var rejection = new RejectionParticipant(reject); using IDisposable registration = m_reloads.Register(rejection); string directory = Path.Combine(AppContext.BaseDirectory, "Modules"); - using (AssemblyReloadSession reload = m_modules.BeginReload([new DotNetModuleSource + for (int iteration = 0; iteration < 10; iteration++) { - artifactRootDirectory = Path.Combine(m_testRoot, "Assemblies"), - moduleName = "GraphRecovery", domain = AssemblyDomain.InnoPlugin, scope = AssemblyScope.Runtime, - mainAssemblyPath = Path.Combine(directory, "Inno.Extensibility.Modules.TestModule.dll"), - preloadAssemblyPaths = [Path.Combine(directory, "Reloadable.PrivateDependency.dll")] - }])) - { - if (reject) Assert.Throws(() => m_reloads.Execute(reload)); - else _ = m_reloads.Execute(reload); + using (AssemblyReloadSession reload = m_modules.BeginReload([new DotNetModuleSource + { + artifactRootDirectory = Path.Combine(m_testRoot, "Assemblies"), + moduleName = "GraphRecovery", domain = AssemblyDomain.InnoPlugin, scope = AssemblyScope.Runtime, + mainAssemblyPath = Path.Combine(directory, "Inno.Extensibility.Modules.TestModule.dll"), + preloadAssemblyPaths = [Path.Combine(directory, "Reloadable.PrivateDependency.dll")] + }])) + { + if (reject) Assert.Throws(() => m_reloads.Execute(reload)); + else _ = m_reloads.Execute(reload); + } + m_modules.generations.Wait(); + Assert.Equal(GenerationState.Ready, m_modules.generations.state); + Assert.Equal(before, GraphDocumentCodec.Encode(controller.document, m_serialization)); } m_modules.generations.Wait(); Assert.Equal(before, GraphDocumentCodec.Encode(controller.document, m_serialization)); @@ -95,6 +103,37 @@ public void AssemblyPublicationPreservesNeutralGraphAndRollsBackLaterParticipant Assert.Single(controller.document.nodes); } + [Fact] + [Trait("Acceptance", "Lifecycle")] + public void OneHundredDocumentCyclesReleasePayloadsAndRejectRetiredControllers() + { + GraphEditorModule module = Assert.IsType(m_sink.module); + Guid identity = Guid.NewGuid(); + var retired = new List<(GraphDocumentController controller, WeakReference document)>(); + for (int iteration = 0; iteration < 100; iteration++) + retired.Add(OpenAndClose()); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + foreach (var entry in retired) + { + Assert.False(entry.controller.isAvailable); + Assert.False(entry.document.TryGetTarget(out _)); + Assert.Throws(() => entry.controller.AddNode("retired", default)); + } + Assert.False(module.TryOpenDocument(identity, m_runtime.interactions.history, out _)); + + [MethodImpl(MethodImplOptions.NoInlining)] + (GraphDocumentController, WeakReference) OpenAndClose() + { + var document = new GraphDocument(); + GraphDocumentController controller = module.OpenDocument(identity, document, m_runtime.interactions.history); + controller.AddNode("missing.example", new(7f, 9f)); + Assert.True(module.CloseDocument(identity)); + return (controller, new WeakReference(document)); + } + } + private sealed class RejectionParticipant(bool reject) : IEditorReloadParticipant, IGenerationChange { public IGenerationChange Capture(AssemblyReloadContext context) => this; diff --git a/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj b/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj index ca09bb5c3..f184a2464 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj +++ b/tests/editor/Inno.Editor.ImGui.Tests/Inno.Editor.ImGui.Tests.csproj @@ -16,6 +16,7 @@ + diff --git a/tests/editor/Inno.Editor.ImGui.Tests/NativeWindowTestHost.cs b/tests/editor/Inno.Editor.ImGui.Tests/NativeWindowTestHost.cs index a002d3dc5..e1406bd4a 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/NativeWindowTestHost.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/NativeWindowTestHost.cs @@ -8,6 +8,11 @@ internal static class NativeWindowTestHost { private static readonly IReadOnlyDictionary S_CONTRACTS = new Dictionary { + ["ViewportMetricsTests.FramePreparationRunsBeforeNativeCreationAndRejectsReentrantOperations"] = () => new ViewportMetricsTests().FramePreparationRunsBeforeNativeCreationAndRejectsReentrantOperations(), + ["ViewportMetricsTests.FailedPreparationDoesNotBeginOrPublishANativeFrame"] = () => new ViewportMetricsTests().FailedPreparationDoesNotBeginOrPublishANativeFrame(), + ["ViewportMetricsTests.AFailedDrawRetiresItsNativeFrameBeforeTheNextRequest"] = () => new ViewportMetricsTests().AFailedDrawRetiresItsNativeFrameBeforeTheNextRequest(), + ["ViewportMetricsTests.ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability.False"] = () => new ViewportMetricsTests().ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability(false), + ["ViewportMetricsTests.ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability.True"] = () => new ViewportMetricsTests().ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability(true), ["ViewportMetricsTests.FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback.False"] = () => new ViewportMetricsTests().FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback(false), ["ViewportMetricsTests.FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback.True"] = () => new ViewportMetricsTests().FrameRefreshesDrawableMetricsWithoutAPlatformScaleCallback(true), ["ViewportMetricsTests.MetricRefreshPreservesRequestedViewportGeometry"] = () => new ViewportMetricsTests().MetricRefreshPreservesRequestedViewportGeometry(), diff --git a/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs index 1bc7f7d67..4fd4fa5d5 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/ViewportMetricsTests.cs @@ -19,6 +19,111 @@ namespace Inno.Editor.ImGui.Tests; public sealed unsafe class ViewportMetricsTests { + [Fact] + public void FramePreparationRunsBeforeNativeCreationAndRejectsReentrantOperations() + { + if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(FramePreparationRunsBeforeNativeCreationAndRejectsReentrantOperations))) + return; + using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); + using var window = application.CreateWindow(new PlatformWindowOptions + { + title = "Prepared presentation frame", width = 320, height = 240, visible = false + }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.None, + new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); + using var ui = new EditorUiSession(); + context.SetIniFile(null); + int phase = 0; + context.RenderFrame(() => + { + Assert.Equal(1, phase); + Assert.Equal(1, NativeImGui.Raw.GetCurrentContext()->WithinFrameScope); + Assert.Equal(ui.widgets.style.fontScale, NativeImGui.Raw.GetStyle()->FontScaleMain); + phase = 2; + }, () => + { + using IDisposable scope = ui.Enter(); + Assert.Equal(0, NativeImGui.Raw.GetCurrentContext()->WithinFrameScope); + Assert.Equal(new Vector2(320f, 240f), Unsafe.BitCast(NativeImGui.Raw.GetIO()->DisplaySize)); + Assert.True(ui.widgets.style.SetZoom(1.25f)); + ui.widgets.SetupStyle(); + Assert.Throws(() => context.RenderFrame(static () => { })); + Assert.Throws(() => context.Dispose()); + phase = 1; + }); + Assert.Equal(2, phase); + Assert.Equal(0, NativeImGui.Raw.GetCurrentContext()->WithinFrameScope); + } + + [Fact] + public void FailedPreparationDoesNotBeginOrPublishANativeFrame() + { + if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(FailedPreparationDoesNotBeginOrPublishANativeFrame))) + return; + using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); + using var window = application.CreateWindow(new PlatformWindowOptions + { + title = "Failed frame preparation", width = 320, height = 240, visible = false + }); + var renderer = new FixtureRenderer(); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.None, + new ImGuiInteractionOptions(false, 1), renderer); + context.SetIniFile(null); + bool drawn = false; + var failure = new InvalidOperationException("Frame preparation failed."); + Assert.Same(failure, Assert.Throws(() => context.RenderFrame(() => drawn = true, () => throw failure))); + Assert.False(drawn); + Assert.Equal(0, NativeImGui.Raw.GetCurrentContext()->WithinFrameScope); + context.RenderFrame(() => drawn = true); + Assert.True(drawn); + } + + [Fact] + public void AFailedDrawRetiresItsNativeFrameBeforeTheNextRequest() + { + if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(AFailedDrawRetiresItsNativeFrameBeforeTheNextRequest))) + return; + using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); + using var window = application.CreateWindow(new PlatformWindowOptions + { + title = "Failed presentation frame", width = 320, height = 240, visible = false + }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.None, + new ImGuiInteractionOptions(false, 1), new FixtureRenderer()); + context.SetIniFile(null); + var failure = new InvalidOperationException("The frame extension failed."); + try + { + Assert.Same(failure, Assert.Throws(() => context.RenderFrame(() => throw failure))); + Assert.Equal(0, NativeImGui.Raw.GetCurrentContext()->WithinFrameScope); + context.RenderFrame(static () => { }); + } + finally + { + if (NativeImGui.Raw.GetCurrentContext()->WithinFrameScope != 0) + NativeImGui.Raw.EndFrame(); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability(bool supported) + { + if (NativeWindowTestHost.RunIfRequired(nameof(ViewportMetricsTests) + "." + nameof(ContextAdvertisesOnlyTheSelectedRenderersVertexOffsetCapability) + "." + supported)) + return; + using var application = new Sdl3PlatformApplication(NativeHostFixture.Create()); + using var window = application.CreateWindow(new PlatformWindowOptions + { + title = "Renderer capability", width = 320, height = 240, visible = false + }); + using var context = application.CreateImGuiContext(window, ImGuiContextFlags.None, + new ImGuiInteractionOptions(false, 1), new FixtureRenderer { supportsVertexOffsets = supported }); + context.SetIniFile(null); + context.RenderFrame(() => Assert.Equal(supported, + (NativeImGui.Raw.GetIO()->BackendFlags & (int)Inno.Native.ImGui.ImGuiBackendFlags.RendererHasVtxOffset) != 0)); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -199,6 +304,7 @@ private sealed class FixtureRenderer : IPlatformImGuiRenderer { internal PlatformImGuiViewportTarget? target; public bool supportsViewports => true; + public bool supportsVertexOffsets { get; init; } public void RenderMain(IntPtr drawData) { diff --git a/tests/editor/Inno.Editor.ImGui.Tests/ViewportRetirementTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/ViewportRetirementTests.cs index c58f4e631..b7fdb6efd 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/ViewportRetirementTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/ViewportRetirementTests.cs @@ -160,6 +160,7 @@ private sealed class PendingRenderer : IPlatformImGuiRenderer internal bool failCreation; public bool supportsViewports => true; + public bool supportsVertexOffsets => false; public void RenderMain(IntPtr drawData) { } public void SynchronizeMainOutput( int pixelWidth, diff --git a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs index 2961907c8..0cd008a8f 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs @@ -16,6 +16,354 @@ namespace Inno.Editor.ImGui.Tests; public sealed unsafe class WidgetLayoutTests { + [Fact] + public void StandaloneMetricsApplyImmediatelyAndRetainedMetricsDoNotRootTheirRetiredSession() + { + var standalone = new EditorStyleMetrics(); + Assert.True(standalone.SetZoom(1.25f)); + Assert.True(standalone.SetCompactMode(true)); + Assert.Equal(1.25f, standalone.zoom); + Assert.True(standalone.isCompact); + EditorStyleMetrics retained = RetireStyleOwner(out WeakReference owner); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(owner.IsAlive); + Assert.Throws(() => retained.SetZoom(1.1f)); + Assert.Throws(() => retained.SetCompactMode(true)); + } + + [Fact] + public void StyleRequestsDoNotChangeTheActiveNativeFrame() + { + using NativeContext context = new(1f); + var widgets = EditorUI.current.widgets; + for (int frame = 0; frame < 2; frame++) + { + context.Frame(() => + { + widgets.SetupStyle(); + BeginWindow("Stable style input", new(400f, 300f)); + try + { + if (frame == 0) + { + float padding = widgets.style.cardBodyPadding.X; + Assert.True(widgets.style.SetZoom(1.25f)); + Assert.True(widgets.style.SetCompactMode(true)); + Assert.Equal(1f, widgets.style.zoom); + Assert.False(widgets.style.isCompact); + widgets.SetupStyle(); + Assert.Equal(1f, widgets.style.zoom); + Assert.Equal(padding, widgets.style.cardBodyPadding.X); + } + else + { + Assert.Equal(1.25f, widgets.style.zoom); + Assert.True(widgets.style.isCompact); + } + widgets.PropertyLabel("Stable frame / 同帧尺度"); + } + finally + { + NativeImGui.End(); + } + }); + } + } + + [Fact] + public void AForeignNativeContextCannotPublishChangesIntoASuspendedOwnerFrame() + { + using NativeContext context = new(1f); + var widgets = EditorUI.current.widgets; + context.Frame(() => + { + widgets.SetupStyle(); + BeginWindow("Suspended style owner", new(400f, 300f)); + try + { + ImGuiNativeContext* own = NativeImGui.Raw.GetCurrentContext(); + ImGuiNativeContext* foreign = NativeImGui.Raw.CreateContext(null); + try + { + NativeImGui.Raw.SetCurrentContext(foreign); + Assert.True(widgets.style.SetZoom(1.25f)); + Assert.Equal(1f, widgets.style.zoom); + } + finally + { + NativeImGui.Raw.SetCurrentContext(own); + NativeImGui.Raw.DestroyContext(foreign); + NativeImGui.Raw.SetCurrentContext(own); + } + widgets.SetupStyle(); + Assert.Equal(1f, widgets.style.zoom); + } + finally + { + NativeImGui.End(); + } + }); + context.Frame(() => + { + widgets.SetupStyle(); + Assert.Equal(1.25f, widgets.style.zoom); + }); + } + + [Fact] + public void CopiedWindowGeometryRequiresAnActiveNativeFrame() + { + using NativeContext context = new(1f); + Assert.Throws(() => EditorImGui.GetClipBounds()); + Assert.Throws(() => EditorImGui.GetWindowBounds()); + context.Frame(() => + { + BeginWindow("Geometry owner", new(400f, 300f)); + try + { + var clip = EditorImGui.GetClipBounds(); + Assert.True(clip.maximum.X > clip.minimum.X && clip.maximum.Y > clip.minimum.Y); + Assert.True(EditorImGui.GetWindowBounds().maximumX > EditorImGui.GetWindowBounds().minimumX); + } + finally + { + NativeImGui.End(); + } + }); + Assert.Throws(() => EditorImGui.GetClipBounds()); + Assert.Throws(() => EditorImGui.GetWindowBounds()); + } + + [Fact] + public void FlattenedRowsRetainExpansionWithoutOwningNativeTreeScopes() + { + using NativeContext context = new(1f); + for (int frame = 0; frame < 3; frame++) + { + context.Frame(() => + { + BeginWindow("Flattened tree", new(400f, 300f)); + try + { + var widgets = EditorUI.current.widgets; + widgets.SetTreeNodeOpen("root", true); + ulong revision = widgets.treeExpansionRevision; + widgets.SetTreeNodeOpen("root", true); + Assert.Equal(revision, widgets.treeExpansionRevision); + int nativeDepth = NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth; + var root = widgets.TreeNode("root", _ => NativeImGui.TextUnformatted("Root"), new() { depth = 0 }); + Assert.True(root.isOpen); + Assert.True(widgets.IsTreeNodeOpen("root")); + Assert.Equal(nativeDepth, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); + var child = widgets.TreeNode("child", _ => NativeImGui.TextUnformatted("Deep child"), new() { depth = 7, isLeaf = true }); + Assert.True(child.contentMin.X > root.contentMin.X); + var failure = new InvalidOperationException("Rejected flat content"); + Assert.Same(failure, Assert.Throws(() => widgets.TreeNode("failed", _ => throw failure, new() { depth = 2 }))); + Assert.Equal(nativeDepth, NativeImGui.Raw.GetCurrentWindow()->DC.TreeDepth); + widgets.SetTreeNodeOpen("root", false); + Assert.True(widgets.treeExpansionRevision > revision); + Assert.False(widgets.IsTreeNodeOpen("root")); + } + finally + { + NativeImGui.End(); + } + }); + } + } + + [Fact] + public void TenThousandTreeRowsSubmitCustomContentOnlyWithinTheViewport() + { + using NativeContext context = new(1f); + for (int frame = 0; frame < 3; frame++) + { + context.Frame(() => + { + BeginWindow("Large tree", new(400f, 300f)); + int drawn = 0; + Action draw = _ => + { + drawn++; + NativeImGui.TextUnformatted("Visible content"); + }; + for (int row = 0; row < 10_000; row++) + _ = EditorUI.current.widgets.TreeNode("row-" + row, draw, new() { isLeaf = true }); + Assert.InRange(drawn, 1, 64); + if (frame == 1) + NativeImGui.SetScrollY(100_000f); + NativeImGui.End(); + }); + } + } + + [Fact] + public void FixedRowsRetainExplicitRowsAndBalanceTheirNativeOwner() + { + using NativeContext context = new(1f); + context.Frame(() => + { + BeginWindow("Clipped rows", new(400f, 300f)); + int submitted = 0; + bool preserved = false; + float height = NativeImGui.GetTextLineHeightWithSpacing(); + EditorUI.current.widgets.ClippedRows(10_000, height, 0, ( + state, + start, + end + ) => + { + submitted += end - start; + preserved |= start <= 9999 && end > 9999; + for (int row = start; row < end; row++) + NativeImGui.Dummy(new(100f, NativeImGui.GetTextLineHeight())); + }, [9999]); + Assert.InRange(submitted, 1, 64); + Assert.True(preserved); + NativeImGui.End(); + }); + } + + [Fact] + public void FixedRowFailureReleasesTheClipperBeforeTheNextFrame() + { + using NativeContext context = new(1f); + for (int frame = 0; frame < 2; frame++) + { + context.Frame(() => + { + BeginWindow("Clipper failure", new(400f, 300f)); + int before = NativeImGui.Raw.GetCurrentContext()->ClipperTempDataStacked; + var failure = new InvalidOperationException("Row extension rejected drawing."); + Assert.Same(failure, Assert.Throws(() => EditorUI.current.widgets.ClippedRows( + 10_000, NativeImGui.GetTextLineHeightWithSpacing(), 0, ( + state, + start, + end + ) => throw failure))); + Assert.Equal(before, NativeImGui.Raw.GetCurrentContext()->ClipperTempDataStacked); + NativeImGui.End(); + }); + } + } + + [Theory] + [InlineData(0.75f)] + [InlineData(1f)] + [InlineData(1.5f)] + public void TrendUsesOneBoundedRegionAndFiniteGeometry(float zoom) + { + using NativeContext context = new(zoom); + context.Frame(() => + { + BeginWindow("Trend bounds", new Vector2(400f, 400f)); + var widgets = EditorUI.current.widgets; + widgets.Trend("Finite extremes", new[] { float.MinValue, 0f, float.MaxValue }, offset: 1); + Assert.Equal(widgets.style.trendHeight, NativeImGui.GetItemRectSize().Y, 3); + Assert.True(NativeImGui.GetItemRectSize().X <= 400f); + var draw = NativeImGui.Raw.GetWindowDrawList(); + for (int index = 0; index < draw->VtxBuffer.Size; index++) + { + Vector2 point = Unsafe.BitCast(draw->VtxBuffer.Data[index].Pos); + Assert.True(float.IsFinite(point.X) && float.IsFinite(point.Y)); + } + widgets.Trend("Empty", ReadOnlySpan.Empty); + Assert.Throws(() => widgets.Trend("Offset", new[] { 1f }, offset: 1)); + Assert.Throws(() => widgets.Trend("Invalid", new[] { float.NaN })); + NativeImGui.End(); + }); + } + + [Fact] + public void ListNavigationUsesVisibleIndicesAndRespectsTextAndPopupOwnership() + { + string root = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "InnoListNavigation", Guid.NewGuid().ToString("N")); + System.IO.Directory.CreateDirectory(root); + try + { + var diagnostics = new Inno.Core.Diagnostics.DiagnosticHub(); + using IDisposable diagnosticScope = diagnostics.EnterScope(); + using var modules = new Inno.Extensibility.Modules.ModuleHost(new() + { + catalogSource = new Inno.Adapter.Modules.DotNet.DotNetAssemblyCatalogSource(typeof(WidgetLayoutTests).Assembly) + }); + using var types = new Inno.Extensibility.Types.TypeCatalog(modules, new Inno.Adapter.Modules.DotNet.ReflectionTypeCatalogSource()); + using var logs = new Inno.Core.Logging.LogRouter(); + using var runtime = new Inno.Editor.Interactions.EditorInteractionRuntime( + root, + types, + logs, + new Inno.Editor.Core.EditorKeyboardPolicy(Inno.Core.Input.KeyModifier.Control, "Super")); + runtime.Start(); + using NativeContext context = new(1f); + NativeImGui.Raw.GetIO()->ConfigFlags |= (int)Inno.Native.ImGui.ImGuiConfigFlags.NavEnableKeyboard; + int focused = 4; + bool textInput = false; + bool popup = false; + string query = string.Empty; + Inno.Editor.ImGui.ImGuiWidget.ListNavigationResult observed = default; + Action draw = () => + { + BeginWindow("List navigation", new Vector2(500f, 400f)); + if (textInput) + { + EditorImGui.FocusNextItem(); + _ = EditorUI.current.widgets.SearchInput("keyboard-query", "Search", ref query, 1024); + } + if (popup) + { + NativeImGui.OpenPopup("Input owner"); + if (NativeImGui.BeginPopup("Input owner")) + { + NativeImGui.TextUnformatted("Exclusive popup input"); + NativeImGui.EndPopup(); + } + } + observed = EditorUI.current.widgets.NavigateList(runtime.interactions, 100, focused, pageSize: 10); + focused = observed.focusedIndex; + NativeImGui.End(); + }; + context.Frame(draw); + context.Frame(draw); + var io = NativeImGui.Raw.GetIO(); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.PageDown, 1); + context.Frame(draw); + Assert.Equal(14, focused); + Assert.True(observed.selectionRequested); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.PageDown, 0); + context.Frame(draw); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.End, 1); + context.Frame(draw); + Assert.Equal(99, focused); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.End, 0); + context.Frame(draw); + textInput = true; + context.Frame(draw); + context.Frame(draw); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.Home, 1); + context.Frame(draw); + Assert.Equal(99, focused); + Assert.False(observed.selectionRequested); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.Home, 0); + context.Frame(draw); + textInput = false; + popup = true; + context.Frame(draw); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.UpArrow, 1); + context.Frame(draw); + Assert.Equal(99, focused); + Assert.False(observed.selectionRequested); + NativeImGui.Raw.AddKeyEvent(io, Inno.Native.ImGui.ImGuiKey.UpArrow, 0); + context.Frame(draw); + } + finally + { + System.IO.Directory.Delete(root, recursive: true); + } + } + [Theory] [InlineData("card-leading")] [InlineData("card-trailing")] @@ -565,6 +913,14 @@ public void MixedValueRestoresOuterStateAfterNestedGroupsAndFailures() }); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static EditorStyleMetrics RetireStyleOwner(out WeakReference owner) + { + using var session = new EditorUiSession(); + owner = new WeakReference(session); + return session.widgets.style; + } + private static void BeginWindow( string name, Vector2 size diff --git a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs index e8f57e9fb..f48c05563 100644 --- a/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs +++ b/tests/editor/Inno.Editor.Interactions.Tests/EditorRuntimeTests.Selection.cs @@ -9,6 +9,44 @@ namespace Inno.Editor.Interactions.Tests; public sealed partial class EditorRuntimeTests { + [Fact] + public void MissingIdentityRejectsTheEntireVisibleRangeWithoutChangingSelection() + { + var first = new SelectionQueueTarget(); + var last = new SelectionQueueTarget(); + Assert.True(m_identities.Register(first)); + Assert.True(m_identities.Register(last)); + m_runtime.interactions.SetSelection(first); + EditorSelectionSnapshot original = m_runtime.interactions.selection.Capture(); + RuntimeIdentity firstId = first.identity.runtimeIdentity!.Value; + RuntimeIdentity missing = new(firstId.domainId, int.MaxValue); + Assert.Throws(() => m_runtime.interactions.SelectRange( + [firstId, missing, last.identity.runtimeIdentity!.Value], last)); + Assert.Same(original, m_runtime.interactions.selection.Capture()); + Assert.Same(first, m_runtime.interactions.selection.selectedTarget); + } + + [Fact] + public void LargeIdentityRangesKeepOrderedPrimaryAndIndexedMembership() + { + var targets = new SelectionQueueTarget[10_000]; + var order = new RuntimeIdentity[targets.Length]; + for (int index = 0; index < targets.Length; index++) + { + targets[index] = new SelectionQueueTarget(); + Assert.True(m_identities.Register(targets[index])); + order[index] = targets[index].identity.runtimeIdentity!.Value; + } + m_runtime.interactions.SetSelection(targets[0]); + m_runtime.interactions.SelectRange(order, targets[^1]); + Assert.Equal(targets.Length, m_runtime.interactions.selection.count); + Assert.Same(targets[^1], m_runtime.interactions.selection.selectedTarget); + foreach (SelectionQueueTarget target in targets) + Assert.True(m_runtime.interactions.selection.Contains(target)); + m_runtime.interactions.SetSelection(targets[0]); + Assert.False(m_runtime.interactions.selection.Contains(targets[^1])); + } + [Fact] public void BoxedTargetsDoNotKeepReferencedObjectsAliveThroughSelectionFocusOrSnapshots() { @@ -150,13 +188,13 @@ public void RangeSelectionUsesVisibleOrderAndTogglePreservesTheAnchor() Assert.True(m_identities.Register(second)); Assert.True(m_identities.Register(third)); m_runtime.interactions.SetSelection(first); - m_runtime.interactions.SelectRange([first, second, third], third); + m_runtime.interactions.SelectRange([first.identity.runtimeIdentity!.Value, second.identity.runtimeIdentity!.Value, third.identity.runtimeIdentity!.Value], third); Assert.Equal(3, m_runtime.interactions.selection.count); m_runtime.interactions.ToggleSelection(second); Assert.False(m_runtime.interactions.selection.Contains(second)); Assert.Same(first, m_runtime.interactions.selection.anchor); Assert.Same(third, m_runtime.interactions.selection.selectedTarget); - m_runtime.interactions.SelectRange([third, second, first], second); + m_runtime.interactions.SelectRange([third.identity.runtimeIdentity!.Value, second.identity.runtimeIdentity!.Value, first.identity.runtimeIdentity!.Value], second); var targets = new List(); Assert.Equal(2, m_runtime.interactions.selection.CopyTargets(targets)); Assert.Equal([first, second], targets); diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs index c8bc5492f..bc60a5e36 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ConsolePanelNativeLayoutTests.cs @@ -142,8 +142,7 @@ void Draw(float width) if (Marshal.PtrToStringUTF8((nint)window->Name)?.Contains("##ConsoleEntryCard", StringComparison.Ordinal) == true) card = window; } Assert.True(card != null); - using NativeStringLease nativeTestText1 = Utf8Codec.EncodeNullTerminated("##HeaderTable", allowsNull: false); - ImGuiTable* header = ImGuiApi.Raw.TableFindByID(ImGuiApi.Raw.GetID(card, (sbyte*)nativeTestText1.pointer, null)); + ImGuiTable* header = FindActiveTable(card, 3); Assert.True(header != null); Vector2 toggle = new(header->Columns.Data[0].WorkMinX + 6, header->RowPosY1 + ImGuiApi.GetFontSize() / 2); ImGuiApi.Raw.AddMousePosEvent(ImGuiApi.Raw.GetIO(), toggle.X, toggle.Y); @@ -157,9 +156,7 @@ void Draw(float width) foreach (float width in new[] { 960f, 500f, 260f, 140f, 960f }) { Draw(width); - uint occurrenceId = ImGuiApi.Raw.GetID(card, unchecked((int)Assert.Single(console.Capture().occurrences).sequence)); - using NativeStringLease nativeTestText2 = Utf8Codec.EncodeNullTerminated("##ConsoleEntryDetails", allowsNull: false); - ImGuiTable* table = ImGuiApi.Raw.TableFindByID(ImGuiApi.Raw.ImHashStr((sbyte*)nativeTestText2.pointer, 0, occurrenceId)); + ImGuiTable* table = FindActiveTable(card, 2); bool details = table != null && table->LastFrameActive == ImGuiApi.GetFrameCount(); if (details) { @@ -175,6 +172,20 @@ void Draw(float width) finally { ImGuiApi.Raw.DestroyContext(context); Directory.Delete(root, true); } } + private static ImGuiTable* FindActiveTable( + ImGuiWindow* window, + int columns + ) { + ImGuiNativeContext* context = ImGuiApi.Raw.GetCurrentContext(); + for (int index = 0; index < context->Tables.Buf.Size; index++) + { + ImGuiTable* table = &context->Tables.Buf.Data[index]; + if (table->OuterWindow == window && table->ColumnsCount == columns && table->LastFrameActive == ImGuiApi.GetFrameCount()) + return table; + } + return null; + } + private sealed class RejectedConsole : IEditorConsole { public int capacity { get; set; } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleSearchNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleSearchNativeTests.cs new file mode 100644 index 000000000..dbbceacfd --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleSearchNativeTests.cs @@ -0,0 +1,154 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BGCS.Runtime; +using Inno.Core.Diagnostics; +using Inno.Core.Logging; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using Xunit; +using NativeImGui = Inno.Native.ImGui.ImGui; +using NativeVector2 = Inno.Native.ImGui.ImVec2C; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class ConsoleSearchNativeTests +{ + [Fact] + public void ActualConsoleFiltersMessagesFilesSourcesAndLongUnicodeQueries() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + var logger = host.logs.CreateLogger(); + for (int occurrence = 0; occurrence < 2; occurrence++) + logger.Write(LogLevel.Info, "Needle message"); + logger.Write(LogLevel.Warn, "Unrelated message"); + string longMessage = new('x', 300); + logger.Write(LogLevel.Info, longMessage); + host.logs.Flush(); + using var reporter = host.diagnostics.CreateReporter(new("search-source", "SOURCE-NAME")); + reporter.Publish(new("SEARCH_CODE", "A diagnostic", DiagnosticSeverity.Error, location: new DiagnosticLocation("Assets/漢字.shader", 42, 3))); + host.DrawPanel(panel); + host.DrawPanel(panel); + Assert.Equal(5, host.console.Capture().occurrences.Count); + Assert.Equal(4, CountCards()); + foreach (string query in new[] { "nEeDlE", "漢字", "source-name", "search_code", longMessage }) + { + Search(host, panel, query); + Assert.Equal(1, CountCards()); + Assert.Equal(5, host.console.Capture().occurrences.Count); + } + Search(host, panel, "no matching content"); + Assert.Equal(0, CountCards()); + Search(host, panel, string.Empty); + Assert.Equal(4, CountCards()); + } + + [Fact] + public void FilteredKeyboardTargetCopiesTheSameGroupAndClearReleasesIt() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + var logger = host.logs.CreateLogger(); + for (int occurrence = 0; occurrence < 2; occurrence++) + logger.Write(LogLevel.Info, "Needle message"); + logger.Write(LogLevel.Warn, "Other entry"); + host.logs.Flush(); + host.DrawPanel(panel); + host.DrawPanel(panel); + Search(host, panel, "needle"); + ImGuiWindow* card = FindActiveWindow("##ConsoleEntryCard"); + Vector2 position = Unsafe.BitCast(card->Pos) + new Vector2(230f, 12f); + Click(host, panel, position); + Assert.Equal("panel/diagnostics.console/entry", host.runtime.interactions.focusedArea); + Assert.NotNull(host.runtime.interactions.focusedTarget); + float collapsedHeight = card->Size.Y; + NativeImGui.Raw.AddKeyEvent(NativeImGui.Raw.GetIO(), ImGuiKey.Enter, 1); + host.DrawPanel(panel); + NativeImGui.Raw.AddKeyEvent(NativeImGui.Raw.GetIO(), ImGuiKey.Enter, 0); + host.DrawPanel(panel); + host.DrawPanel(panel); + Assert.True(card->Size.Y > collapsedHeight + 1f); + host.DrawPanel(panel, () => + { + var context = host.runtime.interactions.For(host.runtime.interactions.focusedArea, host.runtime.interactions.focusedTarget); + Assert.True(context.Execute("console/copy-message")); + Assert.Equal("Needle message", Marshal.PtrToStringUTF8((nint)NativeImGui.Raw.GetClipboardText())); + Assert.True(context.Execute("console/copy-details")); + string? details = Marshal.PtrToStringUTF8((nint)NativeImGui.Raw.GetClipboardText()); + Assert.Contains("Needle message", details); + Assert.Contains("(x2)", details); + }); + host.console.Clear(); + host.DrawPanel(panel); + Assert.Null(host.runtime.interactions.focusedTarget); + Assert.Equal(0, CountCards()); + } + + private static void Search( + NativePanelHost host, + EditorPanelExtension panel, + string query + ) { + ImGuiWindow* child = FindActiveWindow("ConsoleChild", "ConsoleRegion"); + Vector2 position = Unsafe.BitCast(child->DC.CursorStartPos) + new Vector2(80f, 10f); + Click(host, panel, position); + ImGuiIO* io = NativeImGui.Raw.GetIO(); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 1); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.A, 1); + host.DrawPanel(panel); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.A, 0); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 0); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Backspace, 1); + host.DrawPanel(panel); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Backspace, 0); + using NativeStringLease text = Utf8Codec.EncodeNullTerminated(query, allowsNull: false); + NativeImGui.Raw.AddInputCharactersUTF8(io, (sbyte*)text.pointer); + host.DrawPanel(panel); + host.DrawPanel(panel); + } + + private static void Click( + NativePanelHost host, + EditorPanelExtension panel, + Vector2 position + ) { + ImGuiIO* io = NativeImGui.Raw.GetIO(); + NativeImGui.Raw.AddMousePosEvent(io, position.X, position.Y); + host.DrawPanel(panel); + NativeImGui.Raw.AddMouseButtonEvent(io, 0, 1); + host.DrawPanel(panel); + NativeImGui.Raw.AddMouseButtonEvent(io, 0, 0); + host.DrawPanel(panel); + } + + private static int CountCards() + { + ImGuiNativeContext* context = NativeImGui.Raw.GetCurrentContext(); + int count = 0; + for (int index = 0; index < context->Windows.Size; index++) + { + ImGuiWindow* window = context->Windows.Data[index]; + if (window->Active != 0 && Marshal.PtrToStringUTF8((nint)window->Name)?.Contains("##ConsoleEntryCard", StringComparison.Ordinal) == true) + count++; + } + return count; + } + + private static ImGuiWindow* FindActiveWindow( + string containing, + string? excluding = null + ) { + ImGuiNativeContext* context = NativeImGui.Raw.GetCurrentContext(); + for (int index = 0; index < context->Windows.Size; index++) + { + ImGuiWindow* window = context->Windows.Data[index]; + string name = Marshal.PtrToStringUTF8((nint)window->Name) ?? string.Empty; + if (window->Active != 0 && name.Contains(containing, StringComparison.Ordinal) + && (excluding is null || !name.Contains(excluding, StringComparison.Ordinal))) + return window; + } + throw new InvalidOperationException("The actual control window was not submitted: " + containing); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleVirtualizationNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleVirtualizationNativeTests.cs new file mode 100644 index 000000000..2314c176b --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ConsoleVirtualizationNativeTests.cs @@ -0,0 +1,139 @@ +using System; +using System.Runtime.InteropServices; +using Inno.Core.Logging; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using Xunit; +using Xunit.Abstractions; +using NativeGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class ConsoleVirtualizationNativeTests(ITestOutputHelper output) +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RetiringTheContextRecordClosesItsPopup(bool clear) + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + host.console.capacity = 16; + var logger = host.logs.CreateLogger(); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + logger.Write(LogLevel.Info, "Original context record"); + host.logs.Flush(); + host.DrawPanel(panel); + host.DrawPanel(panel); + ImGuiNativeContext* context = NativeGui.Raw.GetCurrentContext(); + ImGuiWindow* card = null; + for (int index = 0; index < context->Windows.Size; index++) + { + ImGuiWindow* window = context->Windows.Data[index]; + if (window->Active != 0 && Marshal.PtrToStringUTF8((nint)window->Name)?.Contains("##ConsoleEntryCard", StringComparison.Ordinal) == true) + card = window; + } + Assert.True(card is not null); + ImGuiIO* io = NativeGui.Raw.GetIO(); + NativeGui.Raw.AddMousePosEvent(io, card->Pos.X + 230f, card->Pos.Y + 12f); + host.DrawPanel(panel); + NativeGui.Raw.AddMouseButtonEvent(io, 1, 1); + host.DrawPanel(panel); + NativeGui.Raw.AddMouseButtonEvent(io, 1, 0); + host.DrawPanel(panel); + Assert.True(context->OpenPopupStack.Size > 0); + if (clear) + host.console.Clear(); + else + { + for (int index = 0; index < host.console.capacity; index++) + logger.Write(LogLevel.Info, "Replacement context record " + index); + host.logs.Flush(); + } + Assert.DoesNotContain(host.console.Capture().occurrences, static entry => entry.message == "Original context record"); + for (int frame = 0; frame < 3; frame++) + host.DrawPanel(panel); + Assert.Equal(0, context->OpenPopupStack.Size); + } + + [Fact] + public void RepeatedContextMenusDoNotAccumulateRetiredOccurrenceWindows() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + host.console.capacity = 16; + var logger = host.logs.CreateLogger(); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + ImGuiIO* io = NativeGui.Raw.GetIO(); + for (int iteration = 0; iteration < 100; iteration++) + { + logger.Write(LogLevel.Info, "Context entry " + iteration); + host.logs.Flush(); + host.DrawPanel(panel); + host.DrawPanel(panel); + ImGuiWindow* card = null; + ImGuiNativeContext* context = NativeGui.Raw.GetCurrentContext(); + for (int index = 0; index < context->Windows.Size; index++) + { + ImGuiWindow* window = context->Windows.Data[index]; + if (window->Active != 0 && Marshal.PtrToStringUTF8((nint)window->Name)?.Contains("##ConsoleEntryCard", StringComparison.Ordinal) == true) + card = window; + } + Assert.True(card is not null); + NativeGui.Raw.AddMousePosEvent(io, card->Pos.X + 230f, card->Pos.Y + 12f); + host.DrawPanel(panel); + NativeGui.Raw.AddMouseButtonEvent(io, 1, 1); + host.DrawPanel(panel); + NativeGui.Raw.AddMouseButtonEvent(io, 1, 0); + host.DrawPanel(panel); + Assert.True(context->OpenPopupStack.Size > 0); + NativePanelInput.Key(host, panel, ImGuiKey.Escape); + } + output.WriteLine("Context menus: 100; retained records: " + host.console.Capture().occurrences.Count + + "; native windows: " + NativeGui.Raw.GetCurrentContext()->Windows.Size); + Assert.InRange(NativeGui.Raw.GetCurrentContext()->Windows.Size, 1, 32); + } + + [Fact] + public void TenThousandOccurrencesSubmitOnlyViewportCardsAndKeepKeyboardNavigation() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + host.console.capacity = 10_000; + var logger = host.logs.CreateLogger(); + for (int index = 0; index < 10_000; index++) + { + logger.Write(LogLevel.Info, "Entry " + index.ToString("D5") + "\nDetails / 资源信息"); + if (index % 128 == 0) + host.logs.Flush(); + } + host.logs.Flush(); + Assert.Equal(10_000, host.console.Capture().occurrences.Count); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + for (int frame = 0; frame < 4; frame++) + host.DrawPanel(panel); + Assert.InRange(NativeGui.Raw.GetCurrentContext()->Windows.Size, 1, 128); + NativePanelInput.Focus(host, panel, "ConsoleRegion"); + NativePanelInput.Key(host, panel, ImGuiKey.Home); + NativePanelInput.Key(host, panel, ImGuiKey.Enter); + for (int frame = 0; frame < 3; frame++) + host.DrawPanel(panel); + Assert.InRange(NativeGui.Raw.GetCurrentContext()->Windows.Size, 1, 128); + } + + [Fact] + public void RetiredOccurrencesDoNotAccumulateNativeChildWindows() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Logging"); + host.console.capacity = 16; + var logger = host.logs.CreateLogger(); + EditorPanelExtension panel = host.GetPanel("diagnostics.console"); + for (int iteration = 0; iteration < 150; iteration++) + { + logger.Write(LogLevel.Info, "Retained occurrence " + iteration); + host.logs.Flush(); + host.DrawPanel(panel); + host.DrawPanel(panel); + } + Assert.Equal(16, host.console.Capture().occurrences.Count); + output.WriteLine("Retained records: 16; completed updates: 150; native windows: " + NativeGui.Raw.GetCurrentContext()->Windows.Size); + Assert.InRange(NativeGui.Raw.GetCurrentContext()->Windows.Size, 1, 64); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs new file mode 100644 index 000000000..ebf1982f1 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs @@ -0,0 +1,139 @@ +using System; +using System.Linq; +using System.Numerics; +using Inno.Core.Graphs; +using Inno.Editor.Graph; +using Inno.Editor.Graph.ImGui; +using Inno.Native.ImGui; +using Xunit; +using NativeGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class GraphCanvasNativeTests +{ + [Fact] + public void NonShaderPointerWorkflowUsesTheRealDocumentHistory() + { + using var host = new NativeProjectPanelHost(); + var document = new GraphDocument(); + var first = new GraphNodeRecord(new("workflow.first"), "workflow.step") { position = new(20f, 20f) }; + var second = new GraphNodeRecord(new("workflow.second"), "workflow.step") { position = new(300f, 20f) }; + document.AddNode(first); + document.AddNode(second); + var history = host.runtime.interactions.history; + var controller = host.graphs.OpenDocument(Guid.NewGuid(), document, history); + var state = new GraphCanvasState(); + var pointer = new GraphCanvasPointerController(state); + var layout = new GraphCanvasLayout(); + Vector2 origin = default; + Frame(new(0f, 0f), false); + Frame(new(0f, 0f), false); + Vector2 hit = origin + new Vector2(30f, 25f); + Frame(hit, true); + Frame(hit + new Vector2(50f, 40f), true); + Frame(hit + new Vector2(50f, 40f), false); + Assert.Equal(new GraphPosition(70f, 60f), controller.document.FindNode(first.id)!.position); + Assert.True(history.Undo().succeeded); + Assert.Equal(new GraphPosition(20f, 20f), controller.document.FindNode(first.id)!.position); + Assert.True(history.Redo().succeeded); + Frame(origin + new Vector2(270f, 100f), true); + Frame(origin + new Vector2(300f, 60f), false); + GraphEdgeRecord edge = Assert.Single(controller.document.edges); + Assert.Equal(first.id, edge.output.nodeId); + Assert.Equal(second.id, edge.input.nodeId); + Assert.True(history.Undo().succeeded); + Assert.Empty(controller.document.edges); + Assert.True(history.Redo().succeeded); + Assert.Single(controller.document.edges); + + void Frame( + Vector2 mouse, + bool down + ) { + var io = NativeGui.Raw.GetIO(); + NativeGui.Raw.AddMousePosEvent(io, mouse.X, mouse.Y); + NativeGui.Raw.AddMouseButtonEvent(io, 0, down ? (byte)1 : (byte)0); + host.native.Frame(() => + { + NativeGui.SetNextWindowPos(new Vector2(20f, 20f), (int)ImGuiCond.Always); + NativeGui.SetNextWindowSize(new Vector2(700f, 450f), (int)ImGuiCond.Always); + bool open = true; + _ = NativeGui.Begin("Workflow graph", ref open, (int)(ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoSavedSettings)); + try + { + origin = Ui.GetCursorScreenPosition(); + Vector2 size = Ui.GetContentRegionAvailable(); + _ = Ui.InvisibleButton("workflow-canvas", size); + var view = new GraphCanvasView(state, origin, size); + _ = layout.SetNodes(controller.document.nodes.Select(node => new GraphCanvasNode(node.id, node.position, + new(200f, 100f), 24f, "Workflow step", [ + new(new(node.id, new("previous")), GraphPortDirection.Input, new(0f, 40f), "Previous"), + new(new(node.id, new("next")), GraphPortDirection.Output, new(200f, 40f), "Next") + ]))); + GraphCanvasPointerResult result = pointer.Step(view, layout, new() + { + position = Ui.GetMousePos(), + isHovered = Ui.IsItemHovered(Inno.Editor.ImGui.ImGuiHoveredFlags.AllowWhenBlockedByActiveItem), + isPressed = Ui.IsMouseClicked(Inno.Editor.ImGui.ImGuiMouseButton.Left), + isDown = Ui.IsMouseButtonDown(Inno.Editor.ImGui.ImGuiMouseButton.Left), + isReleased = Ui.IsMouseReleased(Inno.Editor.ImGui.ImGuiMouseButton.Left) + }); + if (result.movedNodes is { } positions) + controller.MoveNodes(positions); + if (result.output is GraphEndpoint output && result.input is GraphEndpoint input) + _ = controller.Connect(output, input); + foreach (GraphCanvasNode node in layout.nodes) + _ = view.DrawNode(node, 0xFF504030, state.selectedNodes.Contains(node.id), + previewPosition: pointer.previewPositions.TryGetValue(node.id, out GraphPosition position) ? position : null); + } + finally + { + NativeGui.End(); + } + }); + } + } + + [Fact] + public void NonShaderNodeDrawingRestoresItsClipAfterAnExtensionThrows() + { + using var host = new NativePanelHost(); + var state = new GraphCanvasState(); + GraphNodeId id = new("workflow.begin"); + var node = new GraphCanvasNode(id, new(10f, 10f), new(200f, 100f), 24f, "Workflow step", + [new(new(id, new("next")), GraphPortDirection.Output, new(200f, 40f), "Continue", labelBounds: (new(0f, 28f), new(200f, 52f)))]); + for (int frame = 0; frame < 3; frame++) + { + host.Frame(() => + { + NativeGui.SetNextWindowPos(new Vector2(20f, 20f), (int)ImGuiCond.Always); + NativeGui.SetNextWindowSize(new Vector2(600f, 400f), (int)ImGuiCond.Always); + bool open = true; + _ = NativeGui.Begin("Non-shader graph", ref open, (int)ImGuiWindowFlags.NoSavedSettings); + try + { + ImGuiWindow* window = NativeGui.Raw.GetCurrentWindow(); + int clipDepth = window->DrawList->ClipRectStack.Size; + var view = new GraphCanvasView(state, new(40f, 60f), new(540f, 300f)); + var failure = new InvalidOperationException("Workflow node extension failed."); + Assert.Same(failure, Assert.Throws(() => view.DrawNode(node, 0xFF504030, true, + () => throw failure))); + Assert.Equal(clipDepth, window->DrawList->ClipRectStack.Size); + Assert.True(view.DrawNode(node, 0xFF504030, false)); + var offscreen = new GraphCanvasNode(new("workflow.hidden"), new(5000f, 5000f), new(200f, 100f), 24f, "Hidden", []); + Assert.False(view.DrawNode(offscreen, 0xFF504030, false, () => throw new InvalidOperationException("Culled content must not run."))); + var outsideWindow = new GraphCanvasNode(new("workflow.clipped-parent"), new(900f, 900f), new(200f, 100f), 24f, "Clipped by parent", []); + var oversized = new GraphCanvasView(state, new(40f, 60f), new(1800f, 1800f)); + Assert.False(oversized.DrawNode(outsideWindow, 0xFF504030, false, () => throw new InvalidOperationException("Parent-clipped content must not run."))); + Assert.Equal(clipDepth, window->DrawList->ClipRectStack.Size); + } + finally + { + NativeGui.End(); + } + }); + } + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj index 4edaa28b8..074500b50 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj +++ b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj @@ -1,5 +1,14 @@ + + + + + + + + + @@ -20,6 +29,7 @@ + diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs new file mode 100644 index 000000000..b9a2876ae --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json; +using Inno.Editor.Interactions; +using Xunit; +using Xunit.Abstractions; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed class LargePanelPerformanceTests(ITestOutputHelper output) +{ + [Fact] + [Trait("Acceptance", "Performance")] + public void TenThousandHierarchyRowsProduceRepeatableFrameMeasurements() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + string? input = InputDirectory("hierarchy"); + if (input is not null && Directory.Exists(input)) + { + CopyDirectory(input, Path.Combine(host.projectRoot, "Assets")); + host.assets.Rescan(); + _ = host.workspace.Open("Untitled Scene.iscene"); + } + else + { + var scene = host.CreateScene(); + for (int index = 0; index < 10_000; index++) + _ = scene.CreateObject("Object " + index.ToString("D5")); + if (input is not null) + { + Assert.Equal("Untitled Scene.iscene", host.workspace.Save(scene, string.Empty)); + CopyDirectory(Path.Combine(host.projectRoot, "Assets"), input); + } + } + Assert.Equal(10_000, host.workspace.activeScene!.GetObjects().Count); + Measure(host, host.native.GetPanel("scene.hierarchy"), "10000-hierarchy-rows"); + } + + [Fact] + [Trait("Acceptance", "Performance")] + public void TenThousandAssetRowsProduceRepeatableFrameMeasurements() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + string? input = InputDirectory("assets"); + if (input is not null && Directory.Exists(input)) + CopyDirectory(input, Path.Combine(host.projectRoot, "Assets")); + else + for (int index = 0; index < 10_000; index++) + Directory.CreateDirectory(Path.Combine(host.projectRoot, "Assets", "Folder " + index.ToString("D5"))); + host.assets.Rescan(); + Assert.Equal(10_000, Directory.EnumerateDirectories(Path.Combine(host.projectRoot, "Assets"), "Folder *").Count()); + if (input is not null && !Directory.Exists(input)) + CopyDirectory(Path.Combine(host.projectRoot, "Assets"), input); + EditorPanelExtension panel = host.native.GetPanel("asset.file-browser"); + Measure(host, panel, "10000-asset-directory-rows"); + } + + private static string? InputDirectory(string workload) + { + string? owner = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_INPUT_ROOT"); + return owner is null ? null : Path.Combine(Path.GetFullPath(owner), workload); + } + + private static void CopyDirectory( + string source, + string destination + ) { + Directory.CreateDirectory(destination); + foreach (string directory in Directory.EnumerateDirectories(source, "*", SearchOption.AllDirectories)) + Directory.CreateDirectory(Path.Combine(destination, Path.GetRelativePath(source, directory))); + foreach (string file in Directory.EnumerateFiles(source, "*", SearchOption.AllDirectories)) + File.Copy(file, Path.Combine(destination, Path.GetRelativePath(source, file)), overwrite: false); + } + + private void Measure( + NativeProjectPanelHost host, + EditorPanelExtension panel, + string workload + ) { + for (int warmup = 0; warmup < 5; warmup++) + host.native.DrawPanel(panel); + string? requested = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_REPETITIONS"); + int repetitions = requested is null ? 5 : int.Parse(requested, System.Globalization.CultureInfo.InvariantCulture); + Assert.InRange(repetitions, 1, 5); + using Process process = Process.GetCurrentProcess(); + var samples = new List(); + for (int repetition = 0; repetition < repetitions; repetition++) + { + var milliseconds = new double[10]; + long bytesBefore = GC.GetAllocatedBytesForCurrentThread(); + long totalBytesBefore = GC.GetTotalAllocatedBytes(precise: true); + TimeSpan cpuBefore = process.TotalProcessorTime; + int collectionsBefore = GC.CollectionCount(0); + int gen1Before = GC.CollectionCount(1); + int gen2Before = GC.CollectionCount(2); + for (int frame = 0; frame < milliseconds.Length; frame++) + { + long started = Stopwatch.GetTimestamp(); + host.native.DrawPanel(panel); + milliseconds[frame] = Stopwatch.GetElapsedTime(started).TotalMilliseconds; + } + long allocated = GC.GetAllocatedBytesForCurrentThread() - bytesBefore; + double[] sorted = milliseconds.Order().ToArray(); + samples.Add(new + { + repetition, milliseconds, median = (sorted[4] + sorted[5]) / 2, + p95 = sorted[9], allocatedBytes = allocated, + totalAllocatedBytes = GC.GetTotalAllocatedBytes(precise: true) - totalBytesBefore, + cpuMilliseconds = (process.TotalProcessorTime - cpuBefore).TotalMilliseconds, + gen0Collections = GC.CollectionCount(0) - collectionsBefore, + gen1Collections = GC.CollectionCount(1) - gen1Before, + gen2Collections = GC.CollectionCount(2) - gen2Before + }); + } + output.WriteLine(JsonSerializer.Serialize(new + { + workload, configuration = "Debug", samples, + framework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, + viewport = "1000x800", warmupFrames = 5, measuredFramesPerRepetition = 10, + sharedInput = InputDirectory(workload.Contains("hierarchy", StringComparison.Ordinal) ? "hierarchy" : "assets"), + consumer = "native-command-offset-validation", supportsVertexOffsets = true + })); + Assert.True(panel.isOpen); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs new file mode 100644 index 000000000..de520992d --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs @@ -0,0 +1,121 @@ +using System; +using System.Linq; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Editor.Assets.ImGui; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using Inno.Scene; +using Xunit; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed class ListPanelKeyboardNativeTests +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + public void BrowserResolvesCurrentSourceEntriesAfterAnUnchangedRescan(bool tree) + { + using var host = new NativeProjectPanelHost(); + host.assets.CreateDirectory(AssetPath.Project("Alpha")); + host.assets.CreateDirectory(AssetPath.Project("Beta")); + host.assets.Rescan(); + EditorPanelExtension panel = host.native.GetPanel("asset.file-browser"); + host.native.DrawPanel(panel); + host.native.DrawPanel(panel); + Assert.True(host.assets.TryGetFileSystemEntry(AssetPath.Project("Alpha"), out AssetFileEntry first)); + host.runtime.interactions.SetSelection(first); + NativePanelInput.Focus(host.native, panel, tree ? "##TreeScroll" : "##EntriesScroll"); + long contentRevision = host.assets.revision; + host.assets.Rescan(); + Assert.Equal(contentRevision, host.assets.revision); + Assert.True(host.assets.TryGetFileSystemEntry(AssetPath.Project("Beta"), out AssetFileEntry current)); + NativePanelInput.Key(host.native, panel, ImGuiKey.End); + Assert.Same(current, host.runtime.interactions.selection.selectedTarget); + } + + [Fact] + public void HierarchyUsesVisibleRangeAndKeepsTextEditingExclusive() + { + using var host = new NativeProjectPanelHost(); + GameScene scene = host.CreateScene(); + GameObject[] objects = Enumerable.Range(0, 12).Select(index => scene.CreateObject("Object " + index.ToString("D2"))).ToArray(); + GameObject child = scene.CreateObject("Collapsed child"); + child.transform.SetParent(objects[10].transform); + EditorPanelExtension panel = host.native.GetPanel("scene.hierarchy"); + host.native.DrawPanel(panel); + host.native.DrawPanel(panel); + EditorInteractions interactions = host.runtime.interactions; + interactions.SetSelection(objects[0]); + NativePanelInput.Focus(host.native, panel, "##HierarchyContent"); + NativePanelInput.Key(host.native, panel, ImGuiKey.DownArrow); + Assert.Same(objects[1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.End, shift: true); + Assert.Equal(11, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.UpArrow, primary: true); + Assert.Equal(11, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.Space, primary: true); + Assert.False(interactions.selection.Contains(objects[10])); + Assert.Equal(10, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.DownArrow); + Assert.Same(objects[^1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.Home); + Assert.Same(scene, interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.PageDown); + Assert.Same(objects[^1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.PageUp); + Assert.Same(scene, interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.End); + NativePanelInput.Key(host.native, panel, ImGuiKey.Enter); + NativePanelInput.Key(host.native, panel, ImGuiKey.UpArrow); + Assert.Same(objects[^1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.Escape); + Assert.Equal("Object 11", objects[^1].name); + Assert.False(interactions.history.canUndo); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void BrowserUsesTheFocusedVisibleOrderAndActivatesFoldersAfterTraversal(bool tree) + { + using var host = new NativeProjectPanelHost(); + AssetFileEntry[] entries = Enumerable.Range(0, 12).Select(index => + { + AssetPath path = AssetPath.Project("Folder " + index.ToString("D2")); + host.assets.CreateDirectory(path); + Assert.True(host.assets.TryGetFileSystemEntry(path, out AssetFileEntry entry)); + return entry; + }).ToArray(); + host.assets.CreateDirectory(AssetPath.Project("Folder 10/Collapsed child")); + EditorPanelExtension panel = host.native.GetPanel("asset.file-browser"); + host.native.DrawPanel(panel); + host.native.DrawPanel(panel); + EditorInteractions interactions = host.runtime.interactions; + for (int index = 0; index < entries.Length; index++) + { + Assert.True(host.assets.TryGetFileSystemEntry(AssetPath.Project("Folder " + index.ToString("D2")), out AssetFileEntry current)); + entries[index] = current; + } + interactions.SetSelection(entries[0]); + NativePanelInput.Focus(host.native, panel, tree ? "##TreeScroll" : "##EntriesScroll"); + NativePanelInput.Key(host.native, panel, ImGuiKey.DownArrow); + Assert.Same(entries[1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.End, shift: true); + Assert.Equal(11, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.UpArrow, primary: true); + Assert.Equal(11, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.Space, primary: true); + Assert.False(interactions.selection.Contains(entries[10])); + Assert.Equal(10, interactions.selection.count); + NativePanelInput.Key(host.native, panel, ImGuiKey.DownArrow); + Assert.Same(entries[^1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.End); + Assert.Same(entries[^1], interactions.selection.selectedTarget); + NativePanelInput.Key(host.native, panel, ImGuiKey.Enter); + Assert.True(interactions.TryGetModule(out var browser)); + Assert.Equal(entries[^1].assetPath.ToString(), browser!.browser.currentDirectory); + Assert.False(interactions.history.canUndo); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativeClipboard.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativeClipboard.cs new file mode 100644 index 000000000..d06247b3b --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativeClipboard.cs @@ -0,0 +1,64 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +internal sealed unsafe class NativeClipboard : IDisposable +{ + private GCHandle m_handle; + private nint m_text; + private ExceptionDispatchInfo? m_failure; + + internal NativeClipboard(ImGuiNativeContext* context) + { + m_handle = GCHandle.Alloc(this); + ImGuiPlatformIO* platform = NativeImGui.Raw.GetPlatformIO(context); + platform->PlatformClipboardUserData = (void*)GCHandle.ToIntPtr(m_handle); + platform->PlatformGetClipboardTextFn = &Read; + platform->PlatformSetClipboardTextFn = &Write; + m_text = Marshal.StringToCoTaskMemUTF8(string.Empty); + } + + internal void ThrowIfFaulted() => m_failure?.Throw(); + + public void Dispose() + { + if (!m_handle.IsAllocated) + return; + Marshal.FreeCoTaskMem(m_text); + m_text = 0; + m_handle.Free(); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static sbyte* Read(ImGuiNativeContext* context) + { + NativeClipboard owner = GetOwner(context); + return (sbyte*)owner.m_text; + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void Write( + ImGuiNativeContext* context, + sbyte* text + ) { + NativeClipboard owner = GetOwner(context); + try + { + nint replacement = Marshal.StringToCoTaskMemUTF8(Marshal.PtrToStringUTF8((nint)text) ?? string.Empty); + Marshal.FreeCoTaskMem(owner.m_text); + owner.m_text = replacement; + } + catch (Exception failure) + { + owner.m_failure ??= ExceptionDispatchInfo.Capture(failure); + } + } + + private static NativeClipboard GetOwner(ImGuiNativeContext* context) + => (NativeClipboard)GCHandle.FromIntPtr((nint)NativeImGui.Raw.GetPlatformIO(context)->PlatformClipboardUserData).Target!; +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs new file mode 100644 index 000000000..4fda4f1c8 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs @@ -0,0 +1,218 @@ +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using Inno.Adapter.Modules.DotNet; +using Inno.Core.Diagnostics; +using Inno.Core.Input; +using Inno.Core.Logging; +using Inno.Editor.Core; +using Inno.Editor.Diagnostics; +using Inno.Editor.ImGui; +using Inno.Editor.Interactions; +using Inno.Extensibility.Modules; +using Inno.Extensibility.Types; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; +using NativeVector2 = Inno.Native.ImGui.ImVec2C; +using ImGuiCond = Inno.Native.ImGui.ImGuiCond; +using ImGuiWindowFlags = Inno.Native.ImGui.ImGuiWindowFlags; + +namespace Inno.Editor.PlayMode.Tests; + +internal sealed unsafe class NativePanelHost : IDisposable +{ + private readonly string m_root = Path.Combine(Path.GetTempPath(), "InnoNativePanels", Guid.NewGuid().ToString("N")); + private readonly ImGuiNativeContext* m_nativeContext; + private readonly NativeClipboard m_clipboard; + private readonly IDisposable? m_diagnosticScope; + private readonly ModuleHost? m_modules; + private readonly TypeCatalog? m_types; + private readonly bool m_ownsServices; + private readonly bool m_validateVertexOffsets; + private float m_time; + + internal NativePanelHost(params string[] assemblies) : this(false, assemblies) { } + + internal NativePanelHost( + bool supportsVertexOffsets, + params string[] assemblies + ) { + m_validateVertexOffsets = supportsVertexOffsets; + m_ownsServices = true; + Directory.CreateDirectory(m_root); + foreach (string assembly in assemblies) + _ = Assembly.Load(assembly); + diagnostics = new DiagnosticHub(); + m_diagnosticScope = diagnostics.EnterScope(); + logs = new LogRouter(); + m_modules = new ModuleHost(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(NativePanelHost).Assembly) }); + m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); + console = new EditorConsole(logs, diagnostics, new InactivePlayMode()); + console.Start(); + runtime = new EditorInteractionRuntime(new EditorContext(m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Super")), m_types, logs, [console]); + runtime.Start(); + ui = new EditorUiSession(); + m_nativeContext = CreateNativeContext(ui, supportsVertexOffsets); + m_clipboard = new NativeClipboard(m_nativeContext); + logs.Flush(); + console.Clear(); + } + + internal NativePanelHost( + EditorInteractionRuntime runtime, + EditorUiSession ui, + EditorConsole console, + LogRouter logs, + DiagnosticHub diagnostics, + bool supportsVertexOffsets = false + ) { + this.runtime = runtime; + this.ui = ui; + this.console = console; + this.logs = logs; + m_validateVertexOffsets = supportsVertexOffsets; + this.diagnostics = diagnostics; + m_root = string.Empty; + m_nativeContext = CreateNativeContext(ui, supportsVertexOffsets); + m_clipboard = new NativeClipboard(m_nativeContext); + } + + internal DiagnosticHub diagnostics { get; } + internal LogRouter logs { get; } + internal EditorConsole console { get; } + internal EditorInteractionRuntime runtime { get; } + internal EditorUiSession ui { get; } + + internal EditorPanelExtension GetPanel(string id) + { + EditorPanelExtension panel = runtime.panels.Single(value => value.id == id); + panel.isOpen = true; + return panel; + } + + internal void Frame( + Action draw, + float deltaTime = 1f / 60f + ) { + m_time += deltaTime; + runtime.Update(new EditorFrame(deltaTime, m_time, true)); + NativeImGui.Raw.GetIO()->DeltaTime = deltaTime; + using IDisposable scope = ui.Enter(); + NativeImGui.NewFrame(); + try + { + draw(); + } + finally + { + NativeImGui.Render(); + if (m_validateVertexOffsets) + ValidateDrawData(); + m_clipboard.ThrowIfFaulted(); + } + } + + internal void DrawPanel( + EditorPanelExtension panel, + Action? afterDraw = null, + float deltaTime = 1f / 60f + ) { + Frame(() => + { + NativeImGui.SetNextWindowPos(new Vector2(20f, 20f), (int)ImGuiCond.Always); + NativeImGui.SetNextWindowSize(new Vector2(1000f, 800f), (int)ImGuiCond.Always); + bool open = true; + _ = NativeImGui.Begin("Panel acceptance", ref open, (int)ImGuiWindowFlags.NoSavedSettings); + try + { + if (panel.isOpen && !panel.Draw(runtime.context)) + { + logs.Flush(); + string failures = string.Join(Environment.NewLine, console.Capture().occurrences + .Where(static entry => entry.level >= LogLevel.Error) + .Select(static entry => entry.message + Environment.NewLine + entry.stackTrace)); + throw new InvalidOperationException("The actual panel was quarantined: " + panel.id + Environment.NewLine + failures); + } + afterDraw?.Invoke(); + } + finally + { + NativeImGui.End(); + } + }, deltaTime); + } + + public void Dispose() + { + if (m_ownsServices) + { + runtime.Dispose(); + console.Dispose(); + } + ui.Dispose(); + if (m_ownsServices) + { + m_types?.Dispose(); + m_modules?.Dispose(); + logs.Dispose(); + m_diagnosticScope?.Dispose(); + } + NativeImGui.Raw.DestroyContext(m_nativeContext); + m_clipboard.Dispose(); + if (m_ownsServices) + Directory.Delete(m_root, recursive: true); + } + + private static ImGuiNativeContext* CreateNativeContext( + EditorUiSession ui, + bool supportsVertexOffsets + ) { + ImGuiNativeContext* context = NativeImGui.Raw.CreateContext(null); + ImGuiIO* io = NativeImGui.Raw.GetIO(); + io->DisplaySize = Unsafe.BitCast(new Vector2(1200f, 900f)); + io->DeltaTime = 1f / 60f; + io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasTextures; + if (supportsVertexOffsets) + io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasVtxOffset; + io->ConfigFlags |= (int)ImGuiConfigFlags.NavEnableKeyboard; + io->Fonts->RendererHasTextures = 1; + io->IniFilename = null; + using (ui.Enter()) + ui.widgets.SetupStyle(); + return context; + } + + private static void ValidateDrawData() + { + ImDrawData* data = NativeImGui.Raw.GetDrawData(); + for (int listIndex = 0; listIndex < data->CmdListsCount; listIndex++) + { + ImDrawList* list = data->CmdLists.Data[listIndex]; + for (int commandIndex = 0; commandIndex < list->CmdBuffer.Size; commandIndex++) + { + ImDrawCmd command = list->CmdBuffer.Data[commandIndex]; + ulong lastIndex = (ulong)command.IdxOffset + command.ElemCount; + if (lastIndex > (ulong)list->IdxBuffer.Size) + throw new InvalidOperationException("Native draw data exceeded its index buffer."); + for (ulong index = command.IdxOffset; index < lastIndex; index++) + if ((ulong)command.VtxOffset + list->IdxBuffer.Data[index] >= (ulong)list->VtxBuffer.Size) + throw new InvalidOperationException("Native draw data exceeded its vertex buffer after applying the command offset."); + } + } + } + + internal sealed class InactivePlayMode : IEditorPlayMode + { + private readonly Inno.Core.Events.EventDispatcher m_events = new(); + public EditorPlayModeState state => EditorPlayModeState.Editing; + public bool isPlaying => false; + public string? lastFailure => null; + public LogSessionId activeSessionId => LogSessionId.none; + public IDisposable ListenStateChanges(Action listener) => m_events.CreateHub(); + public bool EnterPlayMode() => false; + public bool ExitPlayMode() => false; + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelInput.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelInput.cs new file mode 100644 index 000000000..e3e25f95a --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelInput.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.InteropServices; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using NativeImGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +internal static unsafe class NativePanelInput +{ + internal static void Focus( + NativePanelHost host, + EditorPanelExtension panel, + string windowFragment + ) { + host.DrawPanel(panel, () => + { + ImGuiNativeContext* context = NativeImGui.Raw.GetCurrentContext(); + for (int index = 0; index < context->Windows.Size; index++) + { + ImGuiWindow* window = context->Windows.Data[index]; + string? name = Marshal.PtrToStringUTF8((nint)window->Name); + if (window->Active != 0 && name?.Contains(windowFragment, StringComparison.Ordinal) == true) + { + NativeImGui.Raw.SetWindowFocus(window->Name); + return; + } + } + throw new InvalidOperationException("The actual input owner window is absent: " + windowFragment); + }); + } + + internal static void Key( + NativePanelHost host, + EditorPanelExtension panel, + ImGuiKey key, + bool shift = false, + bool primary = false + ) { + ImGuiIO* io = NativeImGui.Raw.GetIO(); + if (shift) + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModShift, 1); + if (primary) + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 1); + NativeImGui.Raw.AddKeyEvent(io, key, 1); + host.DrawPanel(panel); + NativeImGui.Raw.AddKeyEvent(io, key, 0); + if (shift) + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModShift, 0); + if (primary) + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 0); + host.DrawPanel(panel); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs new file mode 100644 index 000000000..c70a8d74c --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; +using System.Reflection; +using Inno.Adapter.Modules.DotNet; +using Inno.Adapter.Serialization.DotNet; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Core.Identity; +using Inno.Core.Input; +using Inno.Core.IO; +using Inno.Core.Logging; +using Inno.Core.Settings; +using Inno.Editor.Core; +using Inno.Editor.Diagnostics; +using Inno.Editor.ImGui; +using Inno.Editor.Graph; +using Inno.Editor.Interactions; +using Inno.Editor.Scene; +using Inno.Plugins.Authoring; +using Inno.Runtime; +using Inno.Scene; +using Xunit; + +namespace Inno.Editor.PlayMode.Tests; + +internal sealed class NativeProjectPanelHost : IDisposable +{ + private readonly string m_root = Path.Combine(Path.GetTempPath(), "InnoNativeProjectPanels", Guid.NewGuid().ToString("N")); + private readonly EngineHost m_engine; + private readonly RuntimeSession m_session; + private readonly IDisposable m_execution; + private readonly EditorConsole m_console; + private readonly ProjectSettingsStore m_settings; + private readonly PluginEnvironment m_plugins; + + internal NativeProjectPanelHost(bool supportsVertexOffsets = false) + { + _ = Assembly.Load("Inno.Editor.Panel.Hierarchy"); + _ = Assembly.Load("Inno.Editor.Panel.FileBrowser"); + _ = Assembly.Load("Inno.Editor.Workbench"); + _ = Assembly.Load("Inno.Scene.Assets"); + string source = Path.Combine(m_root, "Assets"); + string library = Path.Combine(m_root, "Library"); + string pluginRoot = Path.Combine(m_root, "Plugins"); + Directory.CreateDirectory(source); + m_engine = new EngineHostBuilder().UseMetadataSources( + new DotNetAssemblyCatalogSource(typeof(NativeProjectPanelHost).Assembly), + new ReflectionTypeCatalogSource(), + new ReflectionSerializationMetadataSource()).Build(); + m_session = m_engine.CreateSession(new RuntimeSessionOptions + { + kind = RuntimeSessionKind.Edit, + applicationId = "inno.tests.native-panels", + createLogSink = _ => new FileLogSink(Path.Combine(library, "Logs")), + jobExecutionMode = RuntimeJobExecutionMode.SingleThread + }); + m_execution = m_session.EnterExecutionScope(); + var identities = new IdentityAllocator(); + assets = new AssetPipeline(m_engine.modules, m_engine.types, m_engine.serialization, identities, + m_engine.diagnostics, m_engine.logs, AssetPipelineOptions.Create(source, library) with { enableFileSystemWatcher = false }); + m_settings = new ProjectSettingsStore(new FileByteDocumentStore(Path.Combine(m_root, "Settings.Project.inno")), + m_engine.types, m_engine.serialization, new ProjectId("tests.native-panels"), AssetSerializationContext.Create(assets)); + var pluginSources = new PluginSourceService(m_engine.serialization, pluginRoot, library); + m_plugins = new PluginEnvironment(assets, m_settings, m_engine.serialization, pluginRoot, library, pluginSources.Scan(), m_engine.modules.generations); + m_console = new EditorConsole(m_engine.logs, m_engine.diagnostics, new NativePanelHost.InactivePlayMode()); + m_console.Start(); + var ui = new EditorUiSession(); + var reloads = new EditorReloadCoordinator(); + runtime = new EditorInteractionRuntime(new EditorContext(m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Ctrl")), + m_engine.types, m_engine.logs, [m_engine.types, m_engine.serialization, m_session, identities, m_session.identities, assets, m_plugins, m_settings, reloads, ui, m_console]); + runtime.Start(); + _ = runtime.panelCount; + Assert.True(runtime.interactions.TryGetModule(out var probe)); + workspace = probe!.workspace; + graphs = probe.graphs; + edits = probe.edits; + native = new NativePanelHost(runtime, ui, m_console, m_engine.logs, m_engine.diagnostics, supportsVertexOffsets); + } + + internal AssetPipeline assets { get; } + internal string projectRoot => m_root; + internal EditorInteractionRuntime runtime { get; } + internal IEditorSceneWorkspace workspace { get; } + internal GraphEditorModule graphs { get; } + internal SceneEdits edits { get; } + internal NativePanelHost native { get; } + + internal GameScene CreateScene() => m_session.scenes.LoadNewSceneAdditive(); + + public void Dispose() + { + runtime.Dispose(); + native.Dispose(); + m_console.Dispose(); + m_plugins.Dispose(); + assets.Dispose(); + m_settings.Dispose(); + m_execution.Dispose(); + m_session.Dispose(); + m_engine.Dispose(); + Directory.Delete(m_root, recursive: true); + } +} + +[EditorModule("tests.native-project-panels", order: 2000)] +public sealed class NativeProjectPanelProbe( + IEditorSceneWorkspace workspace, + GraphEditorModule graphs, + SceneEdits edits +) : EditorModule { + public IEditorSceneWorkspace workspace { get; } = workspace; + public GraphEditorModule graphs { get; } = graphs; + public SceneEdits edits { get; } = edits; +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/SceneDocumentLifecycleNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/SceneDocumentLifecycleNativeTests.cs new file mode 100644 index 000000000..7d74386d0 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/SceneDocumentLifecycleNativeTests.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using Inno.Editor.Interactions; +using Inno.Scene; +using Xunit; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed class SceneDocumentLifecycleNativeTests +{ + [Fact] + [Trait("Acceptance", "Lifecycle")] + public void OneHundredSavedSceneCyclesRetireLiveScenesAndPreserveSourceAndHistory() + { + using var host = new NativeProjectPanelHost(); + GameScene original = host.CreateScene(); + _ = original.CreateObject("Persistent lifecycle object"); + string path = host.workspace.Save(original, string.Empty); + byte[] source = File.ReadAllBytes(Path.Combine(host.projectRoot, "Assets", path)); + Assert.True(host.edits.CloseScene(original)); + EditorPanelExtension panel = host.native.GetPanel("scene.hierarchy"); + var retired = new List>(); + for (int iteration = 0; iteration < 100; iteration++) + retired.Add(OpenDrawAndClose(host, panel, path)); + Assert.Empty(host.workspace.scenes); + Assert.True(host.runtime.interactions.history.canUndo); + Assert.Equal(source, File.ReadAllBytes(Path.Combine(host.projectRoot, "Assets", path))); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.All(retired, static reference => Assert.False(reference.TryGetTarget(out _))); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static WeakReference OpenDrawAndClose( + NativeProjectPanelHost host, + EditorPanelExtension panel, + string path + ) { + GameScene scene = host.workspace.Open(path); + host.native.DrawPanel(panel); + Assert.True(host.edits.CloseScene(scene)); + host.native.DrawPanel(panel); + Assert.True(scene.isDestroyed); + return new WeakReference(scene); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/StatisticsPanelNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/StatisticsPanelNativeTests.cs new file mode 100644 index 000000000..4fb40b275 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/StatisticsPanelNativeTests.cs @@ -0,0 +1,86 @@ +using System; +using System.Numerics; +using Inno.Editor.Interactions; +using Inno.Native.ImGui; +using Xunit; +using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class StatisticsPanelNativeTests +{ + [Fact] + public void ActualPanelSamplesAtHalfSecondIntervalsAndResetsAfterHiddenFrames() + { + using var host = new NativePanelHost("Inno.Editor.Panel.Stats"); + EditorPanelExtension panel = host.GetPanel("diagnostics.stats"); + Assert.Equal(0, DrawAndCountTrendVertices(host, panel, 0.1f)); + for (int frame = 0; frame < 4; frame++) + Assert.Equal(0, DrawAndCountTrendVertices(host, panel, 0.1f)); + int twoSampleVertices = DrawAndCountTrendVertices(host, panel, 0.1f); + Assert.True(twoSampleVertices > 0); + panel.isOpen = false; + host.DrawPanel(panel, deltaTime: 0.1f); + host.DrawPanel(panel, deltaTime: 0.1f); + panel.isOpen = true; + Assert.Equal(0, DrawAndCountTrendVertices(host, panel, 0.1f)); + } + + [Theory] + [InlineData(0.5f, 120)] + [InlineData(0.6f, 101)] + public void ActualPanelBoundsCapacityAndElapsedSampleWindow( + float deltaTime, + int maximumSamples + ) { + using var host = new NativePanelHost("Inno.Editor.Panel.Stats"); + EditorPanelExtension panel = host.GetPanel("diagnostics.stats"); + Assert.Equal(0, DrawAndCountTrendVertices(host, panel, deltaTime)); + int verticesPerInterval = DrawAndCountTrendVertices(host, panel, deltaTime); + Assert.True(verticesPerInterval > 0); + int maximum = 0; + for (int frame = 0; frame < 360; frame++) + { + int vertices = DrawAndCountTrendVertices(host, panel, deltaTime); + maximum = Math.Max(maximum, vertices); + Assert.InRange(vertices, 0, (maximumSamples - 1) * verticesPerInterval); + } + Assert.True(maximum >= (maximumSamples - 3) * verticesPerInterval); + } + + private static int DrawAndCountTrendVertices( + NativePanelHost host, + EditorPanelExtension panel, + float deltaTime + ) { + int count = 0; + host.Frame(() => + { + NativeImGui.SetNextWindowPos(new(20f, 20f), (int)ImGuiCond.Always); + NativeImGui.SetNextWindowSize(new(1000f, 800f), (int)ImGuiCond.Always); + bool open = true; + _ = NativeImGui.Begin("Statistics acceptance", ref open, (int)ImGuiWindowFlags.NoSavedSettings); + Vector4 color = new(0.93f, 0.17f, 0.73f, 1f); + uint packed = Ui.ToPackedColor(color); + Ui.PushStyleColor(Inno.Editor.ImGui.ImGuiCol.PlotLines, color); + try + { + Assert.True(panel.Draw(host.runtime.context)); + ImDrawList* draw = NativeImGui.Raw.GetWindowDrawList(); + for (int index = 0; index < draw->VtxBuffer.Size; index++) + { + uint vertex = draw->VtxBuffer.Data[index].Col; + if ((vertex & 0x00ffffff) == (packed & 0x00ffffff) && (vertex >> 24) != 0) + count++; + } + } + finally + { + Ui.PopStyleColor(); + NativeImGui.End(); + } + }, deltaTime); + return count; + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs new file mode 100644 index 000000000..3d6ee9222 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs @@ -0,0 +1,94 @@ +using System; +using System.Numerics; +using Inno.Editor.ImGui.ImGuiWidget; +using Inno.Native.ImGui; +using Xunit; +using NativeImGui = Inno.Native.ImGui.ImGui; +using Ui = Inno.Editor.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class TreeKeyboardOwnershipNativeTests +{ + [Fact] + public void ListActivationDoesNotToggleDisclosureOrStealAnOverlayButton() + { + using var host = new NativePanelHost(); + bool rowFocus = true; + bool overlayFocus = false; + bool open = false; + int toggles = 0; + int overlayPresses = 0; + bool initialize = true; + uint overlayId = 0; + string overlayState = string.Empty; + ListNavigationResult navigation = default; + Action draw = () => + { + NativeImGui.SetNextWindowSize(new Vector2(600f, 400f), (int)ImGuiCond.Always); + bool visible = true; + _ = NativeImGui.Begin("Tree keyboard owner", ref visible, (int)ImGuiWindowFlags.NoSavedSettings); + try + { + if (initialize) + { + host.ui.widgets.SetNextTreeNodeOpen(true); + initialize = false; + } + TreeNodeResult row = host.ui.widgets.TreeNode("keyboard-row", _ => Ui.Text("A domain-independent row"), new() + { + requestFocus = rowFocus, + drawViewportOverlay = () => + { + NativeImGui.SameLine(0f, -1f); + if (overlayFocus) + Ui.FocusNextItem(); + if (NativeImGui.SmallButton("Overlay")) + overlayPresses++; + overlayId = NativeImGui.Raw.GetCurrentContext()->LastItemData.ID; + ImGuiNativeContext* state = NativeImGui.Raw.GetCurrentContext(); + overlayState = $"id={overlayId:X}, nav={state->NavId:X}, active={state->ActiveId:X}, activate={state->NavActivateId:X}, pressed={state->NavActivatePressedId:X}, keyOwner={NativeImGui.GetKeyOwner(ImGuiKey.Space):X}"; + } + }); + open = row.isOpen; + if (open) + NativeImGui.TreePop(); + navigation = host.ui.widgets.NavigateList(host.runtime.interactions, 1, 0); + if (navigation.toggle) + toggles++; + } + finally + { + NativeImGui.End(); + } + }; + host.Frame(draw); + host.Frame(draw); + host.Frame(draw); + rowFocus = false; + ImGuiIO* io = NativeImGui.Raw.GetIO(); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 1); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 1); + host.Frame(draw); + Assert.True(open); + Assert.Equal(1, toggles); + Assert.Equal(0, overlayPresses); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 0); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 0); + host.Frame(draw); + overlayFocus = true; + host.Frame(draw); + overlayFocus = false; + host.Frame(draw); + Assert.Equal(overlayId, NativeImGui.Raw.GetCurrentContext()->NavId); + // Programmatic focus does not enter native keyboard navigation mode by itself. + NativeImGui.SetNavCursorVisible(true); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 1); + host.Frame(draw); + NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 0); + host.Frame(draw); + Assert.True(overlayPresses == 1, overlayState); + Assert.False(navigation.selectionRequested); + Assert.True(open); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/VisualScaleMatrixTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/VisualScaleMatrixTests.cs new file mode 100644 index 000000000..68aa168e8 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/VisualScaleMatrixTests.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using Inno.Editor.ImGui; +using Inno.Native.ImGui; +using Xunit; +using NativeGui = Inno.Native.ImGui.ImGui; +using NativeVector = Inno.Native.ImGui.ImVec2C; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class VisualScaleMatrixTests +{ + public static IEnumerable Metrics() + { + foreach (float zoom in new[] { 0.75f, 0.85f, 0.90f, 1f, 1.10f, 1.25f, 1.50f }) + foreach (float density in new[] { 1f, 1.25f, 1.50f, 2f }) + foreach (Vector2 size in new[] { new Vector2(1280f, 720f), new Vector2(1920f, 1080f) }) + yield return [zoom, density, size.X, size.Y]; + } + + [Theory] + [MemberData(nameof(Metrics))] + [Trait("Acceptance", "Visual")] + public void NativeFormGeometryRemainsBalancedAcrossSimulatedDisplayMetrics( + float zoom, + float density, + float width, + float height + ) { + using var host = new NativePanelHost(); + ImGuiIO* io = NativeGui.Raw.GetIO(); + io->DisplaySize = Unsafe.BitCast(new(width, height)); + io->DisplayFramebufferScale = Unsafe.BitCast(new(density)); + host.ui.widgets.style.SetZoom(zoom); + float labelWidth = 0f; + float valueWidth = 0f; + for (int frame = 0; frame < 4; frame++) + { + host.Frame(() => + { + NativeGui.SetNextWindowPos(new Vector2(20f, 20f), (int)Inno.Editor.ImGui.ImGuiCond.Always); + NativeGui.SetNextWindowSize(new Vector2(320f, 500f), (int)Inno.Editor.ImGui.ImGuiCond.Always); + bool open = true; + _ = NativeGui.Begin("Narrow inspector matrix", ref open, (int)Inno.Editor.ImGui.ImGuiWindowFlags.NoSavedSettings); + try + { + var widgets = host.ui.widgets; + widgets.PropertyRow("reference", () => + { + labelWidth = NativeGui.GetContentRegionAvail().X; + widgets.PropertyLabel("Resource reference / 资源引用 / Missing"); + }, () => + { + valueWidth = NativeGui.GetContentRegionAvail().X; + string value = "Unassigned / 未指定"; + widgets.Disabled(true, () => NativeGui.TextUnformatted(value)); + }); + widgets.MixedValue(true, () => Assert.True(widgets.isMixedValue)); + Assert.False(widgets.isMixedValue); + Assert.Equal(zoom, widgets.style.zoom); + Assert.Equal(new Vector2(density), Unsafe.BitCast(io->DisplayFramebufferScale)); + Assert.InRange(NativeGui.GetScrollMaxX(), 0f, 1f); + } + finally + { + NativeGui.End(); + } + }); + } + Assert.InRange(labelWidth / (labelWidth + valueWidth), 0.37f, 0.42f); + Assert.True(float.IsFinite(labelWidth) && float.IsFinite(valueWidth)); + } +} diff --git a/tests/fixtures/editor-ui/PanelLifecycle.editor.cs b/tests/fixtures/editor-ui/PanelLifecycle.editor.cs new file mode 100644 index 000000000..920b20a06 --- /dev/null +++ b/tests/fixtures/editor-ui/PanelLifecycle.editor.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Linq; +using InnoEditor.Core; +using InnoEditor.Interactions; + +namespace EditorUiAcceptance; + +/// +/// Exercises the normal product panel protocol from an ordinary source Editor extension. +/// +[EditorModule("tests.editor-ui.panel-lifecycle", order: 10_000)] +public sealed class PanelLifecycle(EditorInteractions interactions) : EditorModule +{ + private string[] m_panels = []; + private bool[] m_originalOpenStates = []; + private int m_frame; + private int m_cycles; + private bool m_completed; + + /// + protected override void OnUpdate(EditorContext context) + { + if (m_completed) + return; + if (m_panels.Length == 0) + { + m_panels = interactions.GetPanelDeclarations().Select(static panel => panel.id).Order(StringComparer.Ordinal).ToArray(); + if (m_panels.Length < 8) + throw new InvalidOperationException("The complete Editor product must expose its eight standard panels."); + m_originalOpenStates = new bool[m_panels.Length]; + for (int index = 0; index < m_panels.Length; index++) + { + string panel = m_panels[index]; + if (!interactions.TryGetPanelState(panel, out m_originalOpenStates[index]) || !interactions.ClosePanel(panel)) + throw new InvalidOperationException("The product could not close panel '" + panel + "'."); + } + return; + } + bool expectedOpen = (m_frame++ & 1) != 0; + foreach (string panel in m_panels) + { + if (!interactions.TryGetPanelState(panel, out bool isOpen) || isOpen != expectedOpen) + throw new InvalidOperationException("Panel state or native drawing failed for '" + panel + "' at cycle " + m_cycles + "."); + } + if (expectedOpen) + m_cycles++; + if (m_cycles == 100 && !expectedOpen) + { + for (int index = 0; index < m_panels.Length; index++) + { + bool restored = m_originalOpenStates[index] ? interactions.OpenPanel(m_panels[index]) : interactions.ClosePanel(m_panels[index]); + if (!restored) + throw new InvalidOperationException("The product could not restore panel '" + m_panels[index] + "'."); + } + string directory = Path.Combine(context.projectDirectory, "Library", "Acceptance"); + Directory.CreateDirectory(directory); + File.WriteAllLines(Path.Combine(directory, "panel-lifecycle.tsv"), + new[] { "panel\tcompleted-open-close-cycles", "protocol\tpublic EditorInteractions / actual product drawing" } + .Concat(m_panels.Select(static panel => panel + "\t100"))); + m_completed = true; + return; + } + foreach (string panel in m_panels) + { + bool changed = expectedOpen ? interactions.ClosePanel(panel) : interactions.OpenPanel(panel); + if (!changed) + throw new InvalidOperationException("The panel protocol rejected '" + panel + "'."); + } + } +} diff --git a/tests/scene/Inno.Scene.Tests/SceneFacadeTests.cs b/tests/scene/Inno.Scene.Tests/SceneFacadeTests.cs index 0a73e8915..99b515c6c 100644 --- a/tests/scene/Inno.Scene.Tests/SceneFacadeTests.cs +++ b/tests/scene/Inno.Scene.Tests/SceneFacadeTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Inno.Scene; @@ -12,6 +13,89 @@ namespace Inno.Scene.Tests; [Collection(SceneTestsCollection.NAME)] public sealed class SceneFacadeTests : IDisposable { + [Fact] + public void RootSiblingPositionsStayAuthoritativeAcrossHierarchyMutations() + { + var scene = new GameScene("Indexed Roots"); + var destination = new GameScene("Transfer Target"); + SceneManager.LoadSceneAdditive(scene); + SceneManager.LoadSceneAdditive(destination); + Transform destinationRoot = destination.CreateObject("Existing").transform; + var roots = new List(); + for (int index = 0; index < 64; index++) + roots.Add(scene.CreateObject("Root " + index).transform); + var random = new Random(27102026); + for (int iteration = 0; iteration < 200; iteration++) + { + ulong before = scene.hierarchyRevision; + int selectedIndex = random.Next(roots.Count); + Transform selected = roots[selectedIndex]; + switch (iteration % 5) + { + case 0: + int target = random.Next(roots.Count); + selected.SetSiblingIndex(target); + roots.RemoveAt(selectedIndex); + roots.Insert(target, selected); + break; + case 1: + Transform parent = roots[(selectedIndex + 1) % roots.Count]; + selected.SetParent(parent); + roots.RemoveAt(selectedIndex); + Assert.Equal(0, selected.siblingIndex); + Verify(); + selected.SetParent(null); + roots.Add(selected); + break; + case 2: + SceneManager.MoveGameObjectToScene(selected.gameObject, destination); + roots.RemoveAt(selectedIndex); + Assert.Equal(0, destinationRoot.siblingIndex); + Assert.Equal(1, selected.siblingIndex); + Verify(); + SceneManager.MoveGameObjectToScene(selected.gameObject, scene); + roots.Add(selected); + break; + case 3: + Assert.True(scene.DestroyObject(selected.gameObject)); + roots.RemoveAt(selectedIndex); + roots.Add(scene.CreateObject("Replacement " + iteration).transform); + break; + default: + Assert.Throws(() => selected.SetParent(selected)); + Assert.Throws(() => selected.SetParent(destinationRoot)); + Assert.Equal(before, scene.hierarchyRevision); + break; + } + Verify(); + } + + void Verify() + { + for (int index = 0; index < roots.Count; index++) + Assert.Equal(index, roots[index].siblingIndex); + } + } + + [Fact] + public void ChildSiblingReorderInvalidatesTheSameSceneHierarchyView() + { + var scene = new GameScene("Child Order"); + Transform parent = scene.CreateObject("Parent").transform; + Transform first = scene.CreateObject("First").transform; + Transform second = scene.CreateObject("Second").transform; + first.SetParent(parent); + second.SetParent(parent); + ulong before = scene.hierarchyRevision; + second.SetSiblingIndex(0); + Assert.True(scene.hierarchyRevision > before); + Assert.Equal(0, second.siblingIndex); + Assert.Equal(1, first.siblingIndex); + before = scene.hierarchyRevision; + second.SetSiblingIndex(0); + Assert.Equal(before, scene.hierarchyRevision); + } + private readonly IDisposable m_sceneScope; public SceneFacadeTests(SceneTestsFixture fixture) diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs index 28ae60a8e..a887cd543 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/EditorStyleMetricsTests.cs @@ -26,6 +26,24 @@ namespace Inno.Editor.Scripting.Tests; public sealed unsafe class EditorStyleMetricsTests { + [Fact] + public void BrowserLayoutCapturesTheSharedScaleWithoutRetainingMutableMetrics() + { + var metrics = new EditorStyleMetrics(); + var browser = new Inno.Editor.Assets.ImGui.AssetBrowserStyle(metrics); + Assert.Equal(32f, browser.gridMinimumCellSize); + metrics.SetZoom(1.5f); + Assert.Equal(32f, browser.gridMinimumCellSize); + var scaled = new Inno.Editor.Assets.ImGui.AssetBrowserStyle(metrics); + Assert.Equal(48f, scaled.gridMinimumCellSize); + Assert.Equal(browser.gridDefaultScale, scaled.gridDefaultScale); + Assert.Equal(browser.listNameSeparatorPosition, scaled.listNameSeparatorPosition); + Assert.Equal(32f, default(Inno.Editor.Assets.ImGui.AssetBrowserStyle).gridMinimumCellSize); + Assert.Equal(7.5f, metrics.toolbarSectionSpacing); + Assert.Equal(24f, metrics.paneMinimumVisibleWidth); + Assert.Throws(() => new Inno.Editor.Assets.ImGui.AssetBrowserStyle(null!)); + } + [Fact] public void ZoomScalesDimensionsAndFontWhilePreservingRatios() { @@ -37,7 +55,7 @@ public void ZoomScalesDimensionsAndFontWhilePreservingRatios() Assert.Equal(3f, metrics.frameRounding, 3); Assert.Equal(1.8f, metrics.fontScale, 3); Assert.Equal(0.4f, metrics.propertyLabelRatio, 3); - Assert.Equal(3f, metrics.assetGridDefaultScale, 3); + Assert.Equal(3f, new Inno.Editor.Assets.ImGui.AssetBrowserStyle(metrics).gridDefaultScale, 3); Assert.Equal(new Vector2(7.5f, 4.5f), metrics.cellPadding); } @@ -898,14 +916,14 @@ bool assertGeometry { contentCursor = NativeImGui.GetCursorScreenPos(); NativeImGui.TextUnformatted("Scene_With_A_Name_That_Is_Intentionally_Wider_Than_The_Tree_Viewport"); - }, new TreeNodeOptions { isLeaf = true, showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow }); + }, new TreeNodeOptions { isLeaf = true, showBackground = true, backgroundColor = EditorPalette.title }); if (assertGeometry) { ImGuiWindow* window = NativeImGui.Raw.GetCurrentWindow(); Assert.True(NativeImGui.GetScrollX() > 0f); Assert.True((window->ScrollbarX != 0)); Assert.Equal(row.contentMin.X, contentCursor.X, 3); - AssertCurrentTreeBackgroundSpansViewport(window, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow)); + AssertCurrentTreeBackgroundSpansViewport(window, NativeImGui.ColorConvertFloat4ToU32(EditorPalette.title)); } } @@ -981,7 +999,7 @@ Vector2 size TreeNodeResult root = EditorUI.current.widgets.TreeNode( "current_background_root", static _ => NativeImGui.TextUnformatted("TestScene"), - new TreeNodeOptions { showBackground = true, backgroundColor = EditorPalette.hierarchySceneRow, suppressHoverHighlight = true } + new TreeNodeOptions { showBackground = true, backgroundColor = EditorPalette.title, suppressHoverHighlight = true } ); if (root.isOpen) { @@ -993,7 +1011,7 @@ Vector2 size NativeImGui.TreePop(); } - AssertCurrentDrawListContainsColor(NativeImGui.ColorConvertFloat4ToU32(EditorPalette.hierarchySceneRow), "The current frame did not submit the Scene row background."); + AssertCurrentDrawListContainsColor(NativeImGui.ColorConvertFloat4ToU32(EditorPalette.title), "The current frame did not submit the Scene row background."); AssertCurrentDrawListContainsColor(NativeImGui.GetColorU32((int)(ImGuiCol.Header)), "The current frame did not submit the selected row highlight."); NativeImGui.End(); NativeImGui.Render(); @@ -1117,14 +1135,14 @@ private static void DrawInspectorCardBody(bool assertHorizontalRange) Assert.Equal(NativeImGui.GetWindowPos() + EditorUI.current.widgets.style.windowPadding, NativeImGui.GetCursorScreenPos()); } - EditorUI.current.widgets.LabelChip("Tag", EditorPalette.inspectorTagLabel); + EditorUI.current.widgets.LabelChip("Tag", new Vector4(0.34f, 0.29f, 0.44f, 1f)); float tagMaximumX = NativeImGui.GetItemRectMax().X; NativeImGui.SameLine(0f, 0f); NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); if (assertHorizontalRange) Assert.Equal(tagMaximumX, NativeImGui.GetItemRectMin().X, 3); - NativeImGui.SameLine(0f, EditorUI.current.widgets.style.inspectorHeaderSectionSpacing); - EditorUI.current.widgets.LabelChip("Layer", EditorPalette.inspectorLayerLabel); + NativeImGui.SameLine(0f, EditorUI.current.widgets.style.headerSectionSpacing); + EditorUI.current.widgets.LabelChip("Layer", new Vector4(0.24f, 0.34f, 0.40f, 1f)); float layerMaximumX = NativeImGui.GetItemRectMax().X; NativeImGui.SameLine(0f, 0f); NativeImGui.Dummy(new Vector2(80f, NativeImGui.GetFrameHeight())); diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj b/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj index 9a7a1c7da..17c8dd369 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj +++ b/tests/scripting/Inno.Editor.Scripting.Tests/Inno.Editor.Scripting.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs index 26696ff7b..959f91fad 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/InspectorPresentationLayoutTests.cs @@ -13,6 +13,7 @@ using Xunit; namespace Inno.Editor.Scripting.Tests; +[Trait("Acceptance", "Visual")] public sealed unsafe class InspectorPresentationLayoutTests : IDisposable { private readonly ImGuiNativeContext* m_context = NativeImGui.Raw.CreateContext(null); diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs index e8930ad37..7f0b72183 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs @@ -2336,7 +2336,15 @@ options with { AssetSerializationContext.Create(assets) ); m_plugins = new PluginEnvironment(assets, m_settings, host.serialization, pluginRoot, libraryRoot, scan, host.modules.generations); - compiler = new ScriptCompiler(new ScriptCompilerOptions { projectRootDirectory = projectRoot }, assets, m_plugins); + string dotnetHost = Path.Combine( + Path.GetFullPath("../../..", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + compiler = new ScriptCompiler( + new ScriptCompilerOptions + { + projectRootDirectory = projectRoot, + compilationEnvironment = new Inno.Build.SdkScriptCompilationEnvironment(dotnetHost, projectRoot) + }, assets, m_plugins); } internal string projectRoot { get; } diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs new file mode 100644 index 000000000..44931447f --- /dev/null +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography; +using System.Text.Json; +using Inno.Assets; +using Inno.Assets.Pipeline; +using Inno.Core.Graphs; +using Inno.Core.Serialization; +using Inno.Native.ImGui; +using Inno.Rendering.Shaders; +using Xunit; +using NativeGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.Scripting.Tests; + +public sealed partial class ShaderEditorWorkflowTests +{ + [Fact] + [Trait("Acceptance", "Performance")] + public unsafe void TwoThousandShaderNodesProduceRepeatableFrameMeasurements() + { + NativeGui.Raw.GetIO()->BackendFlags |= (int)ImGuiBackendFlags.RendererHasVtxOffset; + string source = Path.Combine(m_root, "Assets"); + string? inputRoot = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_INPUT_ROOT"); + string? input = inputRoot is null ? null : Path.Combine(Path.GetFullPath(inputRoot), "shader-graph"); + const string name = "Performance.ishader"; + if (input is not null && Directory.Exists(input)) + CopyPerformanceInput(input, source); + else + { + GraphDocument graph = ShaderGraphTemplates.CreateRaster(m_serialization, AssetSerializationContext.Create(m_assets)); + int remaining = 2_000 - graph.nodes.Count; + for (int index = 0; index < remaining; index++) + { + bool constant = index % 2 == 0; + var node = new GraphNodeRecord(new("performance.node." + index.ToString("D4")), + constant ? "inno.shader.constant" : "inno.shader.construct") + { + position = new(index % 20 * 310f, index / 20 * 180f) + }; + if (!constant) + node.SetValue("type", ShaderGraphDocument.Encode("float3", m_serialization, SerializationContext.empty)); + graph.AddNode(node); + if (!constant) + graph.AddEdge(new(new("performance.edge." + index.ToString("D4")), + new(new("performance.node." + (index - 1).ToString("D4")), new("value")), + new(node.id, new("component.0")))); + } + File.WriteAllBytes(Path.Combine(source, name), GraphDocumentCodec.Encode(graph, m_serialization)); + } + Assert.True(m_assets.Import(AssetPath.Project(name))); + Assert.True(m_assets.TryGetFileSystemEntry(AssetPath.Project(name), out AssetFileEntry entry)); + if (input is not null && !Directory.Exists(input)) + CopyPerformanceInput(source, input); + byte[] original = SHA256.HashData(File.ReadAllBytes(Path.Combine(source, name))); + SelectAndDraw(entry); + var controller = Controller(entry); + Assert.Equal(2_000, controller.document.nodes.Count); + Assert.True(controller.document.edges.Count >= 900); + for (int warmup = 0; warmup < 5; warmup++) + DrawPerformanceCanvas(); + string? requested = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_REPETITIONS"); + int repetitions = requested is null ? 5 : int.Parse(requested, System.Globalization.CultureInfo.InvariantCulture); + Assert.InRange(repetitions, 1, 5); + using Process process = Process.GetCurrentProcess(); + var samples = new List(); + for (int repetition = 0; repetition < repetitions; repetition++) + { + var milliseconds = new double[10]; + var vertices = new int[10]; + var indices = new int[10]; + long bytesBefore = GC.GetAllocatedBytesForCurrentThread(); + long totalBytesBefore = GC.GetTotalAllocatedBytes(precise: true); + TimeSpan cpuBefore = process.TotalProcessorTime; + int gen0 = GC.CollectionCount(0); + int gen1 = GC.CollectionCount(1); + int gen2 = GC.CollectionCount(2); + for (int frame = 0; frame < milliseconds.Length; frame++) + { + long start = Stopwatch.GetTimestamp(); + DrawPerformanceCanvas(); + milliseconds[frame] = Stopwatch.GetElapsedTime(start).TotalMilliseconds; + vertices[frame] = NativeGui.Raw.GetDrawData()->TotalVtxCount; + indices[frame] = NativeGui.Raw.GetDrawData()->TotalIdxCount; + } + long allocated = GC.GetAllocatedBytesForCurrentThread() - bytesBefore; + double[] sorted = milliseconds.Order().ToArray(); + samples.Add(new + { + repetition, milliseconds, vertices, indices, median = (sorted[4] + sorted[5]) / 2, + p95 = sorted[9], allocatedBytes = allocated, + totalAllocatedBytes = GC.GetTotalAllocatedBytes(precise: true) - totalBytesBefore, + cpuMilliseconds = (process.TotalProcessorTime - cpuBefore).TotalMilliseconds, + gen0Collections = GC.CollectionCount(0) - gen0, + gen1Collections = GC.CollectionCount(1) - gen1, + gen2Collections = GC.CollectionCount(2) - gen2 + }); + } + m_performanceOutput.WriteLine(JsonSerializer.Serialize(new + { + workload = "2000-shader-nodes", nodeCount = controller.document.nodes.Count, + edgeCount = controller.document.edges.Count, configuration = "Debug", samples, + framework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, + viewport = "1200x800", warmupFrames = 5, measuredFramesPerRepetition = 10, + sharedInput = input, consumer = "native-command-offset-validation", supportsVertexOffsets = true + })); + Assert.Equal(original, SHA256.HashData(File.ReadAllBytes(Path.Combine(source, name)))); + } + + private unsafe void DrawPerformanceCanvas() + { + Draw(new Vector2(1200f, 800f)); + ImDrawData* data = NativeGui.Raw.GetDrawData(); + for (int listIndex = 0; listIndex < data->CmdListsCount; listIndex++) + { + ImDrawList* list = data->CmdLists.Data[listIndex]; + for (int commandIndex = 0; commandIndex < list->CmdBuffer.Size; commandIndex++) + { + ImDrawCmd command = list->CmdBuffer.Data[commandIndex]; + ulong end = (ulong)command.IdxOffset + command.ElemCount; + if (end > (ulong)list->IdxBuffer.Size) + throw new InvalidOperationException("The shader canvas exceeded its native index buffer."); + for (ulong index = command.IdxOffset; index < end; index++) + if ((ulong)command.VtxOffset + list->IdxBuffer.Data[index] >= (ulong)list->VtxBuffer.Size) + throw new InvalidOperationException("The shader canvas exceeded its native vertex buffer."); + } + } + } + + private static void CopyPerformanceInput( + string source, + string destination + ) { + Directory.CreateDirectory(destination); + foreach (string directory in Directory.EnumerateDirectories(source, "*", SearchOption.AllDirectories)) + Directory.CreateDirectory(Path.Combine(destination, Path.GetRelativePath(source, directory))); + foreach (string file in Directory.EnumerateFiles(source, "*", SearchOption.AllDirectories)) + File.Copy(file, Path.Combine(destination, Path.GetRelativePath(source, file)), overwrite: false); + } +} diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs index 5c822cb17..af97f83e6 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.cs @@ -39,11 +39,14 @@ using Inno.Native.ImGui; using ImGuiApi = Inno.Native.ImGui.ImGui; using Xunit; +using Xunit.Abstractions; using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Scripting.Tests; -public sealed class ShaderEditorWorkflowTests : IDisposable +public sealed partial class ShaderEditorWorkflowTests : IDisposable { + private readonly ITestOutputHelper m_performanceOutput; + private readonly EditorUI m_ui = new(); private readonly string m_root = Path.Combine(Path.GetTempPath(), "InnoShaderEditorWorkflow", Guid.NewGuid().ToString("N")); private readonly IdentityAllocator m_identities = new(); @@ -65,8 +68,9 @@ public sealed class ShaderEditorWorkflowTests : IDisposable private Vector2 m_canvasMinimum; private Vector2 m_canvasMaximum; private Vector2 m_probeOffset; - public unsafe ShaderEditorWorkflowTests() + public unsafe ShaderEditorWorkflowTests(ITestOutputHelper output) { + m_performanceOutput = output; Directory.CreateDirectory(Path.Combine(m_root, "Assets")); m_logs.RegisterSink(m_messages); m_identityScope = m_identities.EnterScope(); @@ -274,6 +278,7 @@ public void FileSelectionOpensOnlyTheShaderCanvasAndNonShaderSelectionKeepsItsDo [InlineData(1.25f, true)] [InlineData(1.5f, true)] [InlineData(2f, true)] + [Trait("Acceptance", "Visual")] public unsafe void NativeCanvasResizeCompletesLayoutAtFractionalUiScales( float scale, bool emptyGraph @@ -415,6 +420,15 @@ public void FormatPlacesOutputsOnTheRightWithoutChangingShaderSemanticsAndIsUndo } private unsafe void DragProbe(Vector2 distance) + { + BeginDragProbe(distance); + ImGuiApi.Raw.AddMouseButtonEvent(ImGuiApi.Raw.GetIO(), 0, 0); + Draw(); + Tick(); + m_probeOffset += distance; + } + + private unsafe void BeginDragProbe(Vector2 distance) { // A single framed summary-only node is centered in the canvas (82 px tall). Vector2 start = (m_canvasMinimum + m_canvasMaximum) * 0.5f + new Vector2(0, -25) + m_probeOffset; @@ -425,10 +439,56 @@ private unsafe void DragProbe(Vector2 distance) Draw(); ImGuiApi.Raw.AddMousePosEvent(ImGuiApi.Raw.GetIO(), start.X + distance.X, start.Y + distance.Y); Draw(); - ImGuiApi.Raw.AddMouseButtonEvent(ImGuiApi.Raw.GetIO(), 0, (false ? (byte)1 : (byte)0)); + } + + [Fact] + public unsafe void SaveDuringANativeDragCompletesOneDomainEditBeforeWriting() + { + AssetFileEntry entry = Create("SaveDrag.ishader"); + SelectAndDraw(entry); + GraphDocumentController controller = Controller(entry); + var graph = new GraphDocument(); + var node = new GraphNodeRecord(new("probe"), "tests.shader-ui-probe") { position = new(0f, 0f) }; + graph.AddNode(node); + controller.ReplaceDocument(graph, "Prepare Save Probe"); + Assert.True(m_runtime.interactions.For("panel/rendering.shader-editor", entry).Execute("shader/focus")); Draw(); - Tick(); - m_probeOffset += distance; + Draw(); + GraphPosition original = controller.document.FindNode(node.id)!.position; + BeginDragProbe(new(52f, 37f)); + Assert.Equal(original, controller.document.FindNode(node.id)!.position); + EditorDocumentContext document = Assert.Single(m_runtime.interactions.documents.documents); + Assert.True(m_runtime.interactions.documents.Save(document.documentId)); + GraphPosition saved = controller.document.FindNode(node.id)!.position; + Assert.NotEqual(original, saved); + Assert.Equal(saved, ReadPosition(entry, node.id)); + ImGuiApi.Raw.AddMouseButtonEvent(ImGuiApi.Raw.GetIO(), 0, 0); + Draw(); + Assert.Equal(saved, controller.document.FindNode(node.id)!.position); + Assert.True(m_runtime.interactions.history.Undo().succeeded); + Assert.Equal(original, controller.document.FindNode(node.id)!.position); + Assert.Equal(saved, ReadPosition(entry, node.id)); + } + + [Fact] + [Trait("Acceptance", "Lifecycle")] + public void OneHundredActualShaderDocumentCyclesRetireTheirGraphOwners() + { + AssetFileEntry entry = Create("Lifecycle.ishader"); + byte[] source = File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath)); + for (int iteration = 0; iteration < 100; iteration++) + { + SelectAndDraw(entry); + GraphDocumentController controller = Controller(entry); + controller.AddNode("missing.lifecycle", new(iteration, iteration)); + Tick(); + EditorDocumentContext document = Assert.Single(m_runtime.interactions.documents.documents); + Assert.True(m_runtime.interactions.documents.Close(document.documentId, EditorDocumentCloseMode.Discard)); + Assert.Empty(m_runtime.interactions.documents.documents); + Assert.False(controller.isAvailable); + Assert.False(m_sink.graphs!.TryOpenDocument(AssetId(entry), m_runtime.interactions.history, out _)); + } + Assert.Equal(source, File.ReadAllBytes(Path.Combine(m_root, "Assets", entry.assetPath.localPath))); } [Fact] diff --git a/tests/scripting/Inno.Scripting.Compiler.Tests/CSharp14ScriptingTests.cs b/tests/scripting/Inno.Scripting.Compiler.Tests/CSharp14ScriptingTests.cs index 24f7e9e22..0d39a5be1 100644 --- a/tests/scripting/Inno.Scripting.Compiler.Tests/CSharp14ScriptingTests.cs +++ b/tests/scripting/Inno.Scripting.Compiler.Tests/CSharp14ScriptingTests.cs @@ -24,6 +24,31 @@ namespace Inno.Scripting.Compiler.Tests; /// public sealed class CSharp14ScriptingTests { + [Theory] + [InlineData("LangVersion", "preview")] + [InlineData("ImplicitUsings", "enable")] + [InlineData("DefineConstants", "UNEXPECTED")] + [InlineData("DisableImplicitConfigurationDefines", "false")] + public async Task IdeRejectsOverridesOfTheFrozenCompilerContract( + string property, + string value + ) { + using var fixture = new CompilerFixture(); + fixture.Write("Probe.cs", "public sealed class Probe { }"); + fixture.Rescan(); + fixture.compiler.GenerateProjectFiles(); + string project = Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj"); + string host = Path.GetFullPath(Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "..", "..", "..", + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet")); + DotNetSdkDescriptor sdk = await DotNetSdkResolver.ResolveAsync(host, project); + using var output = new StringWriter(); + using var errors = new StringWriter(); + await Assert.ThrowsAsync(() => ToolchainEnvironment.RunAsync(sdk.hostPath, + [sdk.cliPath, "build", project, "--disable-build-servers", "-m:1", "-nr:false", "-p:" + property + "=" + value], + fixture.projectRoot, CancellationToken.None, DotNetSdkEnvironment.Create(sdk.hostPath), output, errors)); + Assert.Contains("IDE compiler input '" + property + "' differs", output.ToString() + errors, StringComparison.Ordinal); + } + /// /// Projects installed extension properties and field-backed plugin properties into the IDE contract. /// @@ -81,9 +106,7 @@ public static class Consumer string projectPath = Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj"); string host = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); DotNetSdkDescriptor sdk = await DotNetSdkResolver.ResolveAsync(host, projectPath); - await ToolchainEnvironment.RunAsync(sdk.hostPath, - [sdk.cliPath, "build", projectPath, "--disable-build-servers", "-m:1", "-nr:false", "-v:quiet"], - fixture.projectRoot, CancellationToken.None, DotNetSdkEnvironment.Create(sdk.hostPath)); + _ = await BuildIdeAsync(sdk, projectPath, fixture.projectRoot); } /// @@ -206,12 +229,30 @@ int Produce() Assert.Equal("14.0", Assert.Single(project.Descendants("LangVersion")).Value); string host = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); DotNetSdkDescriptor sdk = await DotNetSdkResolver.ResolveAsync(host, projectPath); - string output = await ToolchainEnvironment.CaptureOutputAsync(sdk.hostPath, - [sdk.cliPath, "build", projectPath, "--disable-build-servers", "-m:1", "-nr:false", "-v:quiet"], - fixture.projectRoot, CancellationToken.None, DotNetSdkEnvironment.Create(sdk.hostPath)); + string output = await BuildIdeAsync(sdk, projectPath, fixture.projectRoot); Assert.DoesNotContain("CS8032", output, StringComparison.Ordinal); } + private static async Task BuildIdeAsync( + DotNetSdkDescriptor sdk, + string project, + string directory + ) { + using var output = new StringWriter(); + using var errors = new StringWriter(); + try + { + await ToolchainEnvironment.RunAsync(sdk.hostPath, + [sdk.cliPath, "build", project, "--disable-build-servers", "-m:1", "-nr:false", "-v:quiet"], + directory, CancellationToken.None, DotNetSdkEnvironment.Create(sdk.hostPath), output, errors); + return output.ToString(); + } + catch (InvalidOperationException failure) + { + throw new InvalidOperationException(output + Environment.NewLine + errors, failure); + } + } + [MethodImpl(MethodImplOptions.NoInlining)] private static WeakReference InvokePublicSemantics(string assemblyPath) { @@ -236,40 +277,42 @@ private static WeakReference InvokePublicSemantics(string assemblyPath) /// Invalidates reference and script artifacts when framework bytes change without changing the public contract. /// [Fact] - [Trait("ProcessIsolation", "Required")] public void FrameworkContentChangesSelectNewArtifacts() { - using var fixture = new CompilerFixture(); + var environment = new MutableCompilationEnvironment(); + using var fixture = new CompilerFixture(compilationEnvironment: environment); fixture.Write("Counter.cs", "public sealed class Counter { public int count { get; set; } }"); fixture.Rescan(); - string dotnetRoot = Path.GetFullPath(Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "..", "..", "..")); - string source = Directory.EnumerateDirectories(Path.Combine(dotnetRoot, "packs", "Microsoft.NETCore.App.Ref")) - .Where(path => Path.GetFileName(path).StartsWith("10.0.", StringComparison.Ordinal)) - .OrderByDescending(path => Version.Parse(Path.GetFileName(path))).First(); - string selectedRoot = Path.Combine(fixture.projectRoot, "Framework"); - string destination = Path.Combine(selectedRoot, "packs", "Microsoft.NETCore.App.Ref", Path.GetFileName(source), "ref", "net10.0"); + string host = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + ScriptCompilationInputs selected = new Inno.Build.SdkScriptCompilationEnvironment(host, fixture.projectRoot).Capture(); + string destination = Path.Combine(fixture.projectRoot, "Framework"); Directory.CreateDirectory(destination); - foreach (string path in Directory.EnumerateFiles(Path.Combine(source, "ref", "net10.0"), "*.dll")) + foreach (string path in selected.frameworkReferences) File.Copy(path, Path.Combine(destination, Path.GetFileName(path))); - string? previous = Environment.GetEnvironmentVariable("DOTNET_ROOT"); - try - { - Environment.SetEnvironmentVariable("DOTNET_ROOT", selectedRoot); - ScriptCompilationResult first = fixture.Compile(); - Assert.True(first.success, string.Join(Environment.NewLine, first.diagnostics)); - string referenceRoot = Path.Combine(fixture.projectRoot, "Library", "ScriptApi", "Runtime"); - string[] references = Directory.GetDirectories(referenceRoot); - using (FileStream changed = new(Path.Combine(destination, "System.Runtime.dll"), FileMode.Append)) - changed.WriteByte(0); - ScriptCompilationResult second = fixture.Compile(); - Assert.True(second.success, string.Join(Environment.NewLine, second.diagnostics)); - Assert.NotEqual(first.outputDirectory, second.outputDirectory); - Assert.NotEmpty(second.compiledAssemblyNames); - Assert.NotEmpty(Directory.GetDirectories(referenceRoot).Except(references)); - } - finally + environment.inputs = new ScriptCompilationInputs(selected.sdkIdentity, + Directory.EnumerateFiles(destination, "*.dll"), selected.predefinedSymbols); + ScriptCompilationResult first = fixture.Compile(); + Assert.True(first.success, string.Join(Environment.NewLine, first.diagnostics)); + string referenceRoot = Path.Combine(fixture.projectRoot, "Library", "ScriptApi", "Runtime"); + string[] references = Directory.GetDirectories(referenceRoot); + using (FileStream changed = new(Path.Combine(destination, "System.Runtime.dll"), FileMode.Append)) + changed.WriteByte(0); + ScriptCompilationResult second = fixture.Compile(); + Assert.True(second.success, string.Join(Environment.NewLine, second.diagnostics)); + Assert.NotEqual(first.outputDirectory, second.outputDirectory); + Assert.NotEmpty(second.compiledAssemblyNames); + Assert.NotEmpty(Directory.GetDirectories(referenceRoot).Except(references)); + } + + private sealed class MutableCompilationEnvironment : IScriptCompilationEnvironmentProvider + { + internal ScriptCompilationInputs inputs { get; set; } = null!; + + public ScriptCompilationInputs Capture(CancellationToken cancellationToken = default) { - Environment.SetEnvironmentVariable("DOTNET_ROOT", previous); + cancellationToken.ThrowIfCancellationRequested(); + return inputs; } } } diff --git a/tests/scripting/Inno.Scripting.Compiler.Tests/CompilerFixture.cs b/tests/scripting/Inno.Scripting.Compiler.Tests/CompilerFixture.cs index 9babb8657..7eccaa4ca 100644 --- a/tests/scripting/Inno.Scripting.Compiler.Tests/CompilerFixture.cs +++ b/tests/scripting/Inno.Scripting.Compiler.Tests/CompilerFixture.cs @@ -24,7 +24,10 @@ internal sealed class CompilerFixture : IDisposable private readonly ProjectSettingsStore m_settings; private readonly PluginEnvironment m_plugins; - internal CompilerFixture(Action? configure = null) + internal CompilerFixture( + Action? configure = null, + IScriptCompilationEnvironmentProvider? compilationEnvironment = null + ) { projectRoot = Path.Combine(Path.GetTempPath(), "InnoCompilerTests", Guid.NewGuid().ToString("N")); string assetsRoot = Path.Combine(projectRoot, "Assets"); @@ -55,7 +58,15 @@ internal CompilerFixture(Action? configure = null AssetSerializationContext.Create(m_assets)); m_plugins = new PluginEnvironment(m_assets, m_settings, m_host.serialization, pluginsRoot, libraryRoot, scan, m_host.modules.generations); - compiler = new ScriptCompiler(new ScriptCompilerOptions { projectRootDirectory = projectRoot }, m_assets, m_plugins); + string dotnetHost = Path.Combine( + Path.GetFullPath("../../..", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + compiler = new ScriptCompiler( + new ScriptCompilerOptions + { + projectRootDirectory = projectRoot, + compilationEnvironment = compilationEnvironment ?? new Inno.Build.SdkScriptCompilationEnvironment(dotnetHost, projectRoot) + }, m_assets, m_plugins); } internal string projectRoot { get; } diff --git a/tests/scripting/Inno.Scripting.Compiler.Tests/Inno.Scripting.Compiler.Tests.csproj b/tests/scripting/Inno.Scripting.Compiler.Tests/Inno.Scripting.Compiler.Tests.csproj index a8655ea44..cdbe68960 100644 --- a/tests/scripting/Inno.Scripting.Compiler.Tests/Inno.Scripting.Compiler.Tests.csproj +++ b/tests/scripting/Inno.Scripting.Compiler.Tests/Inno.Scripting.Compiler.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/scripting/Inno.Scripting.Compiler.Tests/SdkCompilationInputsTests.cs b/tests/scripting/Inno.Scripting.Compiler.Tests/SdkCompilationInputsTests.cs new file mode 100644 index 000000000..a3da3069f --- /dev/null +++ b/tests/scripting/Inno.Scripting.Compiler.Tests/SdkCompilationInputsTests.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using Inno.Build; +using Xunit; + +namespace Inno.Scripting.Compiler.Tests; + +public sealed class SdkCompilationInputsTests +{ + [Fact] + public void SelectedSdkSuppliesFrameworkSymbolsAndIdeReferences() + { + using var fixture = new CompilerFixture(); + string host = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + ScriptCompilationInputs inputs = new SdkScriptCompilationEnvironment(host, fixture.projectRoot).Capture(); + Assert.Contains("NET10_0", inputs.predefinedSymbols); + Assert.Contains("NET10_0_OR_GREATER", inputs.predefinedSymbols); + Assert.DoesNotContain("DEBUG", inputs.predefinedSymbols); + Assert.DoesNotContain("TRACE", inputs.predefinedSymbols); + Assert.Contains(inputs.frameworkReferences, static path => Path.GetFileName(path) == "System.Runtime.dll"); + Assert.All(inputs.frameworkReferences, static path => Assert.True(File.Exists(path), path)); + Assert.DoesNotContain(inputs.frameworkReferences, static path => Path.GetFileName(path) == "System.Private.CoreLib.dll"); + fixture.Write("FrameworkSymbols.cs", """ + #if !NET10_0 || !NET10_0_OR_GREATER + #error SDK framework symbols must match runtime and IDE compilation. + #endif + public sealed class FrameworkSymbols { } + """); + fixture.Rescan(); + Assert.True(fixture.Compile().success); + fixture.compiler.GenerateProjectFiles(); + string project = File.ReadAllText(Path.Combine(fixture.projectRoot, "Inno.GameScripts.csproj")); + Assert.Contains("ValidateInnoScriptSdk", project, StringComparison.Ordinal); + Assert.Contains("true", project, StringComparison.Ordinal); + foreach (string path in inputs.frameworkReferences) + Assert.Contains(path.Replace("&", "&", StringComparison.Ordinal), project, StringComparison.Ordinal); + Assert.Empty(Directory.EnumerateFiles(Path.Combine(fixture.projectRoot, "Library", "Scripting"), "*.csproj", SearchOption.AllDirectories)); + } + + [Fact] + public void MissingSelectedSdkDoesNotPublishScriptArtifactsOrRetainEvaluationProjects() + { + using var fixture = new CompilerFixture(); + File.WriteAllText(Path.Combine(fixture.projectRoot, "global.json"), """ + { "sdk": { "version": "10.0.999", "rollForward": "disable", "allowPrerelease": false } } + """); + fixture.Write("Probe.cs", "public sealed class Probe { }"); + fixture.Rescan(); + Assert.Throws(() => fixture.Compile()); + Assert.Empty(Directory.EnumerateFiles(Path.Combine(fixture.projectRoot, "Library"), "*.dll", SearchOption.AllDirectories)); + Assert.Empty(Directory.EnumerateFiles(Path.Combine(fixture.projectRoot, "Library"), "*.csproj", SearchOption.AllDirectories)); + } + + [Fact] + public void CancelledSdkEvaluationCreatesNoPublishedInputs() + { + using var fixture = new CompilerFixture(); + using var cancellation = new CancellationTokenSource(); + cancellation.Cancel(); + string host = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + var provider = new SdkScriptCompilationEnvironment(host, fixture.projectRoot); + Assert.ThrowsAny(() => provider.Capture(cancellation.Token)); + Assert.Empty(Directory.EnumerateFiles(Path.Combine(fixture.projectRoot, "Library"), "*.csproj", SearchOption.AllDirectories)); + } + + [Fact] + public void InputContractCopiesCollectionsAndRejectsIncompleteReferences() + { + string[] references = [typeof(object).Assembly.Location]; + string[] symbols = ["NET10_0"]; + var inputs = new ScriptCompilationInputs("10.0.402", references, symbols); + references[0] = "changed"; + symbols[0] = "CHANGED"; + Assert.True(Path.IsPathFullyQualified(Assert.Single(inputs.frameworkReferences))); + Assert.Equal("NET10_0", Assert.Single(inputs.predefinedSymbols)); + Assert.Throws(() => new ScriptCompilationInputs("10.0.402", [], [])); + Assert.Throws(() => new ScriptCompilationInputs("10.0.402", ["relative.dll"], [])); + } +} diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureOutputOwnerTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureOutputOwnerTests.cs new file mode 100644 index 000000000..450c01099 --- /dev/null +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureOutputOwnerTests.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Inno.Build.Toolchains; +using Xunit; + +namespace Inno.Tooling.Architecture.Tests; + +[Collection("Architecture diagnostics")] +public sealed class ArchitectureOutputOwnerTests +{ + [Fact] + public async Task PublicContractsUseTheEvaluatedOutputOwnerInsteadOfStaleDefaultAssemblies() + { + string root = Path.Combine(Path.GetTempPath(), "InnoArchitectureOutputTests", Guid.NewGuid().ToString("N")); + string host = Path.GetFullPath(Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "..", "..", "..", + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet")); + using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + try + { + string contract = Path.Combine(root, "src/foundation/core/Inno.Core.Contract"); + string editor = Path.Combine(root, "src/composition/editor/framework/Inno.Editor.Probe"); + Directory.CreateDirectory(contract); + Directory.CreateDirectory(editor); + File.WriteAllText(Path.Combine(root, "AGENTS.md"), "# Fixture"); + File.WriteAllText(Path.Combine(root, "InnoEngine.sln"), string.Empty); + File.WriteAllText(Path.Combine(contract, "Inno.Core.Contract.csproj"), """ + + net10.0 + + """); + File.WriteAllText(Path.Combine(contract, "Contract.cs"), "namespace Inno.Core.Contract; public sealed class Contract { }"); + string project = Path.Combine(editor, "Inno.Editor.Probe.csproj"); + File.WriteAllText(project, """ + + net10.0 + + + """); + string source = Path.Combine(editor, "Probe.cs"); + File.WriteAllText(source, "namespace Inno.Editor.Probe; public sealed class Probe { }"); + await Build([]); + File.WriteAllText(source, "namespace Inno.Editor.Probe; public sealed class Probe { public Inno.Core.Contract.Contract value { get; } = new(); }"); + string artifacts = Path.Combine(root, "owned-output"); + await Build(["-p:ArtifactsPath=" + artifacts]); + string graph = Path.Combine(root, "graph.json"); + + string correct = ArchitectureCommandFixture.Run(root, "--dotnet", host, + "--artifacts-path", artifacts, "--project-graph", graph).output; + Assert.DoesNotContain("Inno.Core.Contract is an implementation dependency", correct, StringComparison.Ordinal); + Assert.Contains(artifacts.Replace('\\', '/'), File.ReadAllText(graph).Replace('\\', '/').Replace("//", "/"), StringComparison.OrdinalIgnoreCase); + + File.WriteAllText(project, File.ReadAllText(project).Replace("Include=", "PrivateAssets=\"compile\" Include=", StringComparison.Ordinal)); + string rejected = ArchitectureCommandFixture.Run(root, "--dotnet", host, "--artifacts-path", artifacts).output; + Assert.Contains("public/protected API exposes Inno.Core.Contract", rejected, StringComparison.Ordinal); + + async Task Build(string[] properties) + { + await ToolchainEnvironment.RunAsync(host, ["build", project, "--disable-build-servers", "-m:1", "-nr:false", "-v:quiet", .. properties], + root, cancellation.Token, DotNetSdkEnvironment.Create(host)); + } + } + finally + { + if (Directory.Exists(root)) + Directory.Delete(root, recursive: true); + } + } +} diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj index e366f32a3..1263262ea 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs b/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs index e6f21b275..93a6b859b 100644 --- a/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs +++ b/tools/Inno.Tooling.Architecture/ArchitectureValidator.cs @@ -74,6 +74,7 @@ public static int Execute( bool selectedConfiguration = false; string? dotnetHost = null; string? projectGraphPath = null; + string? artifactsPath = null; var paths = new List(); for (int index = 0; index < arguments.Length; index++) { @@ -96,13 +97,16 @@ public static int Execute( dotnetHost = arguments[++index]; else if (argument == "--project-graph" && projectGraphPath is null && index + 1 < arguments.Length) projectGraphPath = arguments[++index]; + else if (argument == "--artifacts-path" && artifactsPath is null && index + 1 < arguments.Length) + artifactsPath = Path.GetFullPath(arguments[++index]); else if (argument == "--configuration" && !selectedConfiguration && index + 1 < arguments.Length) { configuration = arguments[++index] switch { "Debug" => "Debug", "Release" => "Release", - _ => throw new ArgumentException("Architecture configuration must be Debug or Release.", nameof(arguments)) + "SourceDebug" => "SourceDebug", + _ => throw new ArgumentException("Architecture configuration must be Debug, SourceDebug or Release.", nameof(arguments)) }; selectedConfiguration = true; } @@ -110,11 +114,11 @@ public static int Execute( paths.Add(argument); else throw new ArgumentException( - "Usage: verify [engine-root] [--configuration Debug|Release] [--expand-xml] [--dotnet SDK] [--project-graph output.json] [--wiki] [--wiki-target targetId] [--wiki-api inventory.json] [--wiki-examples examples.json] [--wiki-runtime-reference assembly.dll] [--wiki-editor-reference assembly.dll]", + "Usage: verify [engine-root] [--configuration Debug|SourceDebug|Release] [--expand-xml] [--dotnet SDK] [--artifacts-path directory] [--project-graph output.json] [--wiki] [--wiki-target targetId] [--wiki-api inventory.json] [--wiki-examples examples.json] [--wiki-runtime-reference assembly.dll] [--wiki-editor-reference assembly.dll]", nameof(arguments) ); } - if (projectGraphPath is not null && dotnetHost is null) + if ((projectGraphPath is not null || artifactsPath is not null) && dotnetHost is null) throw new ArgumentException("Evaluated project graph output requires an explicitly selected --dotnet SDK.", nameof(arguments)); if ((wikiInventoryPath is not null || wikiExamplesPath is not null || wikiRuntimeReference is not null || wikiEditorReference is not null || wikiTarget is not null) && !wiki) @@ -130,8 +134,8 @@ public static int Execute( } List failures = []; - WikiProjectReferences? evaluatedReferences = !wiki || dotnetHost is null ? null - : new WikiProjectReferences(repositoryRoot, configuration, dotnetHost, wikiTarget); + WikiProjectReferences? evaluatedReferences = dotnetHost is null ? null + : new WikiProjectReferences(repositoryRoot, configuration, dotnetHost, wikiTarget, artifactsPath); var documentationModels = new DocumentationSourceModels(repositoryRoot, configuration, evaluatedReferences, cancellationToken, failures); if (wiki) @@ -160,10 +164,11 @@ public static int Execute( ValidateProductionSolutionFolders(repositoryRoot, failures); ValidateTestSolutionFolders(repositoryRoot, failures); ArchitectureRules.Validate(repositoryRoot, failures); - PublicApiBoundaryValidator.Validate(repositoryRoot, configuration, failures); + IReadOnlyDictionary? outputs = null; if (dotnetHost is not null) - MSBuildProjectGraphValidator.ValidateAsync(repositoryRoot, configuration, dotnetHost, + outputs = MSBuildProjectGraphValidator.ValidateAsync(repositoryRoot, configuration, dotnetHost, artifactsPath, projectGraphPath, failures, cancellationToken).GetAwaiter().GetResult(); + PublicApiBoundaryValidator.Validate(repositoryRoot, configuration, failures, outputs); if (failures.Count == 0) { Console.WriteLine("InnoEngine architecture validation passed."); diff --git a/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs b/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs index 5d54a2532..5712cc72b 100644 --- a/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs +++ b/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs @@ -42,6 +42,12 @@ internal DocumentationSourceModels( .ToArray(); m_projectNames = projects.Select(Path.GetFileNameWithoutExtension) .OfType().ToHashSet(StringComparer.Ordinal); + if (m_projectReferences is not null) + { + m_references = []; + m_frameworkReferences = m_projectReferences.GetFrameworkReferences(cancellationToken); + return; + } var paths = new Dictionary(StringComparer.OrdinalIgnoreCase); string runtimeDirectory = RuntimeEnvironment.GetRuntimeDirectory().TrimEnd(Path.DirectorySeparatorChar); string frameworkReferences = Path.GetFullPath(Path.Combine(runtimeDirectory, "..", "..", "..", @@ -63,11 +69,6 @@ internal DocumentationSourceModels( path.StartsWith(frameworkReferences + Path.DirectorySeparatorChar, pathComparison) || path.StartsWith(runtimeDirectory + Path.DirectorySeparatorChar, pathComparison)) .Select(static path => (MetadataReference)MetadataReference.CreateFromFile(path)).ToArray(); - if (m_projectReferences is not null) - { - m_references = []; - return; - } var projectOutputs = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (string project in projects) { diff --git a/tools/Inno.Tooling.Architecture/MSBuildProjectGraphValidator.cs b/tools/Inno.Tooling.Architecture/MSBuildProjectGraphValidator.cs index aa1186a28..ba71a4589 100644 --- a/tools/Inno.Tooling.Architecture/MSBuildProjectGraphValidator.cs +++ b/tools/Inno.Tooling.Architecture/MSBuildProjectGraphValidator.cs @@ -17,10 +17,11 @@ internal static class MSBuildProjectGraphValidator "InnoEngine.Editor", "InnoEngine.Build" }; - internal static async Task ValidateAsync( + internal static async Task> ValidateAsync( string root, string configuration, string dotnetHost, + string? artifactsPath, string? outputPath, ICollection failures, CancellationToken cancellationToken @@ -38,7 +39,7 @@ CancellationToken cancellationToken try { EvaluatedProject snapshot = await EvaluateAsync(root, project, configuration, - dotnetHost, cancellationToken).ConfigureAwait(false); + dotnetHost, artifactsPath, cancellationToken).ConfigureAwait(false); evaluated.Add(snapshot); ValidateProject(root, snapshot, failures); } @@ -56,6 +57,8 @@ await File.WriteAllTextAsync(destination, JsonSerializer.Serialize(evaluated, new JsonSerializerOptions { WriteIndented = true }), cancellationToken).ConfigureAwait(false); } Console.WriteLine($"Evaluated MSBuild ownership for {evaluated.Count}/{projects.Length} projects without executing build targets."); + return evaluated.ToDictionary(project => Path.GetFullPath(project.path, root), + static project => project.targetPath, StringComparer.OrdinalIgnoreCase); } private static async Task EvaluateAsync( @@ -63,6 +66,7 @@ private static async Task EvaluateAsync( string project, string configuration, string dotnetHost, + string? artifactsPath, CancellationToken cancellationToken ) { var start = new ProcessStartInfo(dotnetHost) @@ -83,11 +87,13 @@ CancellationToken cancellationToken foreach (string argument in new[] { "msbuild", project, "-nologo", "-getItem:ProjectReference,Compile,Using", - "-getProperty:AssemblyName,OutputType,InnoProductId,InnoProductTarget,InnoNativeTarget,ImplicitUsings", + "-getProperty:AssemblyName,OutputType,InnoProductId,InnoProductTarget,InnoNativeTarget,ImplicitUsings,TargetPath", "-p:DesignTimeBuild=true", "-p:BuildProjectReferences=false", "-p:InnoProductRestore=false", "-p:Configuration=" + configuration }) start.ArgumentList.Add(argument); + if (artifactsPath is not null) + start.ArgumentList.Add("-p:ArtifactsPath=" + artifactsPath); using var process = new Process { StartInfo = start }; if (!process.Start()) throw new InvalidDataException("The selected SDK did not start."); @@ -125,7 +131,7 @@ CancellationToken cancellationToken return new(Relative(root, project), Value(properties, "AssemblyName"), Value(properties, "OutputType"), Value(properties, "InnoProductId"), Value(properties, "InnoProductTarget"), Value(properties, "InnoNativeTarget"), Value(properties, "ImplicitUsings"), - items.GetProperty("Using").GetArrayLength(), references, sources); + items.GetProperty("Using").GetArrayLength(), references, sources, Value(properties, "TargetPath")); } private static void ValidateProject( @@ -237,7 +243,8 @@ private sealed record EvaluatedProject( string implicitUsings, int usingItems, IReadOnlyList references, - IReadOnlyList sources + IReadOnlyList sources, + string targetPath ); private sealed record EvaluatedReference( diff --git a/tools/Inno.Tooling.Architecture/PublicApiBoundaryValidator.cs b/tools/Inno.Tooling.Architecture/PublicApiBoundaryValidator.cs index 007ca63cc..0eb5b67d1 100644 --- a/tools/Inno.Tooling.Architecture/PublicApiBoundaryValidator.cs +++ b/tools/Inno.Tooling.Architecture/PublicApiBoundaryValidator.cs @@ -13,7 +13,8 @@ internal static class PublicApiBoundaryValidator internal static void Validate( string root, string configuration, - ICollection failures + ICollection failures, + IReadOnlyDictionary? evaluatedOutputs = null ) { Project[] projects = new[] { "entry", "src", "backends", "platforms", "build" } .SelectMany(folder => RepositorySourceInventory.Files(Path.Combine(root, folder), "*.csproj")) @@ -22,7 +23,7 @@ ICollection failures .Where(path => !path.Split(Path.DirectorySeparatorChar).Contains("Bindings", StringComparer.Ordinal)) .Where(path => !path.Split(Path.DirectorySeparatorChar).Contains("tests", StringComparer.Ordinal)) .Where(path => !XDocument.Load(path).Descendants("OutputType").Any(static item => item.Value == "Exe")) - .Select(path => Project.Read(path, configuration)).ToArray(); + .Select(path => Project.Read(path, configuration, evaluatedOutputs)).ToArray(); var byName = projects.ToDictionary(project => project.name, StringComparer.Ordinal); var references = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] platform = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") ?? string.Empty).Split(Path.PathSeparator); @@ -201,14 +202,18 @@ bool product ) { internal static Project Read( string path, - string configuration + string configuration, + IReadOnlyDictionary? evaluatedOutputs ) { XDocument document = XDocument.Load(path); string name = document.Descendants("AssemblyName").FirstOrDefault()?.Value ?? Path.GetFileNameWithoutExtension(path); string framework = document.Descendants("TargetFramework").FirstOrDefault()?.Value ?? "net10.0"; string? target = document.Descendants("InnoProductTarget").FirstOrDefault()?.Value; - return new Project(path, name, Path.Combine(Path.GetDirectoryName(path)!, - target is null ? "bin" : Path.Combine("bin", target), configuration, framework, name + ".dll"), + string output = evaluatedOutputs is null + ? Path.Combine(Path.GetDirectoryName(path)!, target is null ? "bin" : Path.Combine("bin", target), + configuration, framework, name + ".dll") + : evaluatedOutputs.TryGetValue(Path.GetFullPath(path), out string? selected) ? selected : string.Empty; + return new Project(path, name, output, document.Descendants("ProjectReference").ToArray(), document.Descendants("InnoProductId").Any()); } } diff --git a/tools/Inno.Tooling.Architecture/WikiProjectReferences.cs b/tools/Inno.Tooling.Architecture/WikiProjectReferences.cs index 789418636..488cfe7d2 100644 --- a/tools/Inno.Tooling.Architecture/WikiProjectReferences.cs +++ b/tools/Inno.Tooling.Architecture/WikiProjectReferences.cs @@ -17,15 +17,18 @@ internal sealed class WikiProjectReferences private readonly string m_configuration; private readonly string m_dotnetHost; private readonly string? m_target; + private readonly string? m_artifactsPath; private readonly Dictionary m_compilations = new(StringComparer.OrdinalIgnoreCase); private readonly Dictionary m_sources = new(StringComparer.OrdinalIgnoreCase); private readonly Dictionary m_outputs = new(StringComparer.OrdinalIgnoreCase); + private IReadOnlyList? m_frameworkReferences; internal WikiProjectReferences( string root, string configuration, string dotnetHost, - string? target = null + string? target = null, + string? artifactsPath = null ) { if (!Path.IsPathFullyQualified(dotnetHost) || !File.Exists(dotnetHost)) throw new ArgumentException("Wiki SDK inspection requires an existing absolute dotnet executable.", nameof(dotnetHost)); @@ -33,6 +36,7 @@ internal WikiProjectReferences( m_configuration = configuration; m_dotnetHost = dotnetHost; m_target = target; + m_artifactsPath = artifactsPath; } internal bool IsExcludedProduct(string project) @@ -43,6 +47,17 @@ internal bool IsExcludedProduct(string project) return target is not null && !string.Equals(target, m_target, StringComparison.Ordinal); } + internal IReadOnlyList GetFrameworkReferences(CancellationToken cancellationToken) + { + if (m_frameworkReferences is not null) + return m_frameworkReferences; + string project = RepositorySourceInventory.Files(Path.Combine(m_root, "src", "foundation"), "*.csproj") + .Where(path => XDocument.Load(path).Descendants("TargetFramework").Any(static item => item.Value == "net10.0")) + .Order(StringComparer.Ordinal).First(); + _ = Evaluate(project, cancellationToken, frameworkOnly: true); + return m_frameworkReferences ?? throw new InvalidDataException("The selected SDK did not provide framework references."); + } + internal CSharpCompilation GetCompilation( string project, CancellationToken cancellationToken @@ -77,7 +92,8 @@ CancellationToken cancellationToken private CSharpCompilation Evaluate( string project, - CancellationToken cancellationToken + CancellationToken cancellationToken, + bool frameworkOnly = false ) { if (m_compilations.TryGetValue(project, out CSharpCompilation? compilation)) return compilation; @@ -88,10 +104,11 @@ CancellationToken cancellationToken { // Resolve compile references only. Design-time evaluation must never prepare native products. output = ToolchainEnvironment.CaptureOutputAsync(m_dotnetHost, - ["msbuild", project, "-nologo", "-m:1", "-nr:false", "-t:ResolveReferences", + ["msbuild", project, "-nologo", "-m:1", "-nr:false", "-t:ResolveReferences;AddImplicitDefineConstants", "-getItem:ReferencePath,Compile", "-getProperty:TargetPath,DefineConstants,LangVersion,Nullable,AllowUnsafeBlocks", "-p:DesignTimeBuild=true", "-p:BuildProjectReferences=false", "-p:InnoProductRestore=false", - "-p:Configuration=" + m_configuration], + "-p:Configuration=" + m_configuration, + .. m_artifactsPath is null ? Array.Empty() : new[] { "-p:ArtifactsPath=" + m_artifactsPath }], m_root, bounded.Token, DotNetSdkEnvironment.Create(m_dotnetHost)).GetAwaiter().GetResult(); } catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) @@ -104,6 +121,17 @@ CancellationToken cancellationToken string[] references = document.RootElement.GetProperty("Items").GetProperty("ReferencePath") .EnumerateArray().Select(static item => item.GetProperty("FullPath").GetString()!) .Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + if (m_frameworkReferences is null) + { + string[] framework = document.RootElement.GetProperty("Items").GetProperty("ReferencePath").EnumerateArray() + .Where(static item => item.TryGetProperty("FrameworkReferenceName", out JsonElement name) + && name.GetString() == "Microsoft.NETCore.App") + .Select(static item => item.GetProperty("FullPath").GetString()!).ToArray(); + if (framework.Length != 0) + m_frameworkReferences = framework.Select(static path => (MetadataReference)MetadataReference.CreateFromFile(path)).ToArray(); + } + if (frameworkOnly) + return CSharpCompilation.Create("WikiFramework", references: m_frameworkReferences); foreach (string reference in references) if (!File.Exists(reference)) throw new FileNotFoundException($"Wiki project '{project}' requires its evaluated compile reference: '{reference}'. Build this target/configuration before validation.", reference); From 8dcceb94f4dd805075f6ed28db996eb017eae619 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 07:35:47 +0800 Subject: [PATCH 12/54] fix(build): isolate neutral Foundation bootstrap compilation outputs --- Directory.Build.targets | 4 +- build/msbuild/Inno.Build.Tasks.targets | 5 +- docs/architecture/BUILD_CACHE_ARCHITECTURE.md | 2 + docs/build/Inno.Build.TaskHosting.md | 2 + .../CompilerToolBuildScopeTests.cs | 17 ++- .../TaskPublisherBootstrapTests.cs | 115 ++++++++++++++++-- 6 files changed, 128 insertions(+), 17 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index f8201e3e9..6247bc117 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -84,13 +84,13 @@ - <_InnoCompilerToolPropertyPattern>(?i)(^|;)(?:$([System.String]::Copy('$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner').Replace(';', '|')))=[^;]* + <_InnoCompilerToolPropertyPattern>(?i)(^|;)(?:$([System.String]::Copy('$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner;BuildingInsideVisualStudio').Replace(';', '|')))=[^;]* <_MSBuildProjectReferenceExistent Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(_MSBuildProjectReferenceExistent.AdditionalPropertiesFromProject)', '<IsRoslynComponent>\s*(?i:true)\s*</IsRoslynComponent>'))"> true $([System.Text.RegularExpressions.Regex]::Replace('%(_MSBuildProjectReferenceExistent.AdditionalProperties)', '$(_InnoCompilerToolPropertyPattern)', '')) - %(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner + %(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);$(InnoCompilerToolExcludedProperties);BuildProjectReferences;InnoTaskBootstrapOwner;BuildingInsideVisualStudio diff --git a/build/msbuild/Inno.Build.Tasks.targets b/build/msbuild/Inno.Build.Tasks.targets index dc76b9ee9..1c74210e4 100644 --- a/build/msbuild/Inno.Build.Tasks.targets +++ b/build/msbuild/Inno.Build.Tasks.targets @@ -131,11 +131,14 @@ Condition="'$(DesignTimeBuild)' != 'true'"> <_InnoFoundationExcludedProperties>$([System.String]::Copy('$(InnoTaskExcludedProperties)').Replace(';InnoTaskBootstrapOwner', '')) + <_InnoFoundationArtifacts Condition="'$(ArtifactsPath)' != ''">$(ArtifactsPath)/.inno-task-foundation + <_InnoFoundationArtifacts Condition="'$(ArtifactsPath)' == ''">$(InnoTaskArtifacts)/foundation + diff --git a/docs/architecture/BUILD_CACHE_ARCHITECTURE.md b/docs/architecture/BUILD_CACHE_ARCHITECTURE.md index 63e82c9b6..fdd54f991 100644 --- a/docs/architecture/BUILD_CACHE_ARCHITECTURE.md +++ b/docs/architecture/BUILD_CACHE_ARCHITECTURE.md @@ -6,6 +6,8 @@ 构建工具冻结实际输入和工具选择,组件维护自己的 recipe,发布器验证并安装完整产物。缓存身份用于判断当前请求是否可以消费这些产物;工作目录用于保留兼容的编译中间态,两者有不同的生命周期。 +最初的 Foundation 引导是独立的普通 SDK 编译工作区:显式 `ArtifactsPath` owner 下的 `.inno-task-foundation`,或默认 Task artifacts 下的固定 `foundation`。其配置目录复用,不按 Build 次数增加;它不属于不可变 publication,也不参与根据后缀猜测删除。中立引导和调用方源码不能以不同 SDK 属性共享同一个编译输出。实际 publisher 与 runtime publication 仍使用既有 Core.IO owner、lease 和维护协议。 + 绑定 generation 同时携带不可变产物目录与实际发布 owner;selection 将两者一起传入后续 MSBuild。冻结源码树只改变组件源码位置,不改变产物的协调域。生成 Task 也输出同一 owner,materialization 不从源码工作区推测它,并继续校验产物必须属于声明 owner。 托管输入规划使用 `artifacts/managed/evaluations/` 下独立的 SDK `ArtifactsPath`,不改写普通 IDE 的 `obj`。还原、声明的输入工程还原、求值与内容读取都在同一 owner/叶锁范围内;不同全局属性的辅助工程拥有独立子布局,避免 RID 图相互覆盖。派生图可以重建,源输入变化仍在 fresh 校验中拒绝。 diff --git a/docs/build/Inno.Build.TaskHosting.md b/docs/build/Inno.Build.TaskHosting.md index 5d6d269e4..0ee536c70 100644 --- a/docs/build/Inno.Build.TaskHosting.md +++ b/docs/build/Inno.Build.TaskHosting.md @@ -10,6 +10,8 @@ owned Restore 使用 NuGet 标准属性 `ExcludeRestorePackageImports=true`;Ta 工程使用标准 PackageReference 和 SDK Restore。输入快照读取实际求值的 assets 路径及包内容,不回查另一配置的默认 obj;独立 ArtifactsPath 隔离所属还原与编译输出。工具保留其真实 SDK 输入,规则与更新方式见[依赖还原](DEPENDENCY_SUBMISSION.md)。 +最早的 Core.IO 引导采用独立、中立的 SDK 编译 scope。有调用方 `ArtifactsPath` 时,其位置为该 owner 下的 `.inno-task-foundation`;普通输出使用 Task artifacts 下固定的 `foundation`。它是按配置复用的 SDK 中间输出,不是每次操作发布一个新 generation。引导请求继续剥离产品 TFM、RID、Browser、AOT 和发布参数;不会以不同全局属性写入调用方同一个可变 Core.IO 输出。随后仍由原有 Core.IO 协议声明 publisher owner,并进行私有加载、冻结、校验和原子发布。 + ## 职责与边界 这是 MSBuild 引导边界的库,负责协调 Task 运行闭包的编译、发布及退休,并在读取保护期间复制生成绑定。它依赖 Core IO、中立 Toolchains 与 MSBuild 契约, diff --git a/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs index 3fea948f9..fa506b7a5 100644 --- a/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs +++ b/tests/build/Inno.Build.Tests/CompilerToolBuildScopeTests.cs @@ -14,8 +14,10 @@ namespace Inno.Build.Tests; public sealed class CompilerToolBuildScopeTests { - [Fact] - public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild() + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild(string configuration) { string repository = TestRepository.GetRoot(typeof(CompilerToolBuildScopeTests).Assembly); string root = Path.Combine(Path.GetTempPath(), "InnoCompilerToolScope", Guid.NewGuid().ToString("N")); @@ -37,7 +39,7 @@ public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild() WriteProject(root, "Runtime", """ net10.0 - + """); WriteProject(root, "Analyzer", """ @@ -57,11 +59,13 @@ public async Task AnalyzerRuntimeAndTransitiveEdgesShareOneCompilerToolBuild() """); string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(3)); - for (int run = 0; run < 2; run++) + for (int run = 0; run < 3; run++) { + if (run == 2) + File.AppendAllText(Path.Combine(root, "Tool", "Tool.cs"), "\npublic sealed class ChangedToolMarker { }\n"); string binlog = Path.Combine(root, run + ".binlog"); await ToolchainEnvironment.RunAsync(dotnet, - ["build", "Root/Root.csproj", "--nologo", "-m:4", "-nodeReuse:false", + ["build", "Root/Root.csproj", "--nologo", "-m:4", "-nodeReuse:false", "-c", configuration, "-p:ArtifactsPath=" + Path.Combine(root, "managed"), "-p:InnoNativeTarget=fixture-target", "-bl:" + binlog], root, timeout.Token, DotNetSdkEnvironment.Create(dotnet)); int compilations = 0; @@ -77,6 +81,7 @@ await ToolchainEnvironment.RunAsync(dotnet, Assert.DoesNotContain("InnoNativeTarget", project.GlobalProperties!.Keys); Assert.DoesNotContain("BuildProjectReferences", project.GlobalProperties.Keys); Assert.DoesNotContain("InnoTaskBootstrapOwner", project.GlobalProperties.Keys); + Assert.DoesNotContain("BuildingInsideVisualStudio", project.GlobalProperties.Keys); identities.Add(string.Join(";", project.GlobalProperties.OrderBy(static pair => pair.Key) .Select(static pair => pair.Key + "=" + pair.Value))); }; @@ -89,7 +94,7 @@ await ToolchainEnvironment.RunAsync(dotnet, }; events.Replay(binlog); Assert.Single(identities); - Assert.Equal(run == 0 ? 1 : 0, compilations); + Assert.Equal(run == 1 ? 0 : 1, compilations); } } finally diff --git a/tests/build/Inno.Build.Tests/TaskPublisherBootstrapTests.cs b/tests/build/Inno.Build.Tests/TaskPublisherBootstrapTests.cs index b0db48582..d5b73a95f 100644 --- a/tests/build/Inno.Build.Tests/TaskPublisherBootstrapTests.cs +++ b/tests/build/Inno.Build.Tests/TaskPublisherBootstrapTests.cs @@ -8,14 +8,17 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Logging; +using Inno.Build.Toolchains; using Xunit; namespace Inno.Build.Tests; public sealed class TaskPublisherBootstrapTests { - [Fact] - public async Task ProjectGraphSharesCompilationButKeepsPrivateLoadsAndFreshBuildOwnership() + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task ProjectGraphSharesCompilationButKeepsPrivateLoadsAndFreshBuildOwnership(string configuration) { string repository = TestRepository.GetRoot(typeof(TaskPublisherBootstrapTests).Assembly); string root = Path.Combine(Path.GetTempPath(), "InnoPublisherTests", Guid.NewGuid().ToString("N")); @@ -45,13 +48,18 @@ public async Task ProjectGraphSharesCompilationButKeepsPrivateLoadsAndFreshBuild }; string references = string.Join(string.Empty, dependencies.Select(static name => $"")); + if (name == "Root") + references += ""; File.WriteAllText(Path.Combine(directory, name + ".csproj"), "net10.0" - + "disable" + references + ""); + + "disablefalse" + + "BuildProjectReferences=true;BuildingInsideVisualStudio=false" + + "" + references + ""); } - var first = await Build(root); + var first = await Build(root, configuration); string firstOwner = Validate(first.output, first.binlog); - var second = await Build(root); + var second = await Build(root, configuration); string secondOwner = Validate(second.output, second.binlog); Assert.NotEqual(firstOwner, secondOwner); } @@ -61,6 +69,66 @@ public async Task ProjectGraphSharesCompilationButKeepsPrivateLoadsAndFreshBuild } } + [Theory] + [InlineData("net10.0-browser", "browser-wasm")] + [InlineData("net10.0", "win-x64")] + public async Task ProductPublishPropertiesDoNotReachFoundationBootstrap( + string framework, + string runtimeIdentifier + ) { + string repository = TestRepository.GetRoot(typeof(TaskPublisherBootstrapTests).Assembly); + string root = Path.Combine(Path.GetTempPath(), "InnoBootstrapTargetTests", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(root); + try + { + string project = Path.Combine(root, "Probe.proj"); + File.WriteAllText(project, $$""" + + Debug + + + + + """); + string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + string binlog = Path.Combine(root, "probe.binlog"); + using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(3)); + using var output = new StringWriter(); + using var errors = new StringWriter(); + Exception? failure = await Record.ExceptionAsync(() => ToolchainEnvironment.RunAsync(dotnet, + ["msbuild", project, "-nologo", "-t:Probe", "-nr:false", "-bl:" + binlog, + "-p:InnoTaskArtifacts=" + Path.Combine(root, "tasks"), "-p:ArtifactsPath=" + Path.Combine(root, "caller"), + "-p:TargetFramework=" + framework, "-p:RuntimeIdentifier=" + runtimeIdentifier, + "-p:PublishAot=true", "-p:PublishSingleFile=true", "-p:SelfContained=true", "-p:WasmBuildNative=true"], + repository, timeout.Token, DotNetSdkEnvironment.Create(dotnet), output, errors)); + Assert.True(failure is null, $"{failure}\n{output}\n{errors}"); + int requests = 0; + var events = new BinaryLogReplayEventSource(); + events.ProjectStarted += ( + _, + item + ) => { + if (Path.GetFileNameWithoutExtension(item.ProjectFile) != "Inno.Core.IO" + || item.TargetNames != "Restore;Build") + return; + requests++; + Assert.NotNull(item.GlobalProperties); + foreach (string property in new[] { "RuntimeIdentifier", "TargetFramework", "PublishAot", "PublishSingleFile", "SelfContained", "WasmBuildNative" }) + Assert.DoesNotContain(property, item.GlobalProperties!.Keys); + Assert.Equal(Path.Combine(root, "caller", ".inno-task-foundation").Replace('\\', '/'), + item.GlobalProperties!["ArtifactsPath"].Replace('\\', '/')); + }; + events.Replay(binlog); + Assert.True(requests > 0, "No neutral Foundation bootstrap was inspected."); + Assert.False(Directory.Exists(Path.Combine(root, "caller", "bin"))); + Assert.False(Directory.Exists(Path.Combine(root, "caller", "obj"))); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + private static string Validate( string output, string binlog @@ -75,6 +143,32 @@ string binlog // Incremental Build targets can report TargetPath without executing the compiler. int compilations = 0; var events = new BinaryLogReplayEventSource(); + int sharedFoundationRequests = 0; + int bootstrapFoundationRequests = 0; + events.ProjectStarted += ( + _, + project + ) => { + if (Path.GetFileNameWithoutExtension(project.ProjectFile) != "Inno.Core.IO" + || project.GlobalProperties is not { } globals + || !globals.TryGetValue("ArtifactsPath", out string? artifacts)) + return; + if (string.Equals(Path.GetFileName(artifacts), ".inno-task-foundation", StringComparison.Ordinal)) + { + bootstrapFoundationRequests++; + Assert.DoesNotContain("InnoNativeTarget", globals.Keys); + Assert.DoesNotContain("BuildingInsideVisualStudio", globals.Keys); + return; + } + if (!string.IsNullOrEmpty(project.TargetNames) + || !string.Equals(Path.GetFileName(artifacts), "managed", StringComparison.Ordinal)) + return; + sharedFoundationRequests++; + Assert.True(globals.TryGetValue("InnoNativeTarget", out string? target)); + Assert.Equal("fixture-target", target); + Assert.True(globals.TryGetValue("BuildingInsideVisualStudio", out string? insideIde)); + Assert.Equal("false", insideIde); + }; events.TaskStarted += ( _, task @@ -83,6 +177,8 @@ string binlog compilations++; }; events.Replay(binlog); + Assert.True(sharedFoundationRequests > 0, "The real shared Foundation graph was not exercised."); + Assert.True(bootstrapFoundationRequests > 0, "The neutral bootstrap output owner was not exercised."); Assert.True(compilations == 1, $"Expected one bootstrap Csc task, observed {compilations}.\n{output}"); string[] runtimeReports = output.Split('\n').Select(static line => line.Trim()) .Where(static line => line.StartsWith("INNO-TASK-RUNTIME ", StringComparison.Ordinal)).ToArray(); @@ -94,8 +190,10 @@ string binlog return owner; } - private static async Task<(string output, string binlog)> Build(string root) - { + private static async Task<(string output, string binlog)> Build( + string root, + string configuration + ) { string dotnet = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); var start = new ProcessStartInfo(dotnet) @@ -109,7 +207,8 @@ string binlog string binlog = Path.Combine(root, Guid.NewGuid().ToString("N") + ".binlog"); foreach (string argument in new[] { - "build", "Root/Root.csproj", "--disable-build-servers", "--nologo", "-m:2", "-nodeReuse:false", + "build", "Root/Root.csproj", "--configuration", configuration, "--disable-build-servers", "--nologo", "-m:2", "-nodeReuse:false", + "-p:InnoNativeTarget=fixture-target", "-p:InnoTaskArtifacts=" + Path.Combine(root, "tasks"), "-p:ArtifactsPath=" + Path.Combine(root, "managed"), "-bl:" + binlog From e23f4ba5b8e836264ec5a0f805707243c27ac5f6 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 07:35:48 +0800 Subject: [PATCH 13/54] fix(assets): verify writable source content before reusing imports --- docs/assets/Inno.Assets.Pipeline.md | 2 + .../Importing/AssetLoader.Dependencies.cs | 14 ++++--- .../Importing/AssetLoader.Loading.cs | 19 ++++++++- .../Importing/AssetLoader.SourceIO.cs | 34 +++++++++++++--- .../AssetLoaderTests.cs | 40 +++++++++++++++++-- 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/docs/assets/Inno.Assets.Pipeline.md b/docs/assets/Inno.Assets.Pipeline.md index 4e4465da4..a27249fc7 100644 --- a/docs/assets/Inno.Assets.Pipeline.md +++ b/docs/assets/Inno.Assets.Pipeline.md @@ -82,6 +82,8 @@ Authoring 启动与 Rescan 时,当前 source 的 `.imeta` 是“路径属于 运行时导出同时校验创作依赖:沿 Artifact 依赖递归检查当前导入状态和源指纹(包括 include/Source 输入)。Editor 可以继续使用 last-good,但失败、缺失或过期的必需创作输入不能认证 Player 构建。导出不在中途重导入,以免已编译的目标产物与新资产混用 generation;拒绝时报告完整依赖路径,需重导入后重新构建。 +可写创作源的路径 Load 和 Source 依赖重新验证实际内容,不把相同长度或修改时间当作内容未变的证明。验证使用标准库流式 SHA-256,避免只为检查有效性分配完整源文件字节数组。源被锁定或拒绝访问时走既有导入失败与 last-good 协议:保留可用产物、公开失败诊断,不能把它认证为当前成功输入;解除阻塞后再次 Load 可恢复。只读安装 mount 由已验证的不可变 generation 提供内容身份,仍可复用其有效 stamp。持久 ID、公开签名和源 sidecar 格式不变;这项完整性修复增加必要的源读取,不宣称为零成本优化。 + ### Source Mount 候选与共同 Recovery `PrepareSourceMounts` 返回未发布的 `AssetSourceMountTransaction`。`sourceMounts`、 diff --git a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs index 9c7e6d2f7..a1f51f645 100644 --- a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs +++ b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Dependencies.cs @@ -307,12 +307,16 @@ out bool metadataChanged string sourcePath = GetSourcePath(NormalizeRelativePath(dependency.key)); if (!AssetSourceFileStamp.TryCapture(sourcePath, out AssetSourceFileStamp sourceStamp)) return "MISSING"; - if (SourceStampMatches(dependency, sourceStamp)) + bool stampChanged = !SourceStampMatches(dependency, sourceStamp); + if (GetMount(dependency.key).isReadOnly && !stampChanged) return dependency.fingerprint; - byte[] sourceBytes = ReadStableSourceBytes(sourcePath, out sourceStamp); - ApplySourceStamp(ref dependency, sourceStamp); - metadataChanged = true; - return ComputeSha256Hex(sourceBytes); + string hash = ReadStableSourceHash(sourcePath, out sourceStamp); + if (stampChanged) + { + ApplySourceStamp(ref dependency, sourceStamp); + metadataChanged = true; + } + return hash; } case AssetImportDependencyKind.Artifact: return Guid.TryParse(dependency.key, out Guid id) && diff --git a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs index a710fc460..3e8bf4f1d 100644 --- a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs +++ b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs @@ -36,8 +36,13 @@ sealed partial class AssetLoader /// The required assignable asset type. /// /// - /// The canonical asset, or when unavailable or incompatible. + /// The canonical asset, including retained last-good content after a diagnosed import failure, + /// or when unavailable or incompatible. /// + /// + /// Writable source content is verified independently of timestamps. Temporarily blocked source + /// verification follows the existing failed-import protocol; it does not certify cached content as current. + /// public AssetObject? Load( AssetPath path, Type requestedAssetType @@ -256,7 +261,17 @@ Type requestedAssetType bool stale = false; bool catalogChanged = false; if (record is not null && sourceExists) - stale = IsStale(record, out catalogChanged); + { + try + { + stale = IsStale(record, out catalogChanged); + } + catch (Exception failure) when (failure is IOException or UnauthorizedAccessException) + { + // Import owns failure diagnostics and the retained last-good artifact. + stale = true; + } + } if (catalogChanged && !stale) CommitCatalogLocked(); if (record is null || stale) diff --git a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceIO.cs b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceIO.cs index 874376486..8d62ba4be 100644 --- a/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceIO.cs +++ b/src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.SourceIO.cs @@ -66,18 +66,21 @@ out bool catalogChanged return true; if (!AssetSourceFileStamp.TryCapture(sourcePath, out AssetSourceFileStamp sourceStamp)) return true; - if (!SourceStampMatches(record.meta, sourceStamp)) + bool stampChanged = !SourceStampMatches(record.meta, sourceStamp); + if (!GetMount(record.relativePath).isReadOnly || stampChanged) { - byte[] sourceBytes = ReadStableSourceBytes(sourcePath, out sourceStamp); if (!string.Equals( record.meta.sourceHash, - ComputeSha256Hex(sourceBytes), + ReadStableSourceHash(sourcePath, out sourceStamp), StringComparison.Ordinal)) { return true; } - ApplySourceStamp(record.meta, sourceStamp); - catalogChanged = true; + if (stampChanged) + { + ApplySourceStamp(record.meta, sourceStamp); + catalogChanged = true; + } } for (int i = 0; i < record.meta.importDependencies.Length; i++) { @@ -309,6 +312,27 @@ private AssetSourceMount GetMount(AssetPath path) private string GetMetaPath(string relativePath) => GetSourcePath(relativePath) + C_META_POSTFIX; + private static string ReadStableSourceHash( + string sourcePath, + out AssetSourceFileStamp sourceStamp + ) { + const int C_MAX_ATTEMPTS = 3; + for (int attempt = 0; attempt < C_MAX_ATTEMPTS; attempt++) + { + if (!AssetSourceFileStamp.TryCapture(sourcePath, out AssetSourceFileStamp before)) + throw new FileNotFoundException("Asset source is unavailable.", sourcePath); + using FileStream source = IOFile.OpenRead(sourcePath); + string hash = Convert.ToHexString(SHA256.HashData(source)); + if (AssetSourceFileStamp.TryCapture(sourcePath, out AssetSourceFileStamp after) + && before == after && source.Length == after.length) + { + sourceStamp = after; + return hash; + } + } + throw new IOException($"Asset source '{sourcePath}' did not remain stable while its content was verified."); + } + private static byte[] ReadStableSourceBytes( string sourcePath, out AssetSourceFileStamp sourceStamp diff --git a/tests/assets/Inno.Assets.Pipeline.Tests/AssetLoaderTests.cs b/tests/assets/Inno.Assets.Pipeline.Tests/AssetLoaderTests.cs index 02ee41abc..8c39415f8 100644 --- a/tests/assets/Inno.Assets.Pipeline.Tests/AssetLoaderTests.cs +++ b/tests/assets/Inno.Assets.Pipeline.Tests/AssetLoaderTests.cs @@ -1050,7 +1050,7 @@ public void PathAndPersistentIdLoads_ReturnCanonicalInstance() } [Fact] - public void CurrentCatalogStamp_LoadsArtifactWithoutOpeningSourceContent() + public void BlockedSourceVerificationRetainsLastGoodArtifactAndReportsImportFailure() { using TestWorkspace workspace = new(); workspace.WriteText("Text/cached.txt", "cached"); @@ -1068,6 +1068,9 @@ public void CurrentCatalogStamp_LoadsArtifactWithoutOpeningSourceContent() loader.Load(AssetPath.Project("Text/cached.txt"), typeof(TextAsset))); Assert.Equal("cached", asset.content); + Assert.True(loader.TryGetInfo(AssetPath.Project("Text/cached.txt"), out AssetInfo? info)); + Assert.Equal(AssetImportStatus.Failed, info!.status); + Assert.Contains(info.diagnostics, diagnostic => diagnostic.Contains("IOException", StringComparison.Ordinal)); } [Fact] @@ -1092,7 +1095,7 @@ public void ChangedStampWithEqualContent_DoesNotReimportCanonicalAsset() } [Fact] - public void CurrentDependencyStamp_DoesNotOpenSourceDependencyContent() + public void BlockedDependencyVerificationRetainsLastGoodArtifactAndReportsImportFailure() { using TestWorkspace workspace = new(); workspace.WriteText("Import/schema.inc", "schema"); @@ -1109,6 +1112,8 @@ public void CurrentDependencyStamp_DoesNotOpenSourceDependencyContent() loader.Load(AssetPath.Project("Import/root.importgraph"), typeof(ImportGraphAsset))); Assert.False(asset.isMissing); + Assert.True(loader.TryGetInfo(AssetPath.Project("Import/root.importgraph"), out AssetInfo? info)); + Assert.Equal(AssetImportStatus.Failed, info!.status); } [Fact] @@ -1136,7 +1141,32 @@ public async Task ConcurrentAsyncLoads_ShareImportAndCancellationOnlyCancelsOneW } [Fact] - public void StaleSource_ReimportsAndUpdatesCanonicalInstanceInPlace() + public void ChangedDependencyWithPreservedTimestampReimportsTheCanonicalAsset() + { + using TestWorkspace workspace = new(); + workspace.WriteText("Import/schema.inc", "first"); + workspace.WriteText("Import/root.importgraph", "Import/schema.inc"); + using var loader = workspace.CreateLoader(m_types, m_serialization, m_identities, m_diagnostics, m_logs); + ImportGraphAsset before = Assert.IsType( + loader.Load(AssetPath.Project("Import/root.importgraph"), typeof(ImportGraphAsset))); + long version = before.contentVersion; + string dependency = workspace.SourcePath("Import/schema.inc"); + DateTime timestamp = System.IO.File.GetLastWriteTimeUtc(dependency); + workspace.WriteText("Import/schema.inc", "other"); + System.IO.File.SetLastWriteTimeUtc(dependency, timestamp); + + ImportGraphAsset after = Assert.IsType( + loader.Load(AssetPath.Project("Import/root.importgraph"), typeof(ImportGraphAsset))); + + Assert.Same(before, after); + Assert.Equal(version + 1, after.contentVersion); + Assert.False(after.isMissing); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void StaleSource_ReimportsAndUpdatesCanonicalInstanceInPlace(bool preserveTimestamp) { using TestWorkspace workspace = new(); workspace.WriteText("Text/reload.txt", "one"); @@ -1144,8 +1174,12 @@ public void StaleSource_ReimportsAndUpdatesCanonicalInstanceInPlace() TextAsset before = Assert.IsType(loader.Load(AssetPath.Project("Text/reload.txt"), typeof(TextAsset))); Guid persistentId = before.identity.persistentId; long version = before.contentVersion; + string sourcePath = workspace.SourcePath("Text/reload.txt"); + DateTime sourceTime = System.IO.File.GetLastWriteTimeUtc(sourcePath); workspace.WriteText("Text/reload.txt", "two"); + if (preserveTimestamp) + System.IO.File.SetLastWriteTimeUtc(sourcePath, sourceTime); TextAsset after = Assert.IsType(loader.Load(AssetPath.Project("Text/reload.txt"), typeof(TextAsset))); Assert.Same(before, after); From 14338d61df533f66646a82ba0f2d3184d264c003 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 07:35:48 +0800 Subject: [PATCH 14/54] fix(tooling): validate complete graphs against their SDK output owner --- .../ArchitectureCommandFixture.cs | 13 ++++++++++++- .../ArchitectureCommandTests.cs | 1 + .../Inno.Tooling.Architecture.Tests.csproj | 4 ++++ .../RepositoryArchitectureTests.cs | 12 +++++++++++- .../DocumentationSourceModels.cs | 6 ++++-- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandFixture.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandFixture.cs index 706b09f7d..8846b0c69 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandFixture.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandFixture.cs @@ -10,8 +10,19 @@ internal static class ArchitectureCommandFixture internal static (int exitCode, string output) Run( string root, params string[] arguments + ) => RunBounded(root, TimeSpan.FromMinutes(2), arguments); + + internal static (int exitCode, string output) RunComplete( + string root, + params string[] arguments + ) => RunBounded(root, TimeSpan.FromMinutes(15), arguments); + + private static (int exitCode, string output) RunBounded( + string root, + TimeSpan budget, + string[] arguments ) { - using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + using var cancellation = new CancellationTokenSource(budget); using var output = new StringWriter(CultureInfo.InvariantCulture); TextWriter previousOutput = Console.Out; TextWriter previousError = Console.Error; diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs index a1b68e063..40a1887e5 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs @@ -88,6 +88,7 @@ string expected Assert.Contains(expected, output, StringComparison.Ordinal); Assert.True(File.Exists(graph)); Assert.False(Directory.Exists(Path.Combine(project, "bin"))); + Assert.False(Directory.Exists(Path.Combine(project, "obj"))); } finally { diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj index 1263262ea..c07476929 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj @@ -10,6 +10,10 @@ + + + + diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs index 5404e8855..9eee4f5ae 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs @@ -1,4 +1,8 @@ using Inno.Testing.Source; +using Inno.Build.Toolchains; +using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using Xunit; @@ -13,7 +17,13 @@ public void CurrentRepositoryPassesArchitectureRules() { string root = TestRepository.GetRoot(typeof(RepositoryArchitectureTests).Assembly); string configuration = typeof(RepositoryArchitectureTests).Assembly.GetCustomAttribute()!.Configuration; - (int exitCode, string output) = ArchitectureCommandFixture.Run(root, "--configuration", configuration); + string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + string? artifacts = typeof(RepositoryArchitectureTests).Assembly.GetCustomAttributes() + .SingleOrDefault(static item => item.Key == "Inno.Testing.ArtifactsPath")?.Value; + var arguments = new List { "--configuration", configuration, "--dotnet", dotnet }; + if (!string.IsNullOrWhiteSpace(artifacts)) + arguments.AddRange(["--artifacts-path", artifacts]); + (int exitCode, string output) = ArchitectureCommandFixture.RunComplete(root, arguments.ToArray()); Assert.True(exitCode == 0, output); } } diff --git a/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs b/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs index 5712cc72b..f1c60977b 100644 --- a/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs +++ b/tools/Inno.Tooling.Architecture/DocumentationSourceModels.cs @@ -45,7 +45,7 @@ internal DocumentationSourceModels( if (m_projectReferences is not null) { m_references = []; - m_frameworkReferences = m_projectReferences.GetFrameworkReferences(cancellationToken); + m_frameworkReferences = []; return; } var paths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -108,7 +108,9 @@ internal DocumentationSourceModels( internal bool IsExcludedProduct(string projectDirectory) => m_projectReferences?.IsExcludedProduct(Directory.EnumerateFiles(projectDirectory, "*.csproj").Single()) == true; - internal IReadOnlyList frameworkReferences => m_frameworkReferences; + internal IReadOnlyList frameworkReferences => m_projectReferences is null + ? m_frameworkReferences + : m_projectReferences.GetFrameworkReferences(m_cancellation); internal SemanticModel? GetModel(string path) { From 081f41f917be7d1f4d33a6848d1fb69bfb68abf7 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 07:35:48 +0800 Subject: [PATCH 15/54] fix(testing): keep temporary SDK work independent of result paths --- .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 2 ++ tests/Invoke-EditorUiAcceptance.ps1 | 2 ++ tests/Invoke-IsolatedTests.ps1 | 19 +++++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index f35776bde..988c17d41 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -97,4 +97,6 @@ Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒 变更必须同步源码、调用方、项目引用、公开 XML、Wiki 和适用测试。使用真实公开契约与可替换边界,不使用 IVT、私有反射或测试后门。 +`tests/Invoke-EditorUiAcceptance.ps1` 编排既有 xUnit/VSTest,按 Contracts、Workflows、Visual、Lifecycle、Performance、Products 分组。每次保留独立 SDK、源码身份、TRX 和结果摘要;空发现不能视为成功。`-ArtifactsPath` 与 `-ResultsDirectory` 指定输出 owner;需要把大量临时文件放到其他卷时,使用 `-TemporaryDirectory` 明确指定较短的临时根。默认保留操作系统的 TEMP/TMP,不把它们隐式嵌套到深层结果目录。该参数同样传递给隔离测试入口,普通测试与独立进程使用同一临时 owner。 + 最终报告分别评价架构、组织、状态与生命周期、抽象、复用、样式、扩展、交互、Undo/Redo、菜单和颗粒度;每项列出证据与限制。目录整齐、测试数量或正常启动不能代替视觉、输入、重复 reload、资源收敛和成对性能验收。 diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 index a2c044a3d..5b1689efc 100644 --- a/tests/Invoke-EditorUiAcceptance.ps1 +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -5,6 +5,7 @@ param( [string]$Dotnet = 'dotnet', [Parameter(Mandatory = $true)][string]$ResultsDirectory, [Parameter(Mandatory = $true)][string]$ArtifactsPath, + [string]$TemporaryDirectory, [string]$ProductProject, [string[]]$GraphicsApis = @(), [string]$BackendPattern = 'BGFX', @@ -110,6 +111,7 @@ foreach ($selected in $profiles) { & (Join-Path $PSScriptRoot 'Invoke-IsolatedTests.ps1') -Dotnet $Dotnet -Solution $selection ` -Configuration $Configuration -ResultsDirectory $stageRoot -ArtifactsPath $artifacts ` -Target $product.InnoNativeTarget -ProductProperties $product.InnoProductBuildProperties -Filter $filter ` + -TemporaryDirectory $TemporaryDirectory ` *> (Join-Path $stageRoot 'execution.log') $executed = 0 $observedSuites = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) diff --git a/tests/Invoke-IsolatedTests.ps1 b/tests/Invoke-IsolatedTests.ps1 index 5d66a8efb..c002029ca 100644 --- a/tests/Invoke-IsolatedTests.ps1 +++ b/tests/Invoke-IsolatedTests.ps1 @@ -6,6 +6,7 @@ param( [string]$ProductProperties, [string]$ArtifactsPath, [string]$Filter, + [string]$TemporaryDirectory, [string]$Solution = 'InnoEngine.sln' ) @@ -22,12 +23,18 @@ $repositoryRoot = Split-Path $PSScriptRoot -Parent $solutionPath = [System.IO.Path]::GetFullPath($Solution, $repositoryRoot) $resultsRoot = [System.IO.Path]::GetFullPath($ResultsDirectory, $repositoryRoot) New-Item -ItemType Directory -Force -Path $resultsRoot | Out-Null -$processTemp = Join-Path $resultsRoot 'process-temp' -New-Item -ItemType Directory -Force -Path $processTemp | Out-Null -$tempEnvironment = @('--environment', "TMP=$processTemp", '--environment', "TEMP=$processTemp", - '--environment', "TMPDIR=$processTemp") -$sdkEnvironment += $tempEnvironment -$assemblyEnvironment += $tempEnvironment +if ($TemporaryDirectory) { + $temporaryOwner = [IO.Path]::GetFullPath($TemporaryDirectory, $repositoryRoot) + New-Item -ItemType Directory -Force -Path $temporaryOwner | Out-Null + $processTemp = Join-Path $temporaryOwner ([Guid]::NewGuid().ToString('N').Substring(0, 12)) + New-Item -ItemType Directory -Path $processTemp | Out-Null + @{ directory = $processTemp; purpose = 'Owned temporary files for the selected test processes.' } | + ConvertTo-Json | Set-Content -LiteralPath (Join-Path $resultsRoot 'temporary-owner.json') -Encoding utf8 + $tempEnvironment = @('--environment', "TMP=$processTemp", '--environment', "TEMP=$processTemp", + '--environment', "TMPDIR=$processTemp") + $sdkEnvironment += $tempEnvironment + $assemblyEnvironment += $tempEnvironment +} $checks = [System.Collections.Generic.List[object]]::new() function Add-CheckResult( From af6eac3153219c2457f9b187e332a1126952fb11 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 07:52:22 +0800 Subject: [PATCH 16/54] fix(testing): require explicit product initialization expectations --- docs/architecture/EDITOR_UI_DESIGN_STANDARD.md | 2 +- tests/Invoke-EditorUiAcceptance.ps1 | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index 988c17d41..0050dad56 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -97,6 +97,6 @@ Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒 变更必须同步源码、调用方、项目引用、公开 XML、Wiki 和适用测试。使用真实公开契约与可替换边界,不使用 IVT、私有反射或测试后门。 -`tests/Invoke-EditorUiAcceptance.ps1` 编排既有 xUnit/VSTest,按 Contracts、Workflows、Visual、Lifecycle、Performance、Products 分组。每次保留独立 SDK、源码身份、TRX 和结果摘要;空发现不能视为成功。`-ArtifactsPath` 与 `-ResultsDirectory` 指定输出 owner;需要把大量临时文件放到其他卷时,使用 `-TemporaryDirectory` 明确指定较短的临时根。默认保留操作系统的 TEMP/TMP,不把它们隐式嵌套到深层结果目录。该参数同样传递给隔离测试入口,普通测试与独立进程使用同一临时 owner。 +`tests/Invoke-EditorUiAcceptance.ps1` 编排既有 xUnit/VSTest,按 Contracts、Workflows、Visual、Lifecycle、Performance、Products 分组。每次保留独立 SDK、源码身份、TRX 和结果摘要;空发现不能视为成功。Products 必须显式提供平台产品的 `-GraphicsApis` 和启动日志期望 `-BackendPattern`;共享入口不内嵌 backend 名称作为默认断言。`-ArtifactsPath` 与 `-ResultsDirectory` 指定输出 owner;需要把大量临时文件放到其他卷时,使用 `-TemporaryDirectory` 明确指定较短的临时根。默认保留操作系统的 TEMP/TMP,不把它们隐式嵌套到深层结果目录。该参数同样传递给隔离测试入口,普通测试与独立进程使用同一临时 owner。 最终报告分别评价架构、组织、状态与生命周期、抽象、复用、样式、扩展、交互、Undo/Redo、菜单和颗粒度;每项列出证据与限制。目录整齐、测试数量或正常启动不能代替视觉、输入、重复 reload、资源收敛和成对性能验收。 diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 index 5b1689efc..77dbea95f 100644 --- a/tests/Invoke-EditorUiAcceptance.ps1 +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -8,7 +8,7 @@ param( [string]$TemporaryDirectory, [string]$ProductProject, [string[]]$GraphicsApis = @(), - [string]$BackendPattern = 'BGFX', + [string]$BackendPattern, [switch]$NoBuild ) @@ -70,6 +70,10 @@ foreach ($selected in $profiles) { Save-Result $selected 'Blocked' 'Supply the explicit graphics API matrix for the selected product using -GraphicsApis.' continue } + if (-not $BackendPattern) { + Save-Result $selected 'Blocked' 'Supply the product initialization expectation using -BackendPattern.' + continue + } if (-not $NoBuild) { & $Dotnet build $entry -c $Configuration @buildArguments *> (Join-Path $stageRoot 'build.log') if ($LASTEXITCODE -ne 0) { throw 'The official Editor build failed.' } From e053be5b1de0ece46df91619711232eb54d5fd82 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 09:50:19 +0800 Subject: [PATCH 17/54] fix(testing): honor evaluated SDK output owners --- docs/testing/Inno.Testing.Source.md | 3 +- tests/RepositoryInput.targets | 7 ++ .../Inno.Build.Tests/RepositoryWikiTests.cs | 21 +++- .../TaskRegistrationBuildTests.cs | 6 +- .../Inno.Testing.Source/TestRepository.cs | 30 ++++- .../Inno.Tooling.Architecture.Tests.csproj | 4 - .../RepositoryArchitectureTests.cs | 4 +- .../TestRepositoryInputsTests.cs | 103 ++++++++++++++++++ 8 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 tests/tooling/Inno.Tooling.Architecture.Tests/TestRepositoryInputsTests.cs diff --git a/docs/testing/Inno.Testing.Source.md b/docs/testing/Inno.Testing.Source.md index 748c80f14..4b69b95f3 100644 --- a/docs/testing/Inno.Testing.Source.md +++ b/docs/testing/Inno.Testing.Source.md @@ -6,13 +6,14 @@ ## 职责与边界 -只为需要真实源码的测试解析显式 checkout 输入,不进入生产或发布闭包。读取器只有这个程序集作为源码 owner;消费项目通过 `tests/RepositoryInput.targets` 声明 `Inno.Testing.RepositoryRoot` assembly metadata 和项目引用,不重复编译共享 C# 文件。 +为需要真实源码和 SDK 输出的测试解析显式输入,不进入生产或发布闭包。消费项目通过 `tests/RepositoryInput.targets` 声明 checkout metadata 和项目引用,不重复编译共享 C# 文件。输出 owner 在 SDK 完成路径求值后、生成程序集属性前记录,涵盖显式 `ArtifactsPath` 与 `UseArtifactsOutput`;普通输出不伪造独立 owner。 ## 公开 API | API | 语义与失败 | | --- | --- | | `TestRepository.GetRoot(Assembly testAssembly)` | 返回所属测试程序集明确声明的绝对 checkout。空程序集抛出 `ArgumentNullException`;缺失、重复、相对路径或没有引擎 Solution 标记的声明抛出 `InvalidOperationException`。不猜测执行目录、进程当前目录或其他 checkout。 | +| `TestRepository.GetArtifactsPath(Assembly testAssembly)` | 返回编译该测试程序集时 SDK 选定的绝对输出 owner;没有独立输出声明时返回 null,继续使用正常 SDK 输出求值。空程序集、重复、空或相对声明明确失败,不从旧 bin/obj 猜测。 | ## 使用与生命周期 diff --git a/tests/RepositoryInput.targets b/tests/RepositoryInput.targets index 4a72b7c79..81bc8ee98 100644 --- a/tests/RepositoryInput.targets +++ b/tests/RepositoryInput.targets @@ -3,4 +3,11 @@ + + + + + + + diff --git a/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs b/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs index 39948b7ef..aff3e1e7d 100644 --- a/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs +++ b/tests/build/Inno.Build.Tests/RepositoryWikiTests.cs @@ -1,5 +1,6 @@ using Inno.Testing.Source; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -91,7 +92,8 @@ public void LogicalEditorReferencesIncludeTheSharedProductFeatures() public async Task CurrentWikiCompilesWithTheCurrentLogicalScriptContracts() { string repository = TestRepository.GetRoot(typeof(RepositoryWikiTests).Assembly); - string owner = Path.Combine(repository, "artifacts", "acceptance", "wiki-gate", Guid.NewGuid().ToString("N")); + string? artifacts = TestRepository.GetArtifactsPath(typeof(RepositoryWikiTests).Assembly); + string owner = Path.Combine(artifacts ?? Path.Combine(repository, "artifacts"), "acceptance", "wiki-gate", Guid.NewGuid().ToString("N")); string project = Path.Combine(owner, "Project"); Directory.CreateDirectory(project); try @@ -106,15 +108,22 @@ public async Task CurrentWikiCompilesWithTheCurrentLogicalScriptContracts() TextWriter previousError = Console.Error; using var errors = new StringWriter(); int result; + string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + var arguments = new List + { + "verify", repository, "--configuration", configuration, "--wiki", "--wiki-target", target, + "--dotnet", dotnet, + "--wiki-api", Path.Combine(owner, "api.json"), + "--wiki-examples", Path.Combine(owner, "examples.json"), + "--wiki-runtime-reference", runtime, "--wiki-editor-reference", editor + }; + if (artifacts is not null) + arguments.AddRange(["--artifacts-path", artifacts]); try { Console.SetError(errors); result = await BuildApplication.Run( - ["verify", repository, "--configuration", configuration, "--wiki", "--wiki-target", target, - "--dotnet", ToolchainEnvironment.ResolveExecutable("dotnet"), - "--wiki-api", Path.Combine(owner, "api.json"), - "--wiki-examples", Path.Combine(owner, "examples.json"), - "--wiki-runtime-reference", runtime, "--wiki-editor-reference", editor], + arguments.ToArray(), CancellationToken.None); } finally diff --git a/tests/build/Inno.Build.Tests/TaskRegistrationBuildTests.cs b/tests/build/Inno.Build.Tests/TaskRegistrationBuildTests.cs index e9e386b04..63e43e21b 100644 --- a/tests/build/Inno.Build.Tests/TaskRegistrationBuildTests.cs +++ b/tests/build/Inno.Build.Tests/TaskRegistrationBuildTests.cs @@ -31,8 +31,10 @@ await File.WriteAllTextAsync(project, """ Exe disable enable + false + @@ -50,6 +52,7 @@ await File.WriteAllTextAsync(Path.Combine(root, "Program.cs"), """ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; + using System.Linq; using System.Text.Json; using System.Threading; using Microsoft.Build.Evaluation; @@ -98,8 +101,7 @@ await File.WriteAllTextAsync(Path.Combine(root, "Program.cs"), """ if (copies.count != 0) return 3; ProjectInstance completed = result.ProjectStateAfterBuild!; - string foundationProject = Path.Combine(args[3], "src/foundation/core/Inno.Core.IO/Inno.Core.IO.csproj"); - string foundationAssembly = projects.LoadProject(foundationProject).GetPropertyValue("TargetPath"); + string foundationAssembly = completed.GetItems("_InnoFoundationOutput").Single().EvaluatedInclude; using (FileStream writable = File.Open(foundationAssembly, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { if (writable.Length == 0) diff --git a/tests/source/Inno.Testing.Source/TestRepository.cs b/tests/source/Inno.Testing.Source/TestRepository.cs index 67fb936d8..fbeae2be5 100644 --- a/tests/source/Inno.Testing.Source/TestRepository.cs +++ b/tests/source/Inno.Testing.Source/TestRepository.cs @@ -6,11 +6,39 @@ namespace Inno.Testing.Source; /// -/// Resolves a source checkout explicitly declared by a test assembly's SDK project. +/// Resolves source and SDK output inputs explicitly declared by a test assembly's project. /// public static class TestRepository { private const string C_ROOT_METADATA = "Inno.Testing.RepositoryRoot"; + private const string C_ARTIFACTS_METADATA = "Inno.Testing.ArtifactsPath"; + + /// + /// Reads the SDK output owner recorded when the test assembly was built. + /// + /// + /// The test assembly carrying the optional evaluated artifacts path. + /// + /// + /// The declared absolute artifacts directory, or null when normal SDK output paths were selected. + /// + /// + /// The test assembly is null. + /// + /// + /// The declaration is duplicated, empty or relative. + /// + public static string? GetArtifactsPath(Assembly testAssembly) + { + ArgumentNullException.ThrowIfNull(testAssembly); + AssemblyMetadataAttribute? declaration = testAssembly.GetCustomAttributes() + .SingleOrDefault(static attribute => attribute.Key == C_ARTIFACTS_METADATA); + if (declaration is null) + return null; + if (string.IsNullOrWhiteSpace(declaration.Value) || !Path.IsPathFullyQualified(declaration.Value)) + throw new InvalidOperationException("Tests require the absolute SDK artifacts owner recorded by their project."); + return Path.GetFullPath(declaration.Value); + } /// /// Reads and validates the checkout input independently of the test assembly's output directory. diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj index c07476929..1263262ea 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/Inno.Tooling.Architecture.Tests.csproj @@ -10,10 +10,6 @@ - - - - diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs index 9eee4f5ae..1a605e83c 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/RepositoryArchitectureTests.cs @@ -2,7 +2,6 @@ using Inno.Build.Toolchains; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Xunit; @@ -18,8 +17,7 @@ public void CurrentRepositoryPassesArchitectureRules() string root = TestRepository.GetRoot(typeof(RepositoryArchitectureTests).Assembly); string configuration = typeof(RepositoryArchitectureTests).Assembly.GetCustomAttribute()!.Configuration; string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); - string? artifacts = typeof(RepositoryArchitectureTests).Assembly.GetCustomAttributes() - .SingleOrDefault(static item => item.Key == "Inno.Testing.ArtifactsPath")?.Value; + string? artifacts = TestRepository.GetArtifactsPath(typeof(RepositoryArchitectureTests).Assembly); var arguments = new List { "--configuration", configuration, "--dotnet", dotnet }; if (!string.IsNullOrWhiteSpace(artifacts)) arguments.AddRange(["--artifacts-path", artifacts]); diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/TestRepositoryInputsTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/TestRepositoryInputsTests.cs new file mode 100644 index 000000000..490d976de --- /dev/null +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/TestRepositoryInputsTests.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Linq; +using System.Collections.Generic; +using System.Text.Json; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Inno.Build.Toolchains; +using Inno.Testing.Source; +using Xunit; + +namespace Inno.Tooling.Architecture.Tests; + +public sealed class TestRepositoryInputsTests +{ + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task SdkRecordsItsEvaluatedArtifactsOwner( + bool useArtifacts, + bool explicitOwner + ) { + string root = Path.Combine(Path.GetTempPath(), "InnoTestInputs", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(root); + try + { + string repository = TestRepository.GetRoot(typeof(TestRepositoryInputsTests).Assembly); + string import = SecurityElement.Escape(Path.Combine(repository, "tests/RepositoryInput.targets")); + string project = Path.Combine(root, "Probe.csproj"); + await File.WriteAllTextAsync(project, $""" + + net10.0disable + + + """); + string dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + var arguments = new List + { + "msbuild", project, "-nologo", "-nr:false", "-t:GetAssemblyAttributes", + "-getProperty:ArtifactsPath", "-getItem:AssemblyAttribute", + "-p:UseArtifactsOutput=" + useArtifacts.ToString().ToLowerInvariant() + }; + if (explicitOwner) + arguments.Add("-p:ArtifactsPath=" + Path.Combine(root, "independent")); + using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + string output = await ToolchainEnvironment.CaptureOutputAsync(dotnet, arguments, + root, timeout.Token, DotNetSdkEnvironment.Create(dotnet)); + using JsonDocument result = JsonDocument.Parse(output); + string? artifacts = result.RootElement.GetProperty("Properties").GetProperty("ArtifactsPath").GetString(); + JsonElement[] declarations = result.RootElement.GetProperty("Items").GetProperty("AssemblyAttribute").EnumerateArray() + .Where(item => item.TryGetProperty("_Parameter1", out var key) && key.GetString() == "Inno.Testing.ArtifactsPath").ToArray(); + if (useArtifacts) + { + Assert.False(string.IsNullOrWhiteSpace(artifacts)); + Assert.Equal(Path.GetFullPath(artifacts!), Path.TrimEndingDirectorySeparator( + Assert.Single(declarations).GetProperty("_Parameter2").GetString()!)); + } + else + { + Assert.Empty(declarations); + } + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + [Fact] + public void AbsentArtifactsMetadataPreservesNormalSdkOutputSelection() + => Assert.Null(TestRepository.GetArtifactsPath(CreateAssembly())); + + [Fact] + public void ExplicitArtifactsMetadataPreservesTheRecordedAbsoluteOwner() + { + string owner = Path.Combine(Path.GetTempPath(), "owned-sdk-output"); + Assert.Equal(Path.GetFullPath(owner), TestRepository.GetArtifactsPath(CreateAssembly(owner))); + } + + [Fact] + public void RelativeEmptyOrDuplicateArtifactsDeclarationsAreRejected() + { + Assert.Throws(() => TestRepository.GetArtifactsPath(CreateAssembly("relative-owner"))); + Assert.Throws(() => TestRepository.GetArtifactsPath(CreateAssembly(string.Empty))); + string owner = Path.Combine(Path.GetTempPath(), "owned-sdk-output"); + Assert.Throws(() => TestRepository.GetArtifactsPath(CreateAssembly(owner, owner))); + Assert.Throws(() => TestRepository.GetArtifactsPath(null!)); + } + + private static Assembly CreateAssembly(params string[] owners) + { + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly( + new AssemblyName("RepositoryInputs." + Guid.NewGuid().ToString("N")), AssemblyBuilderAccess.RunAndCollect); + ConstructorInfo constructor = typeof(AssemblyMetadataAttribute).GetConstructor([typeof(string), typeof(string)])!; + foreach (string owner in owners) + assembly.SetCustomAttribute(new CustomAttributeBuilder(constructor, ["Inno.Testing.ArtifactsPath", owner])); + return assembly; + } +} From a4d975abbd17cb12b866a36a5447ea0f07ad0e52 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 09:50:20 +0800 Subject: [PATCH 18/54] fix(editor): apply settings atomically and retire failed presentation --- .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 2 + docs/editor/Inno.Editor.Panel.Settings.md | 10 +- .../Inno.Editor.Panel.Settings.csproj | 1 + .../Presentation/SettingsModal.cs | 119 ++++-- .../Runtime/OpenSettingsAction.cs | 4 + .../Runtime/SettingsEditSession.cs | 78 +++- .../Runtime/SettingsWindowModule.cs | 45 ++- .../Inno.Editor.PlayMode.Tests.csproj | 12 + .../NativeProjectPanelHost.cs | 30 +- .../SettingsApplyNativeTests.cs | 340 ++++++++++++++++++ .../SettingsPanelServices.cs | 71 ++++ 11 files changed, 662 insertions(+), 50 deletions(-) create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/SettingsApplyNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/SettingsPanelServices.cs diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index 0050dad56..8105eb417 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -99,4 +99,6 @@ Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒 `tests/Invoke-EditorUiAcceptance.ps1` 编排既有 xUnit/VSTest,按 Contracts、Workflows、Visual、Lifecycle、Performance、Products 分组。每次保留独立 SDK、源码身份、TRX 和结果摘要;空发现不能视为成功。Products 必须显式提供平台产品的 `-GraphicsApis` 和启动日志期望 `-BackendPattern`;共享入口不内嵌 backend 名称作为默认断言。`-ArtifactsPath` 与 `-ResultsDirectory` 指定输出 owner;需要把大量临时文件放到其他卷时,使用 `-TemporaryDirectory` 明确指定较短的临时根。默认保留操作系统的 TEMP/TMP,不把它们隐式嵌套到深层结果目录。该参数同样传递给隔离测试入口,普通测试与独立进程使用同一临时 owner。 +Settings 的一次 Apply 复用单一 History transaction,所有 dirty owner 成功后才接受新基线。失败先尝试回滚并保留草稿;可恢复的文件/校验错误在窗口内反馈并记录完整诊断。History 完整性丢失后禁止继续 Apply,不能把错误吞掉后重试。打开的设置窗口借用当前 generation 的编辑对象,使用现有 admission guard 延迟 reload,关闭后恢复许可;不为此建立另一套状态恢复或 History 系统。 + 最终报告分别评价架构、组织、状态与生命周期、抽象、复用、样式、扩展、交互、Undo/Redo、菜单和颗粒度;每项列出证据与限制。目录整齐、测试数量或正常启动不能代替视觉、输入、重复 reload、资源收敛和成对性能验收。 diff --git a/docs/editor/Inno.Editor.Panel.Settings.md b/docs/editor/Inno.Editor.Panel.Settings.md index 2c82e18ac..3d8f22de8 100644 --- a/docs/editor/Inno.Editor.Panel.Settings.md +++ b/docs/editor/Inno.Editor.Panel.Settings.md @@ -23,17 +23,20 @@ Settings 专用字段底色与内边距由内部 `Styling/SettingsStyle` 维护 - 任何拥有子页面的非叶页面都会在 description 下方自动显示相同的子页面跳转列表,即使该页面自身同时拥有设置字段;字段仍从跳转列表下方开始,不要求每个页面重复编写导航 UI。 - 右侧每个完整字段使用自动内容行高;label/content/reset 保持对齐,连续行之间没有空隙。Reset 固定宽度扣除后,label/content 使用共享 2:3 比例,长标签在本列换行。Field Table 严格使用 page 的真实 content width,不通过负 cursor 或扩大 table width 穿透 padding,因此不会污染 `CursorMaxPos` 或产生虚假水平滚动范围。左右 gutter 背景作为不参与 layout 的 draw-list geometry 延伸至内容窗口边缘,文字与控件继续使用正常 window/cell inset。两种背景使用轻微明度差和固定 `0.005` alpha,只辅助辨认连续字段而不形成明显色块。 - 合成页面不需要中央 page 注册;frontend 根据 slash-delimited path 自动补齐祖先。 -- Catalog generation 改变时丢弃旧 staged generation,按新 definitions 原子重建窗口 session。 +- 窗口借用当前 generation 的 definitions 和隔离编辑值。打开期间通过既有 reload admission guard 延迟脚本请求与激活;关闭后恢复许可,不在后台丢弃未提交草稿。导航只跟踪中立 session revision,不保存上一次编辑 Session。 ## 单一 Apply 操作 底部根据 staged state 使用两种互斥状态:没有变化时只显示右对齐的 `OK`;存在变化时显示右对齐的 `Cancel` 与 `Apply`,其中 `Cancel` 在左、`Apply` 在右: -- `Apply` 一次提交所有 dirty scope;Editor、Project、Build 分别原子写入 `Settings.Editor.inno`、`Settings.Project.inno`、`Settings.Build.inno`,并形成各自的 History entry。 +- `Apply` 使用同一个既有 History transaction 提交所有 dirty scope;三个 owner 分别原子写入自己的文件,全部成功后形成一个 `Apply Settings` Undo/Redo entry,才更新草稿基线。 - `Cancel` 同时丢弃三个域尚未 Apply 的隔离 staged 值并关闭窗口。 - `OK` 仅在 session clean 时关闭窗口,不执行无意义的持久化。 - `Apply` 仅在 staged effective value 相对窗口打开或上次 Apply 的基线真正变化时启用。把一次未提交修改 Reset 回原始默认值不会留下虚假的 reset intent。 -- 三个持久化域仍分别保证各自的原子写入与 History;单一按钮不把三个文件伪装成一个跨文件事务。 +- 任一写入或 History 记录失败时,通过既有事务回滚本次已应用修改。普通文件或校验失败留在窗口内显示,草稿可重试或取消,完整诊断进入 Console,不因此隔离整个 Modal。 +- 回滚无法证明状态完整时保留原始与回滚诊断,遵守 History 的 Faulted 状态,禁用 Apply 并明确要求重启。关闭失败前不会释放仍承担恢复的 Session/transaction owner。 +- 意外 Drawer/呈现异常仍由既有 Registry 隔离;先取消窗口的临时编辑 owner,释放正常 reload admission。Settings 动作明确禁用并说明需恢复扩展;catalog 更新后才能重开。不会留下已不可操作、却永久阻塞重载的 Modal。 +- 这是进程内的领域回滚和单一 History 操作;三个文件仍是各自原子写入,不宣称断电或进程崩溃期间具有跨文件提交保证。 Reset Editor 恢复字段定义的 `defaultValue`。Reset Project 删除项目 override,并恢复 Host 默认值与依赖有序 Plugin 默认贡献的合成结果。Reset Build 恢复由项目名、host target 和首个已导入 Scene 建立的 project-derived 默认值。页面顶部 Reset 会递归作用于当前页面所有后代字段,但提交仍遵循各自数据域。 @@ -120,6 +123,7 @@ Settings frontend 不内建 Boolean、Layer、Tag、PBR 或其他业务字段类 | 工程 | 边界 | | --- | --- | +| `Inno.Core.Logging` | 实现依赖;不传递 compile surface | | `Inno.Build` | 实现依赖;不传递 compile surface | | `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | | `Inno.Editor.Core` | 实现依赖;不传递 compile surface | diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj index e8dd281b5..21f6e44c2 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Inno.Editor.Panel.Settings.csproj @@ -10,6 +10,7 @@ + diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs index 256645cd5..38b33cbd6 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsModal.cs @@ -1,13 +1,12 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; -using EditorUI = Inno.Editor.ImGui.EditorUiSession; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; +using System.IO; using System.Collections.Generic; using System.Numerics; using Inno.Editor.Core; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; +using Inno.Core.Logging; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; @@ -15,14 +14,17 @@ namespace Inno.Editor.Panel.Settings; internal sealed class SettingsModal( EditorSettings editorSettings, ProjectSettingsEditor projectSettings, - SettingsWindowModule window + SettingsWindowModule window, + LogRouter logs ) : EditorModal { private readonly SettingsNavigation m_navigation = new(); private readonly SettingsTree m_tree = new(); - private SettingsEditSession? m_navigationSession; + private long m_navigationRevision = -1; private string m_query = string.Empty; private float m_treePaneRatio = 0.25f; + private readonly Logger m_log = logs.CreateLogger(); + private string m_applyError = string.Empty; /// /// Gets whether this implementation is visible. /// @@ -55,6 +57,26 @@ SettingsWindowModule window /// The context that supplies state and services for this operation. /// protected override void OnDraw(EditorContext context) + { + try + { + DrawWindow(); + } + catch (Exception failure) + { + try + { + window.FailPresentation(); + } + catch (Exception retirementFailure) + { + throw new AggregateException("Settings presentation and its owned retirement failed.", failure, retirementFailure); + } + throw; + } + } + + private void DrawWindow() { SettingsEditSession? session = window.session; if (session is null) @@ -68,15 +90,25 @@ protected override void OnDraw(EditorContext context) } IReadOnlyList pages = session.pages; - EnsureNavigationSession(session, pages); + EnsureNavigationSession(pages); SettingsPage? selected = SettingsTree.FindPage(pages, m_navigation.currentPath); selected ??= SettingsTree.FindFirstMatch(pages, m_query); if (selected is not null) m_navigation.Replace(selected.path); - float footerHeight = Ui.GetFrameHeightWithSpacing() + UiDrawing.GetLayout().itemSpacing.Y; + float footerHeight = Ui.GetFrameHeightWithSpacing() + Ui.GetLayout().itemSpacing.Y; + if (m_applyError.Length != 0) + footerHeight += Ui.GetFrameHeightWithSpacing(); Vector2 available = Ui.GetContentRegionAvailable(); DrawBody(session, pages, new Vector2(available.X, MathF.Max(1f, available.Y - footerHeight))); Ui.Separator(); + if (m_applyError.Length != 0) + { + EditorUiSession.current.widgets.ColoredText(EditorPalette.error, + session.isHistoryFaulted ? "History integrity was lost. Restart the Editor." + : session.canApply ? "Apply failed. Edits remain staged; retry or Cancel." + : "Settings recovery is required. See Console for details."); + EditorUiSession.current.widgets.DrawItemTooltip(m_applyError); + } DrawButtons(session); } @@ -86,7 +118,7 @@ private void DrawBody( Vector2 size ) { ImGuiTableFlags flags = ImGuiTableFlags.NoPadOuterX | ImGuiTableFlags.NoKeepColumnsVisible | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings; - float splitterWidth = MathF.Max(5f * EditorUI.current.widgets.style.zoom, UiDrawing.GetLayout().dockingSeparatorSize); + float splitterWidth = MathF.Max(5f * EditorUiSession.current.widgets.style.zoom, Ui.GetLayout().dockingSeparatorSize); float treeWidth = ResolveTreeWidth(size.X, splitterWidth); Ui.PushStyleVar(ImGuiStyleVar.CellPadding, Vector2.Zero); bool tableStarted = false; @@ -130,10 +162,10 @@ float height if (!visible) return; DrawNavigationButtons(); - Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarSectionSpacing); + Ui.SameLine(0f, EditorUiSession.current.widgets.style.toolbarSectionSpacing); bool wasSearching = !string.IsNullOrWhiteSpace(m_query); Ui.SetNextItemWidth(-1f); - bool queryChanged = EditorUI.current.widgets.SearchInput("settings", "Search settings", ref m_query, width: -1f); + bool queryChanged = EditorUiSession.current.widgets.SearchInput("settings", "Search settings", ref m_query, width: -1f); if (queryChanged) { SettingsPage? first = SettingsTree.FindFirstMatch(pages, m_query); @@ -228,7 +260,7 @@ float height Vector2 maximum = Ui.GetItemMaximum(); System.Numerics.Vector4 color = active ? EditorPalette.contentAccent : hovered ? EditorPalette.contentBorder : EditorPalette.contentBorderSoft; float centerX = (minimum.X + maximum.X) * 0.5f; - EditorImGui.DrawLine(new Vector2(centerX, minimum.Y), new Vector2(centerX, maximum.Y), Ui.ToPackedColor(color), EditorUI.current.widgets.style.borderSize); + Ui.DrawLine(new Vector2(centerX, minimum.Y), new Vector2(centerX, maximum.Y), Ui.ToPackedColor(color), EditorUiSession.current.widgets.style.borderSize); } private float ResolveTreeWidth( @@ -243,20 +275,20 @@ private static float ClampTreeWidth( float requested, float usable ) { - float minimum = MathF.Min(EditorUI.current.widgets.style.paneMinimumVisibleWidth, usable * 0.5f); + float minimum = MathF.Min(EditorUiSession.current.widgets.style.paneMinimumVisibleWidth, usable * 0.5f); float maximum = MathF.Max(minimum, usable - minimum); return Math.Clamp(requested, minimum, maximum); } private void DrawButtons(SettingsEditSession session) { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); if (!session.isDirty) { float okWidth = Ui.MeasureText("OK").X + style.framePadding.X * 2f; Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - okWidth)); if (Ui.Button("OK")) - window.Close(); + TryClose(); return; } @@ -265,12 +297,52 @@ private void DrawButtons(SettingsEditSession session) float totalWidth = cancelWidth + style.itemSpacing.X + applyWidth; Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - totalWidth)); if (Ui.Button("Cancel")) - window.Close(); + TryClose(); Ui.SameLine(0f, -1f); - if (Ui.Button("Apply")) - _ = session.Apply(); + Ui.BeginDisabled(!session.canApply); + try + { + if (Ui.Button("Apply")) + { + try + { + _ = session.Apply(); + m_applyError = string.Empty; + } + catch (Exception failure) when (IsOperationalFailure(failure)) + { + ReportFailure(failure); + } + } + } + finally + { + Ui.EndDisabled(); + } + } + + private void TryClose() + { + try + { + window.Close(); + m_applyError = string.Empty; + } + catch (Exception failure) when (IsOperationalFailure(failure)) + { + ReportFailure(failure); + } } + private void ReportFailure(Exception failure) + { + m_applyError = failure.Message; + m_log.Write(LogLevel.Error, "Settings operation failed; edits and History ownership were retained: {0}", [failure]); + } + + private static bool IsOperationalFailure(Exception failure) + => failure is IOException or UnauthorizedAccessException or ArgumentException or InvalidOperationException or AggregateException; + private void DrawNavigationButtons() { float frameHeight = Ui.GetFrameHeight(); @@ -282,7 +354,7 @@ private void DrawNavigationButtons() } Ui.EndDisabled(); - Ui.SameLine(0f, EditorUI.current.widgets.style.toolbarTightSpacing); + Ui.SameLine(0f, EditorUiSession.current.widgets.style.toolbarTightSpacing); Ui.BeginDisabled(!m_navigation.canGoForward); if (Ui.Button($"{Inno.Editor.ImGui.ImGuiIcon.AngleRight}##SettingsForward", buttonSize)) { @@ -292,13 +364,12 @@ private void DrawNavigationButtons() Ui.EndDisabled(); } - private void EnsureNavigationSession( - SettingsEditSession session, - IReadOnlyList pages - ) { - if (ReferenceEquals(m_navigationSession, session)) + private void EnsureNavigationSession(IReadOnlyList pages) + { + if (m_navigationRevision == window.sessionRevision) return; - m_navigationSession = session; + m_navigationRevision = window.sessionRevision; + m_applyError = string.Empty; SettingsPage? first = SettingsTree.FindFirstMatch(pages, m_query); m_navigation.Reset(first?.path ?? string.Empty); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/OpenSettingsAction.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/OpenSettingsAction.cs index c7c4cbb6d..901ca08f3 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/OpenSettingsAction.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/OpenSettingsAction.cs @@ -6,6 +6,10 @@ namespace Inno.Editor.Panel.Settings; [EditorMenu(SettingsInteractionIds.C_MAIN_MENU_AREA, "Edit/Settings...", order: 1000)] internal sealed class OpenSettingsAction(SettingsWindowModule window) : EditorAction { + /// + protected override EditorActionState Query(EditorActionContext context) + => new(true, window.canOpen, unavailableReason: window.unavailableReason); + /// /// Applies the editor action to the supplied interaction context. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsEditSession.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsEditSession.cs index f89427b42..098e8f7aa 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsEditSession.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsEditSession.cs @@ -71,7 +71,7 @@ internal SettingsField(BuildSettingsField definition) internal BuildSettingsField? build => m_build; } -internal sealed class SettingsEditSession +internal sealed class SettingsEditSession : IDisposable { private readonly EditorSettings m_editorSettings; private readonly ProjectSettingsEditor m_projectSettings; @@ -92,6 +92,7 @@ internal sealed class SettingsEditSession private BuildSettings m_buildDefault; private BuildSettings m_buildOriginal; private BuildSettings m_buildValue; + private EditorHistoryTransaction? m_applyTransaction; internal SettingsEditSession( EditorSettings editorSettings, @@ -149,6 +150,13 @@ IEditorHistory history pages = BuildPages(editorSettings.definitions, definitions); } + /// + public void Dispose() + { + m_applyTransaction?.Dispose(); + m_applyTransaction = null; + } + internal long editorCatalogRevision { get; } internal long projectCatalogRevision { get; } internal IReadOnlyList definitions { get; } @@ -156,6 +164,8 @@ IEditorHistory history internal bool isEditorDirty => m_editorModified.Count > 0 || m_editorResets.Count > 0; internal bool isProjectDirty => m_projectModified.Count > 0 || m_projectResets.Count > 0; internal bool isBuildDirty => !BuildSettingsField.ValuesEqual(m_buildValue, m_buildOriginal); + internal bool canApply => m_applyTransaction is null && !m_history.isFaulted; + internal bool isHistoryFaulted => m_history.isFaulted; internal bool isDirty => isEditorDirty || isProjectDirty || isBuildDirty; internal bool Draw(SettingsField field) @@ -278,13 +288,45 @@ bool differsFromDrawBaseline internal bool Apply() { + if (!canApply) + throw new InvalidOperationException("Settings cannot be applied while their previous transaction or History requires recovery."); + bool editorDirty = isEditorDirty; + bool projectDirty = isProjectDirty; + bool buildDirty = isBuildDirty; bool changed = false; - if (isEditorDirty) - changed |= ApplyEditor(); - if (isProjectDirty) - changed |= ApplyProject(); - if (isBuildDirty) - changed |= ApplyBuild(); + m_applyTransaction = m_history.BeginTransaction("Apply Settings"); + try + { + if (editorDirty) + changed |= ApplyEditor(); + if (projectDirty) + changed |= ApplyProject(); + if (buildDirty) + changed |= ApplyBuild(); + m_applyTransaction.Commit(); + m_applyTransaction = null; + } + catch (Exception failure) + { + try + { + Dispose(); + } + catch (Exception rollbackFailure) + { + throw new AggregateException( + "Settings Apply failed and its History transaction could not retire. Recovery is required before further editing.", + failure, + rollbackFailure); + } + throw; + } + if (editorDirty) + AcceptEditorValues(); + if (projectDirty) + AcceptProjectValues(); + if (buildDirty) + AcceptBuildValues(); return changed; } @@ -294,7 +336,11 @@ private bool ApplyEditor() path => path, path => m_editorValues[path], StringComparer.Ordinal); - bool changed = m_editorSettings.Apply(values, m_editorResets); + return m_editorSettings.Apply(values, m_editorResets); + } + + private void AcceptEditorValues() + { m_editorModified.Clear(); m_editorInitiallyNonDefault.Clear(); m_editorResetIntent.Clear(); @@ -307,13 +353,16 @@ private bool ApplyEditor() if (!definition.IsDefault(value)) m_editorInitiallyNonDefault.Add(field.path); } - return changed; } private bool ApplyProject() { var values = m_projectModified.ToDictionary(id => id, id => m_projectValues[id]); - bool changed = m_projectSettings.Apply(values, m_projectResets); + return m_projectSettings.Apply(values, m_projectResets); + } + + private void AcceptProjectValues() + { m_projectModified.Clear(); m_projectInitiallyNonDefault.Clear(); m_projectResetIntent.Clear(); @@ -330,7 +379,6 @@ private bool ApplyProject() if (!definition.ValuesEqual(value, m_projectSettings.GetComposedDefault(definition))) m_projectInitiallyNonDefault.Add(definition.settingId); } - return changed; } private bool ApplyBuild() @@ -349,10 +397,14 @@ private bool ApplyBuild() throw; } + return true; + } + + private void AcceptBuildValues() + { m_buildDefault = m_buildSettings.defaultSettings; - m_buildOriginal = m_buildSettings.Load(); + m_buildOriginal = m_buildValue.Copy(); m_buildValue = m_buildOriginal.Copy(); - return true; } private static void UpdateDirty( diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsWindowModule.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsWindowModule.cs index 7081dbc4b..0ed225df8 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsWindowModule.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Runtime/SettingsWindowModule.cs @@ -1,3 +1,4 @@ +using System; using Inno.Build; using Inno.Editor.Core; using Inno.Editor.Interactions; @@ -11,48 +12,84 @@ internal sealed class SettingsWindowModule( ProjectSettingsEditor projectSettings, BuildPipeline buildPipeline, BuildSettingsStore buildSettings, - EditorInteractions interactions -) : EditorModule + EditorInteractions interactions, + EditorReloadCoordinator reloads +) : EditorModule, IEditorReloadGuard { + private const string C_PRESENTATION_FAILURE = "Settings presentation failed. Reload extensions or restart the Editor."; + private SettingsEditSession? m_session; + private IDisposable? m_reloadGuard; + private long m_failedCatalogRevision = long.MinValue; internal bool isVisible { get; private set; } internal SettingsEditSession? session => m_session; + internal long sessionRevision { get; private set; } + internal bool canOpen => m_failedCatalogRevision != editorSettings.catalogRevision; + internal string? unavailableReason => canOpen ? null : C_PRESENTATION_FAILURE; internal void Open() { + if (!canOpen) + throw new InvalidOperationException(C_PRESENTATION_FAILURE); if (!isVisible) + { m_session = new SettingsEditSession( editorSettings, projectSettings, buildPipeline, buildSettings, interactions.history); + sessionRevision++; + } isVisible = true; } internal void Close() { + m_session?.Dispose(); isVisible = false; m_session = null; + sessionRevision++; } internal void Refresh() { + m_session?.Dispose(); m_session = new SettingsEditSession( editorSettings, projectSettings, buildPipeline, buildSettings, interactions.history); + sessionRevision++; + } + + internal void FailPresentation() + { + m_failedCatalogRevision = editorSettings.catalogRevision; + Close(); } /// - /// Stops this feature before its owning runtime releases the active generation. + /// Registers the admission boundary for generation-bound Settings editing. /// /// /// The context that supplies state and services for this operation. /// - protected override void OnStop(EditorContext context) => Close(); + protected override void OnStart(EditorContext context) => m_reloadGuard = reloads.RegisterGuard(this); + + /// + protected override void OnStop(EditorContext context) + { + Close(); + m_reloadGuard?.Dispose(); + m_reloadGuard = null; + } + + bool IEditorReloadGuard.canRequestReload => !isVisible; + bool IEditorReloadGuard.canActivateReload => !isVisible; + string? IEditorReloadGuard.blockingReason + => isVisible ? "Close Settings before reloading scripts to retire its current-generation editing state." : null; } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj index 074500b50..5acb9930f 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj +++ b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj @@ -1,5 +1,9 @@ + + + + @@ -28,6 +32,14 @@ $(InnoNativeTarget) + + + + diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs index c70a8d74c..7432e9df3 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs @@ -33,8 +33,11 @@ internal sealed class NativeProjectPanelHost : IDisposable private readonly ProjectSettingsStore m_settings; private readonly PluginEnvironment m_plugins; - internal NativeProjectPanelHost(bool supportsVertexOffsets = false) - { + internal NativeProjectPanelHost( + bool supportsVertexOffsets = false, + bool includeSettings = false, + IByteDocumentStore? settingsDocument = null + ) { _ = Assembly.Load("Inno.Editor.Panel.Hierarchy"); _ = Assembly.Load("Inno.Editor.Panel.FileBrowser"); _ = Assembly.Load("Inno.Editor.Workbench"); @@ -58,16 +61,28 @@ internal NativeProjectPanelHost(bool supportsVertexOffsets = false) var identities = new IdentityAllocator(); assets = new AssetPipeline(m_engine.modules, m_engine.types, m_engine.serialization, identities, m_engine.diagnostics, m_engine.logs, AssetPipelineOptions.Create(source, library) with { enableFileSystemWatcher = false }); - m_settings = new ProjectSettingsStore(new FileByteDocumentStore(Path.Combine(m_root, "Settings.Project.inno")), + m_settings = new ProjectSettingsStore(settingsDocument ?? new FileByteDocumentStore(Path.Combine(m_root, "Settings.Project.inno")), m_engine.types, m_engine.serialization, new ProjectId("tests.native-panels"), AssetSerializationContext.Create(assets)); var pluginSources = new PluginSourceService(m_engine.serialization, pluginRoot, library); m_plugins = new PluginEnvironment(assets, m_settings, m_engine.serialization, pluginRoot, library, pluginSources.Scan(), m_engine.modules.generations); m_console = new EditorConsole(m_engine.logs, m_engine.diagnostics, new NativePanelHost.InactivePlayMode()); m_console.Start(); var ui = new EditorUiSession(); - var reloads = new EditorReloadCoordinator(); - runtime = new EditorInteractionRuntime(new EditorContext(m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Ctrl")), - m_engine.types, m_engine.logs, [m_engine.types, m_engine.serialization, m_session, identities, m_session.identities, assets, m_plugins, m_settings, reloads, ui, m_console]); + reloads = new EditorReloadCoordinator(); + SettingsPanelServices? settingsServices = includeSettings + ? new SettingsPanelServices(m_root, assets, m_plugins, m_settings, m_engine) + : null; + if (includeSettings) + _ = Assembly.Load("Inno.Editor.Panel.Settings"); + runtime = new EditorInteractionRuntime( + new EditorContext(m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Ctrl")), + m_engine.types, + m_engine.logs, + [ + m_engine.types, m_engine.serialization, m_session, identities, m_session.identities, + assets, m_plugins, m_settings, reloads, ui, m_console, + .. (settingsServices?.services ?? []) + ]); runtime.Start(); _ = runtime.panelCount; Assert.True(runtime.interactions.TryGetModule(out var probe)); @@ -77,6 +92,8 @@ internal NativeProjectPanelHost(bool supportsVertexOffsets = false) native = new NativePanelHost(runtime, ui, m_console, m_engine.logs, m_engine.diagnostics, supportsVertexOffsets); } + internal EditorReloadCoordinator reloads { get; } + internal ProjectSettingsStore projectSettings => m_settings; internal AssetPipeline assets { get; } internal string projectRoot => m_root; internal EditorInteractionRuntime runtime { get; } @@ -86,6 +103,7 @@ internal NativeProjectPanelHost(bool supportsVertexOffsets = false) internal NativePanelHost native { get; } internal GameScene CreateScene() => m_session.scenes.LoadNewSceneAdditive(); + internal void RefreshTypes() => m_engine.types.Rebuild(); public void Dispose() { diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/SettingsApplyNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/SettingsApplyNativeTests.cs new file mode 100644 index 000000000..b9388dabc --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/SettingsApplyNativeTests.cs @@ -0,0 +1,340 @@ +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Inno.Core.IO; +using Inno.Core.Serialization; +using Inno.Core.Settings; +using Inno.Editor.Interactions; +using Inno.Editor.Settings; +using Inno.Native.ImGui; +using Xunit; +using NativeGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class SettingsApplyNativeTests +{ + [Fact] + [Trait("Acceptance", "Workflows")] + public void FaultedDrawerReleasesReloadAdmissionUntilItsExtensionsRecover() + { + using var host = new NativeProjectPanelHost(includeSettings: true); + var driver = new SettingsInput(host); + Assert.True(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + driver.Draw(); + AtomicEditorSetting.throwOnDraw = true; + try + { + Assert.ThrowsAny(() => driver.Search("Atomic Editor")); + } + finally + { + AtomicEditorSetting.throwOnDraw = false; + } + Assert.True(host.reloads.CanActivateReload(out _)); + EditorActionState state = host.runtime.interactions.For("editor/main-menu").Query("editor.settings.open"); + Assert.True(state.isVisible); + Assert.False(state.isEnabled); + Assert.False(string.IsNullOrWhiteSpace(state.unavailableReason)); + Assert.False(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + host.RefreshTypes(); + Assert.True(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + driver.Draw(); + driver.Search("Atomic Editor"); + driver.CloseClean(); + Assert.False(driver.isVisible); + Assert.True(host.reloads.CanActivateReload(out _)); + } + + [Fact] + [Trait("Acceptance", "Lifecycle")] + public void OneHundredSettingsCyclesReleaseDrawnValuesAndReloadAdmission() + { + AtomicEditorSetting.snapshots.Clear(); + AtomicEditorSetting.observe = true; + try + { + using var host = new NativeProjectPanelHost(includeSettings: true); + for (int cycle = 0; cycle < 100; cycle++) + OpenAndClose(host); + Assert.True(AtomicEditorSetting.snapshots.Count >= 100); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.All(AtomicEditorSetting.snapshots, snapshot => Assert.False(snapshot.TryGetTarget(out _))); + } + finally + { + AtomicEditorSetting.observe = false; + AtomicEditorSetting.snapshots.Clear(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void OpenAndClose(NativeProjectPanelHost host) + { + Assert.True(host.reloads.CanRequestReload(out _)); + Assert.True(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + var driver = new SettingsInput(host); + driver.Draw(); + Assert.False(host.reloads.CanActivateReload(out string? reason)); + Assert.Contains("Settings", reason); + driver.Search("Atomic Editor"); + driver.CloseClean(); + Assert.False(driver.isVisible); + Assert.True(host.reloads.CanRequestReload(out _)); + Assert.True(host.reloads.CanActivateReload(out _)); + } + + [Fact] + [Trait("Acceptance", "Workflows")] + public void FailedRollbackKeepsHistoryFaultedAndPreventsFurtherApply() + { + string directory = Path.Combine(Path.GetTempPath(), "InnoSettingsRollback", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(directory); + try + { + var document = new FailingSettingsDocument(new FileByteDocumentStore(Path.Combine(directory, "Project.inno"))); + using var host = new NativeProjectPanelHost(includeSettings: true, settingsDocument: document); + Assert.True(host.runtime.interactions.TryGetModule(out var editor)); + var driver = new SettingsInput(host); + Assert.True(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + driver.Draw(); + AtomicEditorSetting.stage = true; + driver.Search("Atomic Editor"); + Assert.False(AtomicEditorSetting.stage); + AtomicProjectEditor.stage = true; + driver.Search("Atomic Project"); + Assert.False(AtomicProjectEditor.stage); + string editorFile = Path.Combine(host.projectRoot, "Settings.Editor.inno"); + string savedFile = Path.Combine(directory, "Editor.inno"); + bool obstructed = false; + using IDisposable listener = editor!.ListenChanges(_ => + { + if (obstructed) + return; + File.Move(editorFile, savedFile); + Directory.CreateDirectory(editorFile); + obstructed = true; + }); + document.failWrites = true; + try + { + driver.Apply(); + Assert.True(obstructed); + Assert.Equal(42, editor.Get("Editor/Atomic Editor/Value").GetAsInt32("value")); + Assert.Equal(0, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + Assert.True(host.runtime.interactions.history.isFaulted); + Assert.False(host.runtime.interactions.history.canUndo); + Assert.False(host.runtime.interactions.history.canRedo); + Assert.False(string.IsNullOrWhiteSpace(host.runtime.interactions.history.faultReason)); + } + finally + { + if (obstructed) + { + Directory.Delete(editorFile); + File.Move(savedFile, editorFile); + } + } + document.failWrites = false; + driver.Apply(); + Assert.True(host.runtime.interactions.history.isFaulted); + Assert.Equal(0, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + driver.Cancel(); + host.native.logs.Flush(); + Assert.False(driver.isVisible, driver.diagnostic + Environment.NewLine + string.Join(Environment.NewLine, host.native.console.Capture().occurrences.Select(entry => entry.message))); + } + finally + { + AtomicEditorSetting.stage = false; + AtomicProjectEditor.stage = false; + Directory.Delete(directory, recursive: true); + } + } + + [Fact] + [Trait("Acceptance", "Workflows")] + public void FailedSettingsBatchPreservesAllOwnersAndDraftsForRetry() + { + string directory = Path.Combine(Path.GetTempPath(), "InnoSettingsPublication", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(directory); + try + { + var document = new FailingSettingsDocument(new FileByteDocumentStore(Path.Combine(directory, "Project.inno"))); + using var host = new NativeProjectPanelHost(includeSettings: true, settingsDocument: document); + Assert.True(host.runtime.interactions.TryGetModule(out var editor)); + var driver = new SettingsInput(host); + Assert.True(host.runtime.interactions.For("editor/main-menu").Execute("editor.settings.open")); + driver.Draw(); + AtomicEditorSetting.stage = true; + driver.Search("Atomic Editor"); + Assert.False(AtomicEditorSetting.stage); + AtomicProjectEditor.stage = true; + driver.Search("Atomic Project"); + Assert.False(AtomicProjectEditor.stage); + document.failWrites = true; + driver.Apply(); + Assert.Equal(0, editor!.Get("Editor/Atomic Editor/Value").GetAsInt32("value")); + Assert.Equal(0, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + Assert.False(host.runtime.interactions.history.canUndo); + Assert.False(host.runtime.interactions.history.isFaulted); + document.failWrites = false; + driver.Apply(); + Assert.Equal(42, editor.Get("Editor/Atomic Editor/Value").GetAsInt32("value")); + Assert.Equal(84, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + Assert.True(host.runtime.interactions.history.Undo().succeeded); + Assert.Equal(0, editor.Get("Editor/Atomic Editor/Value").GetAsInt32("value")); + Assert.Equal(0, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + Assert.False(host.runtime.interactions.history.canUndo); + Assert.True(host.runtime.interactions.history.Redo().succeeded); + Assert.Equal(42, editor.Get("Editor/Atomic Editor/Value").GetAsInt32("value")); + Assert.Equal(84, host.projectSettings.Get(new("tests.ui.atomic-settings")).value); + } + finally + { + AtomicEditorSetting.stage = false; + AtomicProjectEditor.stage = false; + Directory.Delete(directory, recursive: true); + } + } + + private sealed class FailingSettingsDocument(IByteDocumentStore owner) : IByteDocumentStore + { + internal bool failWrites { get; set; } + public string documentName => owner.documentName; + public bool canWrite => owner.canWrite; + public bool exists => owner.exists; + public byte[]? Read() => owner.Read(); + public void Write(ReadOnlySpan data) + { + if (failWrites) + throw new IOException("Controlled project settings publication failure."); + owner.Write(data); + } + } + + private sealed class SettingsInput(NativeProjectPanelHost host) + { + private Vector2 m_search; + private Vector2 m_apply; + private Vector2 m_cancel; + internal bool isVisible { get; private set; } + internal string diagnostic => $"Apply={m_apply}; Cancel={m_cancel}; Search={m_search}"; + + internal void Draw() + { + host.native.Frame(() => + { + NativeGui.SetNextWindowPos(new Vector2(20f), (int)ImGuiCond.Always); + NativeGui.SetNextWindowSize(new Vector2(1000f, 800f), (int)ImGuiCond.Always); + bool open = true; + _ = NativeGui.Begin("Settings acceptance", ref open, (int)ImGuiWindowFlags.NoSavedSettings); + try + { + Vector2 content = NativeGui.GetCursorScreenPos(); + m_search = content + new Vector2(115f, NativeGui.GetFrameHeight() * 0.5f + 3f); + EditorModalExtension modal = Assert.Single(host.runtime.modals, modal => modal.id == "editor.settings"); + Assert.True(modal.TryGetPresentation(out var presentation)); + isVisible = presentation.isVisible; + if (!isVisible) + return; + Assert.True(modal.Draw(host.runtime.context), "The operational Settings failure must not quarantine the modal."); + m_apply = (NativeGui.GetItemRectMin() + NativeGui.GetItemRectMax()) * 0.5f; + var style = NativeGui.Raw.GetStyle(); + m_cancel = new Vector2(NativeGui.GetItemRectMin().X - style->ItemSpacing.X + - NativeGui.CalcTextSize("Cancel").X * 0.5f - style->FramePadding.X, m_apply.Y); + } + finally + { + NativeGui.End(); + } + }); + } + + internal void Search(string text) + { + Click(m_search); + ImGuiIO* io = NativeGui.Raw.GetIO(); + NativeGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 1); + NativeGui.Raw.AddKeyEvent(io, ImGuiKey.A, 1); + Draw(); + NativeGui.Raw.AddKeyEvent(io, ImGuiKey.A, 0); + NativeGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 0); + Draw(); + foreach (char character in text) + NativeGui.Raw.AddInputCharacter(io, character); + Draw(); + Draw(); + } + + internal void Apply() => Click(m_apply); + internal void Cancel() => Click(m_cancel); + internal void CloseClean() => Click(m_apply); + + private void Click(Vector2 point) + { + ImGuiIO* io = NativeGui.Raw.GetIO(); + NativeGui.Raw.AddMousePosEvent(io, point.X, point.Y); + Draw(); + NativeGui.Raw.AddMouseButtonEvent(io, 0, 1); + Draw(); + NativeGui.Raw.AddMouseButtonEvent(io, 0, 0); + Draw(); + Draw(); + } + } +} + +[EditorSettingPath("Editor/Atomic Editor/Value")] +public sealed class AtomicEditorSetting : EditorSetting +{ + public static bool stage; + public static bool throwOnDraw; + public static bool observe; + public static readonly List> snapshots = []; + public override EditorSettingObject defaultValue + { + get + { + var value = new EditorSettingObject(); + value.SetAsInt32("value", 0); + return value; + } + } + protected override void OnDraw(EditorSettingObject setting) + { + if (throwOnDraw) + throw new InvalidOperationException("Controlled Settings drawer failure."); + if (observe && (snapshots.Count == 0 || !snapshots[^1].TryGetTarget(out var last) || !ReferenceEquals(last, setting))) + snapshots.Add(new WeakReference(setting)); + if (!stage) + return; + setting.SetAsInt32("value", 42); + stage = false; + } +} + +[GenerateSerializationConverter] +[ProjectSettingDefinition("tests.ui.atomic-settings")] +public sealed class AtomicProjectSetting : ISerializable +{ + [SerializableProperty] + public int value { get; set; } +} + +[ProjectSettingPath("Project/Atomic Project/Value")] +public sealed class AtomicProjectEditor : ProjectSettingEditor +{ + public static bool stage; + protected override void OnDraw(AtomicProjectSetting setting) + { + if (!stage) + return; + setting.value = 84; + stage = false; + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/SettingsPanelServices.cs b/tests/editor/Inno.Editor.PlayMode.Tests/SettingsPanelServices.cs new file mode 100644 index 000000000..56e2dbb70 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/SettingsPanelServices.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Inno.Assets.Pipeline; +using Inno.Build; +using Inno.Build.Managed; +using Inno.Core.Settings; +using Inno.Plugins.Authoring; +using Inno.Runtime; +using Inno.Scripting.Compiler; + +namespace Inno.Editor.PlayMode.Tests; + +internal sealed class SettingsPanelServices +{ + internal SettingsPanelServices( + string root, + AssetPipeline assets, + PluginEnvironment plugins, + ProjectSettingsStore settings, + EngineHost engine + ) { + string host = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + var compiler = new ScriptCompiler(new ScriptCompilerOptions + { + projectRootDirectory = root, + compilationEnvironment = new SdkScriptCompilationEnvironment(host, root) + }, assets, plugins); + var target = new FixtureTarget(); + var pipeline = new BuildPipeline(assets, plugins, settings, engine.serialization, engine.modules.generations, + compiler, Path.Combine(root, "SupportPacks"), [new GameBuildTargetBinding(target, target)], + new ManagedDeploymentCatalog([new FixtureDeployment()])); + var build = new BuildSettingsStore(Path.Combine(root, "Settings.Build.inno"), engine.serialization, + BuildSettings.CreateDefault("Settings acceptance", string.Empty, target.id)); + services = [pipeline, build]; + } + + internal IReadOnlyList services { get; } + + private sealed class FixtureTarget : IGameBuildTarget, IGameContentCompiler + { + public BuildTargetId id => new("tests-ui-settings"); + public BuildTargetId target => id; + public ManagedDeploymentId defaultManagedDeployment => new("tests-ui-managed"); + public string runtimeIdentifier => "tests-ui-rid"; + public string displayName => "Settings fixture"; + public void Validate(string directory) => throw new NotSupportedException("Settings acceptance does not package players."); + public ValueTask PackageAsync( + GameBuildPackageContext context, + CancellationToken cancellationToken = default + ) => throw new NotSupportedException("Settings acceptance does not package players."); + public ValueTask CompileAsync( + GameBuildContentContext context, + CancellationToken cancellationToken = default + ) => throw new NotSupportedException("Settings acceptance does not compile content."); + } + + private sealed class FixtureDeployment : IManagedDeploymentCompiler + { + public ManagedDeploymentId id => new("tests-ui-managed"); + public ManagedDeploymentCapabilities capabilities => new(["tests-ui-rid"], true, false, false); + public ValueTask CompileAsync( + ManagedDeploymentRequest request, + CancellationToken cancellationToken = default + ) => throw new NotSupportedException("Settings acceptance does not compile players."); + } +} From 0974c317c3afcdb97f8f95adf35e4677c40b6c46 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 09:51:23 +0800 Subject: [PATCH 19/54] docs(editor): synchronize current acceptance and API references --- docs/architecture/CURRENT_ACCEPTANCE.md | 2 +- docs/architecture/CURRENT_ISSUES.md | 2 +- docs/assets/Inno.Assets.Pipeline.md | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/architecture/CURRENT_ACCEPTANCE.md b/docs/architecture/CURRENT_ACCEPTANCE.md index c3500b000..0c4ed8b11 100644 --- a/docs/architecture/CURRENT_ACCEPTANCE.md +++ b/docs/architecture/CURRENT_ACCEPTANCE.md @@ -4,7 +4,7 @@ 当前 Editor UI 的分层、Session、Selection、History、插件调用方及逐项质量评价见[Editor UI 验收](../../records/architecture/acceptance/EDITOR_UI_ACCEPTANCE_2026_10_27.md)和[当前 UI 架构](EDITOR_UI_ARCHITECTURE.md)。该报告分别列出自动验证、产品运行与未执行的人工/设备检查,不将此前数量累加为本次结果。 -**原计划尚未全部完成。** 后续实现和实时证据以[issue 闭合台账](../../records/architecture/EDITOR_UI_CLOSURE_PLAN.md)为准;菜单索引、Graph、Console、Stats 和大视图已有后续实现,完整矩阵仍在验收,原基线的当前 FlappyBird 同输入比较已复现为阻塞。PR #26 保持 Draft。`2dc37114` 等历史源码的 6/6 CI checks 包含 macOS ARM64 自动测试,只保留其对应 revision 的证明范围;不能把历史 CI 或局部用例算作最终候选已全部通过。 +**原计划尚未全部完成。** 后续实现和实时证据以[issue 闭合台账](../../records/architecture/EDITOR_UI_CLOSURE_PLAN.md)为准;菜单索引、Graph、Console、Stats 和大视图已有后续实现,完整矩阵仍在验收,原基线的当前 指定样例 同输入比较已复现为阻塞。PR #26 保持 Draft。`2dc37114` 等历史源码的 6/6 CI checks 包含 macOS ARM64 自动测试,只保留其对应 revision 的证明范围;不能把历史 CI 或局部用例算作最终候选已全部通过。 当前依赖通过标准 `.csproj` / `PackageReference` 和 SDK Restore 解析,源码不保存独立依赖锁清单;本轮范围与证据见[标准 NuGet 还原验收](../../records/architecture/acceptance/STANDARD_NUGET_RESTORE_ACCEPTANCE_2026_10_26.md)。基线保持 .NET SDK 10.0.402、C# 14、BGCS 4.1.2。 diff --git a/docs/architecture/CURRENT_ISSUES.md b/docs/architecture/CURRENT_ISSUES.md index 1a763f701..4583431da 100644 --- a/docs/architecture/CURRENT_ISSUES.md +++ b/docs/architecture/CURRENT_ISSUES.md @@ -10,7 +10,7 @@ [原计划复审](../../records/architecture/acceptance/EDITOR_UI_PLAN_REVIEW_2026_10_27.md)发现的缺口正在按[真实 issue 闭合台账](../../records/architecture/EDITOR_UI_CLOSURE_PLAN.md)处理。菜单/快捷键 generation 索引、通用 Graph 呈现、Console 搜索、Stats 趋势与大视图裁剪已有实现;最终操作、视觉、生命周期和成对性能矩阵仍未全部闭合。深查发现的资产刷新身份问题按 [#43](https://github.com/Wolfiora/InnoEngine/issues/43)在所属索引修复,未在 UI 添加 fallback。PR #26 保持 Draft,不能用历史 CI 或局部测试代替最终验收。 -E11 的原重构前 FlappyBird 同输入比较存在已复现阻塞:原基线 `b303d9f1` 及其正式工具能够构建,但当前插件源码依赖其不存在的 `EditorUiSession`,正式脚本编译失败。原始日志保留在闭合验收 owner 的 `e11/original-flappy-projection.log`。不增加兼容层或替换输入来冒充同输入比较;中立 10k/2k 负载继续按相同冻结输入验收。 +E11 的原重构前 指定样例 同输入比较存在已复现阻塞:原基线 `b303d9f1` 及其正式工具能够构建,但当前插件源码依赖其不存在的 `EditorUiSession`,正式脚本编译失败。原始日志保留在闭合验收 owner 的 `e11/original-flappy-projection.log`。不增加兼容层或替换输入来冒充同输入比较;中立 10k/2k 负载继续按相同冻结输入验收。 F03 / [#13](https://github.com/Wolfiora/InnoEngine/issues/13) 已在本分支修复:History 先验证栈顶 ID 再 Pop,三种非 LIFO 拒绝回归在最终 Windows/macOS CI 通过,本次 121 项 Interactions 复跑也通过。等待 PR 合并后关闭 issue,不再把当前分支描述为仍在校验前弹栈。 diff --git a/docs/assets/Inno.Assets.Pipeline.md b/docs/assets/Inno.Assets.Pipeline.md index a27249fc7..650e89484 100644 --- a/docs/assets/Inno.Assets.Pipeline.md +++ b/docs/assets/Inno.Assets.Pipeline.md @@ -794,13 +794,13 @@ static bool AdvanceSampleImport( | [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.BuildAsync(Inno.Assets.AssetObject definition, System.Collections.Generic.IReadOnlyList inputs, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Importing.cs#L55) | Builds and validates the requested artifact asynchronously before publishing it.
`definition`: The build definition asset.
`inputs`: The immutable input catalog snapshots.
`cancellationToken`: Cancellation for the candidate build. | The content-addressed output bundle key. | — | | [`public Inno.Assets.Pipeline.AssetImportSettingsSnapshot Inno.Assets.Pipeline.AssetLoader.GetImportSettings(Inno.Assets.AssetPath path)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L31) | Reads a detached importer settings value without changing source metadata.
`path`: The isolated source path, not its sidecar path. | The current settings and the metadata fingerprint required for a subsequent save. | `System.NotSupportedException`: The source has no registered importer.
`System.IO.InvalidDataException`: The persisted settings cannot be restored by the current importer. | | [`public bool Inno.Assets.Pipeline.AssetLoader.SaveImportSettings(Inno.Assets.AssetPath path, Inno.Core.Serialization.ISerializable? settings, string expectedFingerprint)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.ImportSettings.cs#L68) | Atomically saves importer settings and reimports the source. Failed imports retain both the saved settings and the previous successful artifact; callers must inspect the returned import status separately from saving.
`path`: The writable isolated source path.
`settings`: A value of the current importer's settings type, or null to reset to defaults.
`expectedFingerprint`: The fingerprint returned when the settings were read. | True when reimport succeeds; false when settings were saved but reimport failed. | `System.IO.IOException`: The sidecar changed externally or could not be written.
`System.InvalidOperationException`: The source is read-only or belongs to an isolated candidate.
`System.ArgumentException`: The settings have a different type from the current importer. | -| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(Inno.Assets.AssetPath path, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L41) | Loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type. | The canonical asset, or when unavailable or incompatible. | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(Inno.Assets.AssetPath path, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L64) | Tries to load a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | -| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(System.Guid persistentId, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L85) | Loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type. | The canonical asset, or when unavailable or incompatible. | — | -| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(System.Guid persistentId, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L108) | Tries to load a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(Inno.Assets.AssetPath path, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L132) | Asynchronously loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | -| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(System.Guid persistentId, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L172) | Asynchronously loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | -| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetLoader.ResolveReference(System.Guid persistentId, System.Guid stableTypeId, string lastKnownPath, System.Type expectedType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L214) | Resolves a serialized reference or creates a persistent missing placeholder.
`persistentId`: The referenced persistent identity.
`stableTypeId`: The referenced stable asset type identity.
`lastKnownPath`: The last known source-relative path.
`expectedType`: The declared destination type. | A compatible canonical asset or missing placeholder. | — | +| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(Inno.Assets.AssetPath path, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L46) | Loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type. | The canonical asset, including retained last-good content after a diagnosed import failure, or when unavailable or incompatible. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(Inno.Assets.AssetPath path, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L69) | Tries to load a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | +| [`public Inno.Assets.AssetObject? Inno.Assets.Pipeline.AssetLoader.Load(System.Guid persistentId, System.Type requestedAssetType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L90) | Loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type. | The canonical asset, or when unavailable or incompatible. | — | +| [`public bool Inno.Assets.Pipeline.AssetLoader.TryLoad(System.Guid persistentId, System.Type requestedAssetType, out Inno.Assets.AssetObject? asset)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L113) | Tries to load a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`asset`: The canonical asset when successful. | when a compatible asset was loaded. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(Inno.Assets.AssetPath path, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L137) | Asynchronously loads a canonical asset by isolated source path.
`path`: The isolated source path.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | +| [`public System.Threading.Tasks.ValueTask Inno.Assets.Pipeline.AssetLoader.LoadAsync(System.Guid persistentId, System.Type requestedAssetType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L177) | Asynchronously loads a canonical asset by persistent identity.
`persistentId`: The persistent asset identity.
`requestedAssetType`: The required assignable asset type.
`cancellationToken`: Cancellation for the current caller's wait. | The canonical asset, or when unavailable or incompatible. | — | +| [`public Inno.Assets.AssetObject Inno.Assets.Pipeline.AssetLoader.ResolveReference(System.Guid persistentId, System.Guid stableTypeId, string lastKnownPath, System.Type expectedType)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Loading.cs#L219) | Resolves a serialized reference or creates a persistent missing placeholder.
`persistentId`: The referenced persistent identity.
`stableTypeId`: The referenced stable asset type identity.
`lastKnownPath`: The last known source-relative path.
`expectedType`: The declared destination type. | A compatible canonical asset or missing placeholder. | — | | [`public int Inno.Assets.Pipeline.AssetLoader.CollectArtifacts(System.TimeSpan gracePeriod, long maximumSizeBytes)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L41) | Collects unreachable content-addressed artifacts.
`gracePeriod`: The minimum age of an unreachable bundle.
`maximumSizeBytes`: The cache size limit, or zero for no limit. | The number of removed artifact bundles. | — | | [`public bool Inno.Assets.Pipeline.AssetLoader.TryGetArtifact(System.Guid persistentId, string outputName, out Inno.Assets.AssetArtifactInfo? artifact)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L74) | Tries to resolve a named output from the current artifact bundle.
`persistentId`: The stable persistent identity used for lookup.
`outputName`: The stable artifact output name used for lookup.
`artifact`: The resolved immutable artifact payload returned to the caller. | when the operation succeeds or its condition is satisfied; otherwise, . | — | | [`public Inno.Assets.ArtifactLease Inno.Assets.Pipeline.AssetLoader.AcquireArtifact(System.Guid persistentId, string outputName)`](../../src/content/assets/Inno.Assets.Pipeline/Importing/AssetLoader.Residency.cs#L109) | Acquires an immutable output while excluding concurrent artifact collection.
`persistentId`: The source asset's persistent identity.
`outputName`: The required output protocol. | A lease retaining this exact artifact key, even after reimport or source deletion. | `System.InvalidOperationException`: No current output can be resolved. | From 660962a5bcd4bc393028738b0dc3b0d53a0b0e40 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 10:43:14 +0800 Subject: [PATCH 20/54] fix(testing): forward the selected tool host to UI acceptance --- tests/Invoke-EditorUiAcceptance.ps1 | 8 +++++--- tests/Invoke-IsolatedTests.ps1 | 11 ++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 index 77dbea95f..606462436 100644 --- a/tests/Invoke-EditorUiAcceptance.ps1 +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -46,13 +46,15 @@ function Save-Result( $entry = Join-Path $repository 'entry/InnoEngine.Editor/InnoEngine.Editor.csproj' $evaluated = @(& $Dotnet msbuild $entry -nologo "-p:Configuration=$Configuration" "-p:ArtifactsPath=$artifacts" ` - '-getProperty:InnoNativeTarget,InnoProductBuildProperties,TargetPath') + '-getProperty:InnoNativeTarget,InnoToolTarget,InnoProductBuildProperties,TargetPath') if ($LASTEXITCODE -ne 0) { throw 'The official Editor entry could not select its product owner.' } $product = ($evaluated -join [Environment]::NewLine | ConvertFrom-Json).Properties -if (-not $product.InnoNativeTarget -or -not (Test-Path -LiteralPath $product.InnoProductBuildProperties)) { +if (-not $product.InnoNativeTarget -or -not $product.InnoToolTarget -or + -not (Test-Path -LiteralPath $product.InnoProductBuildProperties)) { throw 'The selected development host does not provide an Editor product.' } $buildArguments = @("-p:ArtifactsPath=$artifacts", "-p:InnoNativeTarget=$($product.InnoNativeTarget)", + "-p:InnoToolTarget=$($product.InnoToolTarget)", "-p:InnoProductBuildProperties=$($product.InnoProductBuildProperties)", "-p:InnoTestNativeTarget=$($product.InnoNativeTarget)", '-p:InnoTestNativeProduct=editor') $profiles = if ($AcceptanceProfile -eq 'All') { @('Contracts', 'Workflows', 'Visual', 'Lifecycle', 'Performance', 'Products') } else { @($AcceptanceProfile) } @@ -114,7 +116,7 @@ foreach ($selected in $profiles) { } & (Join-Path $PSScriptRoot 'Invoke-IsolatedTests.ps1') -Dotnet $Dotnet -Solution $selection ` -Configuration $Configuration -ResultsDirectory $stageRoot -ArtifactsPath $artifacts ` - -Target $product.InnoNativeTarget -ProductProperties $product.InnoProductBuildProperties -Filter $filter ` + -Target $product.InnoNativeTarget -ToolTarget $product.InnoToolTarget -ProductProperties $product.InnoProductBuildProperties -Filter $filter ` -TemporaryDirectory $TemporaryDirectory ` *> (Join-Path $stageRoot 'execution.log') $executed = 0 diff --git a/tests/Invoke-IsolatedTests.ps1 b/tests/Invoke-IsolatedTests.ps1 index c002029ca..b9edee234 100644 --- a/tests/Invoke-IsolatedTests.ps1 +++ b/tests/Invoke-IsolatedTests.ps1 @@ -3,6 +3,7 @@ param( [string]$ResultsDirectory = 'artifacts/acceptance/ci-tests', [string]$Dotnet = 'dotnet', [string]$Target, + [string]$ToolTarget, [string]$ProductProperties, [string]$ArtifactsPath, [string]$Filter, @@ -67,7 +68,15 @@ if ($Filter) { } if ($Target) { if (-not $ProductProperties) { throw 'A target test build requires explicit product properties.' } - $productArguments += @("-p:InnoNativeTarget=$Target", "-p:InnoToolTarget=$Target", + if (-not $ToolTarget) { + $entry = Join-Path $repositoryRoot 'entry/InnoEngine.Editor/InnoEngine.Editor.csproj' + $selectedTool = @(& $Dotnet msbuild $entry -nologo -getProperty:InnoToolTarget) + if ($LASTEXITCODE -ne 0 -or $selectedTool.Count -ne 1 -or -not $selectedTool[0]) { + throw 'The SDK-selected development host did not provide a tool target.' + } + $ToolTarget = $selectedTool[0].Trim() + } + $productArguments += @("-p:InnoNativeTarget=$Target", "-p:InnoToolTarget=$ToolTarget", "-p:InnoProductBuildProperties=$ProductProperties", "-p:InnoTestNativeTarget=$Target", '-p:InnoTestNativeProduct=editor') } From f7dce89c0d91ecb0f2333b8ca3117fefc71db96a Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 16:01:06 +0800 Subject: [PATCH 21/54] fix(editor): preserve input semantics and bound serialization allocations --- AGENTS.md | 1 + .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 6 + docs/core/Inno.Core.Serialization.md | 6 +- docs/editor/Inno.Editor.Graph.ImGui.md | 26 +- docs/editor/Inno.Editor.ImGui.md | 14 +- .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 45 +++ ...EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md | 269 ++++++++++++++++++ .../Canvas/ShaderEditorCanvas.cs | 11 +- .../Canvas/GraphCanvasView.Nodes.cs | 29 +- .../Canvas/GraphCanvasView.cs | 9 +- .../Inno.Editor.Graph.ImGui.csproj | 2 +- .../presentation/Inno.Editor.ImGui/ImGui.cs | 4 +- .../Runtime/EditorUiInput.cs | 9 +- .../Widgets/ImGuiWidget.Selection.cs | 9 +- .../Internal/BinarySerializationFormat.cs | 6 +- .../Internal/PropertySnapshotPipeline.cs | 62 ++-- .../Internal/SerializationOperation.cs | 100 +++---- .../SerializationRegistry.cs | 11 +- tests/Invoke-EditorUiAcceptance.ps1 | 3 +- tests/Invoke-IsolatedTests.ps1 | 2 +- .../Inno.Build.Tests/Inno.Build.Tests.csproj | 2 + .../SerializationBehaviorTests.Efficiency.cs | 162 +++++++++++ .../SerializationBehaviorTests.cs | 2 +- .../WidgetLayoutTests.cs | 39 +++ .../GraphCanvasNativeTests.cs | 12 +- .../NativePanelHost.cs | 10 +- .../TreeKeyboardOwnershipNativeTests.cs | 31 +- .../ScriptingPipelineTests.UiLifecycle.cs | 79 +++++ .../ScriptingPipelineTests.cs | 115 +++++--- 29 files changed, 872 insertions(+), 204 deletions(-) create mode 100644 records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md create mode 100644 tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs create mode 100644 tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs diff --git a/AGENTS.md b/AGENTS.md index 158ddc36d..a6e95d04f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -332,3 +332,4 @@ public void AAA( - Session-owned zoom and density requests commit through the presentation preparation phase after selecting the owned context and display metrics, before native frame creation. Repeated preparation during the same drawing frame cannot publish a new metric snapshot. Standalone metrics remain usable without native UI. - Presentation preparation, drawing and publication form one owned operation. Reentry and retirement during that operation must fail before releasing resources. A callback failure ends only its owned native frame, preserves the original failure and never publishes partial draw data; it must not clear another context's stacks or lower native assertions. - Native presentation fixtures must create and enter an `EditorUiSession`, explicitly compose their feature/presentation owners, and resolve the exact service under test. Asynchronous shutdown fixtures must retry the public Pending retirement protocol until owned cleanup completes; they cannot rely on continuation timing or remove resource-release assertions. +- Presentation input snapshots expose physical Core modifier semantics. Reverse native shortcut normalization at the presentation boundary using the owned context configuration; do not interpret normalized Control/Super flags as physical keys or add shared-layer OS detection. Native input fixtures explicitly select their conventions and cover both modifier policies and native modes. diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index 8105eb417..9fe72571c 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -101,4 +101,10 @@ Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒 Settings 的一次 Apply 复用单一 History transaction,所有 dirty owner 成功后才接受新基线。失败先尝试回滚并保留草稿;可恢复的文件/校验错误在窗口内反馈并记录完整诊断。History 完整性丢失后禁止继续 Apply,不能把错误吞掉后重试。打开的设置窗口借用当前 generation 的编辑对象,使用现有 admission guard 延迟 reload,关闭后恢复许可;不为此建立另一套状态恢复或 History 系统。 +验收编排从正式 Editor 入口取得实际 Native target、tool target 和产品属性,不全局覆盖 `InnoTestNativeProduct`。需要原生部署的测试工程声明自己的产品及目标;纯契约测试不会因传递引用包含 Native 程序集而自动承担完整产品准备。普通执行、隔离发现和隔离执行使用相同的 SDK 输入。 + +原生借用验证与纯值转换必须按实际契约区分:`EditorDrawList` 每次操作验证原窗口、帧、Context 和线程;颜色转换保留既有 Session/Context/线程准入。颜色格式的饱和、取整及非有限值行为通过实际原生 oracle 验证,不用普通数值相等推断所有边界一致。 + +`EditorUiInput` 的 Control/Super 表示物理修饰键,不继承原生控件内部的快捷键归一化。呈现边界依据当前 Context 的键盘配置还原这些值,列表选择复用同一快照语义,平台仍通过 `EditorKeyboardPolicy` 选择 primary modifier。原生文本控件继续使用该 Context 的输入习惯;测试必须显式声明自己的键盘配置,并覆盖 Control/Super 与两种原生模式的组合,不能依赖 runner 的默认值。 + 最终报告分别评价架构、组织、状态与生命周期、抽象、复用、样式、扩展、交互、Undo/Redo、菜单和颗粒度;每项列出证据与限制。目录整齐、测试数量或正常启动不能代替视觉、输入、重复 reload、资源收敛和成对性能验收。 diff --git a/docs/core/Inno.Core.Serialization.md b/docs/core/Inno.Core.Serialization.md index ffeee4f16..26696b0e6 100644 --- a/docs/core/Inno.Core.Serialization.md +++ b/docs/core/Inno.Core.Serialization.md @@ -299,6 +299,10 @@ static (float x, float y)[] RoundTripPoints( - Compatible property restore 只跳过单个成员的数据/类型不兼容;对象结构或 restore hook 错误仍由上层事务处理。 - 序列化层只实现当前二进制契约,不包含旧 schema 的兼容读取或迁移分支。不兼容数据会直接失败。 +单属性快照 CapturePropertyData 只读取指定的持久属性;其他 getter 不会因捕获该属性而执行。二进制格式与完整对象捕获保持一致,缓冲区按实际内容增长。 + +内部序列化操作仅在需要时创建回调、恢复去重与循环检测集合;不使用跨操作对象池。已有上下文只有在 TypeCatalog、Registry 和 metadata source 均属于当前 owner 时才复用,否则重新绑定这些依赖并保留调用方服务。`SerializationContext.With` 仍返回新的不可变上下文,确定性键排序和当前二进制格式保持不变。 + ## 当前源码公开 API 清单 按类型列出当前 public 与可供外部派生实现使用的 protected 声明。签名包含 nullable、默认参数和泛型约束;源码链接可定位实现。表中的“—”表示该声明没有额外 XML 约束,不能据此推断线程安全或不会失败。生命周期与组合顺序见本页前文。 @@ -609,7 +613,7 @@ static (float x, float y)[] RoundTripPoints( | [`public Inno.Core.Serialization.SerializationGeneration Inno.Core.Serialization.SerializationRegistry.CaptureGeneration()`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L65) | Captures the currently active converter generation for deterministic work that may cross asynchronous continuations.
返回:An immutable serialization generation owned by the caller. | — | `System.InvalidOperationException`:Thrown when serialization services have not been initialized. | | [`public System.Collections.Generic.IReadOnlyList Inno.Core.Serialization.SerializationRegistry.CaptureProperties(Inno.Core.Serialization.ISerializable value, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L146) | Captures each persistent property independently for reload-safe state restoration.
返回:Ordered independent property snapshots.
`value`:The object whose serializable properties should be captured.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when value is null.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | | [`public byte[] Inno.Core.Serialization.SerializationRegistry.CapturePropertiesData(Inno.Core.Serialization.ISerializable value, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L214) | Captures every persistent property as neutral bytes without serializing the owning object graph.
返回:Strictly validated bytes containing independently encoded property values.
`value`:The serializable object whose persistent properties should be captured.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when value is null.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | -| [`public byte[] Inno.Core.Serialization.SerializationRegistry.CapturePropertyData(Inno.Core.Serialization.ISerializable value, string propertyName, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L180) | Captures one persistent property as independently restorable neutral bytes.
返回:Neutral bytes containing the property name and encoded value.
`value`:The serializable object containing the requested property.
`propertyName`:The exact serialized member key to capture.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when value is null.
`System.ArgumentException`:Thrown when propertyName is empty or does not identify a persistent property.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | +| [`public byte[] Inno.Core.Serialization.SerializationRegistry.CapturePropertyData(Inno.Core.Serialization.ISerializable value, string propertyName, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L180) | Captures one persistent property as independently restorable neutral bytes without reading unrelated members.
返回:Neutral bytes containing the property name and encoded value.
`value`:The serializable object containing the requested property.
`propertyName`:The exact serialized member key to capture.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when value is null.
`System.ArgumentException`:Thrown when propertyName is empty or does not identify a persistent property.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | | [`public TResult Inno.Core.Serialization.SerializationRegistry.Decode(System.ReadOnlySpan bytes, System.Func read, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L566) | Decodes a manually defined structured schema from deterministic binary data.
返回:The callback result after all completion callbacks succeed.
`bytes`:The encoded bytes.
`read`:The callback that reads the root object.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when read is null.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | | [`public T Inno.Core.Serialization.SerializationRegistry.Deserialize(System.ReadOnlySpan bytes, Inno.Core.Serialization.SerializationContext? context = null) where T : class, Inno.Core.Serialization.ISerializable`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L415) | Deserializes a new serializable root object from deterministic binary data.
返回:The restored object.
`bytes`:The encoded bytes.
`context`:Optional immutable converter context. | — | `System.InvalidOperationException`:Thrown when the manager is not initialized. | | [`public void Inno.Core.Serialization.SerializationRegistry.Dispose()`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L45) | Releases every converter generation owned by this registry. | — | — | diff --git a/docs/editor/Inno.Editor.Graph.ImGui.md b/docs/editor/Inno.Editor.Graph.ImGui.md index cd4b8b80e..c1b2e8d25 100644 --- a/docs/editor/Inno.Editor.Graph.ImGui.md +++ b/docs/editor/Inno.Editor.Graph.ImGui.md @@ -22,7 +22,7 @@ 每次绘制按当前 viewport 创建值对象;同一连接曲线用于绘制与命中测试。屏幕坐标只在当前帧有效。 -`DrawNode` 的自定义内容只在节点与画布相交时借用执行,返回前恢复本次 clip;异常继续传播。调用方应在该次回调中配对自己的样式和其他作用域。没有原生 UI 帧时仍可调用纯几何、命中及指针契约;实际绘制要求有效的 owner thread、Session 和 ImGui Context。 +`DrawNode` 和 `DrawConnection` 使用调用方当前窗口的帧内 `EditorDrawList` 借用;方法不保留或释放该借用,每次原生绘制仍验证窗口、帧、线程和 Context。`DrawNode` 的自定义内容只在节点与画布相交时借用执行,返回前恢复本次 clip;异常继续传播。调用方应在该次回调中配对自己的样式和其他作用域。没有原生 UI 帧时仍可调用纯几何、命中及指针契约;实际绘制要求有效的 owner thread、Session 和 ImGui Context。 ## 错误、生命周期与热重载 @@ -92,13 +92,13 @@ - + - + | API 与源码 | 契约与参数 | 返回值 | 重要异常 | | --- | --- | --- | --- | @@ -154,26 +154,26 @@ | [`public override int Inno.Editor.Graph.ImGui.GraphCanvasPort.GetHashCode()`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Returns the hash code for this instance. | A 32-bit signed integer that is the hash code for this instance. | — | | [`public override bool Inno.Editor.Graph.ImGui.GraphCanvasPort.Equals(object obj)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Indicates whether this instance and a specified object are equal.
`obj`: The object to compare with the current instance. | if and this instance are the same type and represent the same value; otherwise, . | — | | [`public bool Inno.Editor.Graph.ImGui.GraphCanvasPort.Equals(Inno.Editor.Graph.ImGui.GraphCanvasPort other)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasPort.cs#L10) | Indicates whether the current object is equal to another object of the same type.
`other`: An object to compare with this object. | if the current object is equal to the parameter; otherwise, . | — | -| [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasView`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L11) | Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. | — | — | -| [`public Inno.Editor.Graph.ImGui.GraphCanvasView.GraphCanvasView(Inno.Editor.Graph.GraphCanvasState state, System.Numerics.Vector2 origin, System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L33) | Creates a frame-local graph presenter over navigation owned by its document.
`state`: The existing neutral navigation and selection state.
`origin`: The screen-space canvas origin.
`size`: The finite nonnegative canvas extent. | — | `System.ArgumentOutOfRangeException`: The canvas geometry is invalid. | -| [`public float Inno.Editor.Graph.ImGui.GraphCanvasView.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L55) | Gets the graph-to-screen zoom captured for this frame, independently of later navigation changes. | — | `System.InvalidOperationException`: The view was not initialized through its constructor. | -| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasView.ToScreen(Inno.Core.Graphs.GraphPosition point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L66) | Projects a graph-space location into the current screen rectangle.
`point`: The domain-independent graph coordinate. | The corresponding screen coordinate. | — | -| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasView.ToGraph(System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L76) | Converts a screen location back into graph coordinates.
`point`: The screen coordinate to project. | The graph coordinate under the pointer. | — | -| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawGrid(uint color, float spacing = 32)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L89) | Draws a bounded graph-space grid through the current UI drawing context.
`color`: The packed presentation color.
`spacing`: Positive graph-space grid spacing. | — | `System.ArgumentOutOfRangeException`: Spacing is nonpositive or nonfinite. | -| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawConnection(System.Numerics.Vector2 output, System.Numerics.Vector2 input, uint color, float thickness = 2)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L120) | Draws a horizontal-tangent connection using the same curve used for hit testing.
`output`: The output endpoint in screen space.
`input`: The input endpoint in screen space.
`color`: The packed connection color.
`thickness`: The positive screen-space stroke width. | — | `System.ArgumentOutOfRangeException`: An endpoint or derived control point is nonfinite, or the stroke width is not positive and finite. | -| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasView.HitConnection(System.Numerics.Vector2 point, System.Numerics.Vector2 output, System.Numerics.Vector2 input, float radius = 6)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L177) | Checks pointer distance against the displayed cubic connection without any UI or document access.
`point`: The screen-space pointer location.
`output`: The displayed output endpoint.
`input`: The displayed input endpoint.
`radius`: Nonnegative screen-space acceptance radius. | True when the pointer is within the curve's hit area, including its endpoints. | `System.ArgumentOutOfRangeException`: Radius is negative or nonfinite. | +| [`public readonly struct Inno.Editor.Graph.ImGui.GraphCanvasView`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L12) | Projects and presents an arbitrary graph viewport without owning document mutations or domain semantics. | — | — | +| [`public Inno.Editor.Graph.ImGui.GraphCanvasView.GraphCanvasView(Inno.Editor.Graph.GraphCanvasState state, System.Numerics.Vector2 origin, System.Numerics.Vector2 size)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L34) | Creates a frame-local graph presenter over navigation owned by its document.
`state`: The existing neutral navigation and selection state.
`origin`: The screen-space canvas origin.
`size`: The finite nonnegative canvas extent. | — | `System.ArgumentOutOfRangeException`: The canvas geometry is invalid. | +| [`public float Inno.Editor.Graph.ImGui.GraphCanvasView.zoom { get; }`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L56) | Gets the graph-to-screen zoom captured for this frame, independently of later navigation changes. | — | `System.InvalidOperationException`: The view was not initialized through its constructor. | +| [`public System.Numerics.Vector2 Inno.Editor.Graph.ImGui.GraphCanvasView.ToScreen(Inno.Core.Graphs.GraphPosition point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L67) | Projects a graph-space location into the current screen rectangle.
`point`: The domain-independent graph coordinate. | The corresponding screen coordinate. | — | +| [`public Inno.Core.Graphs.GraphPosition Inno.Editor.Graph.ImGui.GraphCanvasView.ToGraph(System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L77) | Converts a screen location back into graph coordinates.
`point`: The screen coordinate to project. | The graph coordinate under the pointer. | — | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawGrid(uint color, float spacing = 32)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L90) | Draws a bounded graph-space grid through the current UI drawing context.
`color`: The packed presentation color.
`spacing`: Positive graph-space grid spacing. | — | `System.ArgumentOutOfRangeException`: Spacing is nonpositive or nonfinite. | +| [`public void Inno.Editor.Graph.ImGui.GraphCanvasView.DrawConnection(Inno.Editor.ImGui.EditorDrawList draw, System.Numerics.Vector2 output, System.Numerics.Vector2 input, uint color, float thickness = 2)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L127) | Draws a horizontal-tangent connection using the same curve used for hit testing.
`draw`: The current window's frame-local drawing borrow; it is neither retained nor released by this call.
`output`: The output endpoint in screen space.
`input`: The input endpoint in screen space.
`color`: The packed connection color.
`thickness`: The positive screen-space stroke width. | — | `System.ArgumentOutOfRangeException`: An endpoint or derived control point is nonfinite, or the stroke width is not positive and finite.
`System.InvalidOperationException`: The canvas is uninitialized or a visible connection uses an expired or mismatched drawing borrow. | +| [`public static bool Inno.Editor.Graph.ImGui.GraphCanvasView.HitConnection(System.Numerics.Vector2 point, System.Numerics.Vector2 output, System.Numerics.Vector2 input, float radius = 6)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs#L184) | Checks pointer distance against the displayed cubic connection without any UI or document access.
`point`: The screen-space pointer location.
`output`: The displayed output endpoint.
`input`: The displayed input endpoint.
`radius`: Nonnegative screen-space acceptance radius. | True when the pointer is within the curve's hit area, including its endpoints. | `System.ArgumentOutOfRangeException`: Radius is negative or nonfinite. | | [`public (System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum) Inno.Editor.Graph.ImGui.GraphCanvasView.NodeBounds(Inno.Editor.Graph.ImGui.GraphCanvasNode node, Inno.Core.Graphs.GraphPosition? previewPosition = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L27) | Projects neutral node geometry, optionally substituting a transient drag position.
`node`: The immutable display node.
`previewPosition`: An uncommitted gesture position, or null to use the document position. | The screen-space node bounds. | — | | [`public Inno.Editor.Graph.ImGui.GraphCanvasNode? Inno.Editor.Graph.ImGui.GraphCanvasView.HitNode(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 point)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L48) | Finds the frontmost node beneath a pointer using the same geometry as drawing.
`layout`: The current document presentation snapshot.
`point`: The screen-space pointer. | The frontmost neutral node, or null over empty canvas. | — | | [`public Inno.Editor.Graph.ImGui.GraphCanvasPort? Inno.Editor.Graph.ImGui.GraphCanvasView.HitPort(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 point, float radius = 8)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L81) | Finds a port by its displayed socket or label row, without interpreting its domain type.
`layout`: The current document presentation snapshot.
`point`: The screen-space pointer.
`radius`: The nonnegative minimum socket hit radius in screen pixels. | The hit neutral port, including Missing state, or null over non-port content. | `System.ArgumentOutOfRangeException`: The radius is negative or nonfinite. | | [`public System.Collections.Generic.IReadOnlyList Inno.Editor.Graph.ImGui.GraphCanvasView.BoxNodes(Inno.Editor.Graph.ImGui.GraphCanvasLayout layout, System.Numerics.Vector2 first, System.Numerics.Vector2 second)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L124) | Finds nodes intersecting a screen-space selection rectangle in document draw order.
`layout`: The current neutral display snapshot.
`first`: One corner of the selection box.
`second`: The opposite corner, in any drag direction. | Stable graph node IDs; no live domain objects are returned. | — | -| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasView.DrawNode(Inno.Editor.Graph.ImGui.GraphCanvasNode node, uint headerColor, bool selected, System.Action? drawContent = null, Inno.Core.Graphs.GraphPosition? previewPosition = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L164) | Draws a visible neutral node and its ports, borrowing optional content only for this call.
`node`: The immutable geometric and textual node input.
`headerColor`: The domain-selected packed header color; the presenter does not classify node types.
`selected`: Whether to display the current selection outline.
`drawContent`: Optional frame-local custom drawing. Exceptions propagate after restoring the node clip.
`previewPosition`: An uncommitted movement position, or null to draw the document position. | True when the node intersects the canvas and effective window clip and was submitted; false when culled. | — | +| [`public bool Inno.Editor.Graph.ImGui.GraphCanvasView.DrawNode(Inno.Editor.ImGui.EditorDrawList draw, Inno.Editor.Graph.ImGui.GraphCanvasNode node, uint headerColor, bool selected, System.Action? drawContent = null, Inno.Core.Graphs.GraphPosition? previewPosition = null)`](../../src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs#L173) | Draws a visible neutral node and its ports, borrowing optional content only for this call.
`draw`: The current window's frame-local drawing borrow; it is neither retained nor released by this call.
`node`: The immutable geometric and textual node input.
`headerColor`: The domain-selected packed header color; the presenter does not classify node types.
`selected`: Whether to display the current selection outline.
`drawContent`: Optional frame-local custom drawing. Exceptions propagate after restoring the node clip.
`previewPosition`: An uncommitted movement position, or null to draw the document position. | True when the node intersects the canvas and effective window clip and was submitted; false when culled. | `System.ArgumentNullException`: The node is null.
`System.InvalidOperationException`: The canvas is uninitialized or a visible node uses an expired or mismatched drawing borrow. | ## 项目依赖 | 工程 | 边界 | | --- | --- | | `Inno.Scripting.Api` | 实现依赖;不传递 compile surface | -| `Inno.Editor.ImGui` | 实现依赖;不传递 compile surface | +| `Inno.Editor.ImGui` | 公开签名依赖 | | `Inno.Core.Graphs` | 公开签名依赖 | | `Inno.Core.Serialization` | 公开签名依赖 | | `Inno.Extensibility.Catalogs` | 公开签名依赖 | diff --git a/docs/editor/Inno.Editor.ImGui.md b/docs/editor/Inno.Editor.ImGui.md index 4061427b5..286fa28a4 100644 --- a/docs/editor/Inno.Editor.ImGui.md +++ b/docs/editor/Inno.Editor.ImGui.md @@ -1589,11 +1589,11 @@ static void DrawHelp() | [`public static bool Inno.Editor.ImGui.ImGui.IsMouseButtonDown(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1094) | Gets whether a mouse button is held in the current Editor UI frame, including input consumed by UI controls.
`button`: The UI mouse button to query. | True while the button is held; callers must also check their item's active or hovered state before editing. | — | | [`public static System.Numerics.Vector2 Inno.Editor.ImGui.ImGui.GetMouseDragDelta(Inno.Editor.ImGui.ImGuiMouseButton button = Inno.Editor.ImGui.ImGuiMouseButton.Left, float threshold = -1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1113) | Gets accumulated pointer displacement from the current UI drag's starting position for timeline and canvas controls.
`button`: The button that owns the drag.
`threshold`: Movement required to start dragging, or a negative value to use the Editor's configured threshold. | Displacement in UI coordinates, or zero before the drag threshold is crossed or when no drag is available. The value is cumulative; it must be applied to a captured starting value, not added again each frame. | — | | [`public static uint Inno.Editor.ImGui.ImGui.ToPackedColor(System.Numerics.Vector4 color)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1130) | Converts a floating-point RGBA color to Dear ImGui packed color order.
`color`: The color consumed by to packed color; ownership remains with the caller unless explicitly stated otherwise. | The packed color value. | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1151) | Adds a line to the current window draw list.
`start`: The screen-space start point.
`end`: The screen-space end point.
`color`: The packed ImGui color.
`thickness`: The line thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1179) | Adds a rectangle outline to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius.
`thickness`: The outline thickness. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1205) | Adds a filled rectangle to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawCubicCurve(System.Numerics.Vector2 start, System.Numerics.Vector2 firstControl, System.Numerics.Vector2 secondControl, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1236) | Draws a cubic curve without exposing a native draw list or borrowed pointer.
`start`: The first screen-space endpoint.
`firstControl`: The control point beside the first endpoint.
`secondControl`: The control point beside the final endpoint.
`end`: The final screen-space endpoint.
`color`: The packed presentation color.
`thickness`: The positive stroke width. | — | — | -| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1261) | Adds text to the current window draw list.
`position`: The screen-space text origin.
`color`: The packed ImGui color.
`text`: The text to draw. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawLine(System.Numerics.Vector2 start, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1153) | Adds a line to the current window draw list.
`start`: The screen-space start point.
`end`: The screen-space end point.
`color`: The packed ImGui color.
`thickness`: The line thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1181) | Adds a rectangle outline to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius.
`thickness`: The outline thickness. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawFilledRectangle(System.Numerics.Vector2 minimum, System.Numerics.Vector2 maximum, uint color, float rounding = 0)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1207) | Adds a filled rectangle to the current window draw list.
`minimum`: The screen-space minimum corner.
`maximum`: The screen-space maximum corner.
`color`: The packed ImGui color.
`rounding`: The corner rounding radius. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawCubicCurve(System.Numerics.Vector2 start, System.Numerics.Vector2 firstControl, System.Numerics.Vector2 secondControl, System.Numerics.Vector2 end, uint color, float thickness = 1)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1238) | Draws a cubic curve without exposing a native draw list or borrowed pointer.
`start`: The first screen-space endpoint.
`firstControl`: The control point beside the first endpoint.
`secondControl`: The control point beside the final endpoint.
`end`: The final screen-space endpoint.
`color`: The packed presentation color.
`thickness`: The positive stroke width. | — | — | +| [`public static void Inno.Editor.ImGui.ImGui.DrawText(System.Numerics.Vector2 position, uint color, string text)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs#L1263) | Adds text to the current window draw list.
`position`: The screen-space text origin.
`color`: The packed ImGui color.
`text`: The text to draw. | — | — | | [`public enum Inno.Editor.ImGui.ImGuiButtonFlags : int`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L8) | Defines the supported button flags choices at the Editor presentation boundary. | — | — | | [`Inno.Editor.ImGui.ImGuiButtonFlags.None = 0`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L13) | Uses the ordinary behavior without additional options. | — | — | | [`Inno.Editor.ImGui.ImGuiButtonFlags.MouseButtonLeft = 1`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Controls/Options/ImGuiButtonFlags.cs#L17) | React on left mouse button (default) | — | — | @@ -2241,9 +2241,9 @@ static void DrawHelp() | [`public bool Inno.Editor.ImGui.EditorMenuRenderer.DrawSearchItems(Inno.Editor.Interactions.EditorInteraction interaction, System.Collections.Generic.IReadOnlyList items, string search)`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorMenuRenderer.cs#L364) | Draws matching leaf commands as a flat searchable list.
`interaction`: The interaction used to enqueue selected actions.
`items`: The immutable menu tree whose leaves should be searched.
`search`: The case-insensitive text matched against each slash-delimited leaf path. | when a command was selected. | — | | [`public readonly struct Inno.Editor.ImGui.EditorUiInput`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L8) | Copies frame input facts used by presentation without exposing native input storage. | — | — | | [`public bool Inno.Editor.ImGui.EditorUiInput.alt { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L13) | Gets whether Alt is held. | — | — | -| [`public bool Inno.Editor.ImGui.EditorUiInput.control { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L17) | Gets whether Control is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.control { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L17) | Gets whether the physical Control modifier is held, independently of native shortcut conventions. | — | — | | [`public bool Inno.Editor.ImGui.EditorUiInput.shift { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L21) | Gets whether Shift is held. | — | — | -| [`public bool Inno.Editor.ImGui.EditorUiInput.super { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L25) | Gets whether the platform super key is held. | — | — | +| [`public bool Inno.Editor.ImGui.EditorUiInput.super { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L25) | Gets whether the physical Super modifier is held, independently of native shortcut conventions. | — | — | | [`public bool Inno.Editor.ImGui.EditorUiInput.wantsText { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L29) | Gets whether an active control owns text input. | — | — | | [`public System.Numerics.Vector2 Inno.Editor.ImGui.EditorUiInput.mouseDelta { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L33) | Gets the current pointer movement in UI units. | — | — | | [`public float Inno.Editor.ImGui.EditorUiInput.wheel { get; }`](../../src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs#L37) | Gets the vertical scroll movement. | — | — | diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index d7ce47a29..d43af7e76 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -160,3 +160,48 @@ No item above closes its issue by itself. The PR remains Draft until the complet - `e16/loaded-package-shared.trx`: 3 passed; the isolated loaded-package mismatch contract also passed. Negative content belongs only to test-owned copies, never global NuGet or BGCS files. - The first full Contracts attempt was interrupted and is invalid: a test output was missing its native deployment and an overlapping test compilation encountered the live testhost's file handle. The unified builder now supplies the actual test product selection. The diagnostic dump was retired after preserving its text analysis; no production assertion, timeout or file-lock protocol was relaxed. - The Console eviction premise was corrected to its actual minimum capacity. Both real clear/evict cases pass against the existing focus logic; a speculative production focus change was withdrawn. + + +## Settings and output-owner correction (2026-10-28) + +- `e053be5b`: test-owned SDK metadata records the actual late-evaluated artifacts owner; repository architecture and Wiki share this input. Six public SDK/metadata cases pass. The registration fixture now consumes actual Foundation TargetOutputs; its official targeted rerun passes. +- `a4d975ab`: fixes the real cross-owner Settings Apply failure through existing History, plus staged retry, Faulted semantics, current-generation reload admission and unexpected Drawer retirement. Four real-native cases pass, including one hundred normal cycles and a red/green Drawer failure regression. The drawn-value weak test is not private Session/ALC leak proof. +- The completed `af6eac31` Contracts operation remains Failed (two real test-owner failures). A corrected-owner Wiki rerun found one new explicit XML omission, now fixed. Final builds/profiles/Wiki/CI are running; do not treat a checkpoint commit as whole-plan completion. +- Two actual CLI roots select the same SDK-owned 167 references despite an interference pack directory; an incompatible .NET 9-only host fails explicitly. This is not evidence of two different installed .NET 10 SDK patches. +- E11 remains blocked for same-input original FlappyBird: current plugin source needs the later EditorUiSession API. No legacy adapter, changed input or synthetic benchmark substitutes for that product requirement. + +## Controlled performance and SDK follow-up (2026-10-28) + +- Two actual supported SDK patches (10.0.401 and 10.0.402), their 167 real references, Runtime/actual IDE agreement and same-project SDK switching are verified in e19-dual-sdk/verified-current-result.json. Root-only tests remain separately scoped. +- The first clean five-pair graph matrix fails the CPU/P95 threshold. Issue #34 retains that failure; draw-borrow, label-width and frame-input reuse are being verified without removing native lifetime checks. +- New #45 owns the Core allocation defect and unrelated getter capture. Four red cases reproduce it; all 46 Core serialization cases pass after correction. Final product and controlled performance verification remain required. +- Under #37, existing reload fixtures are being extended to ten real cycles without weakening the strict unload barrier. +- The parent issue and Draft PR remain incomplete. Whole-product same-input original FlappyBird performance is blocked by the documented original API/input mismatch. + +## Current correction candidate gates (2026-10-28) + +| Gate | State | Evidence and limits | +| --- | --- | --- | +| Twelve-project SDK compilation | 通过 | final-owned-native-suites.log/binlog; zero compile errors; environment NU1900 warnings are preserved | +| Native color oracle and owner retirement | 通过 | e45/color-final-owned.trx; 4,387 numeric vectors and rejection after owner disposal | +| Core serialization and Scene | 通过 | e45/serialization-scene.trx; 46 + 53 cases across both real assemblies | +| Workflows | 通过 | unified-correction-final/88902e2955954ce8aa0169c2fffb84c3; 302 cases, including the isolated real Action/menu reload loop | +| Visual automated profile | 通过 | unified-correction-final/19006ef0cc77448b8de9ec2fd06b2a76; 136 cases; native simulated metrics are not physical-display certification | +| Lifecycle automated profile | 通过 | 11 cases in unified-correction-final; per-panel resource inventory remains separately tracked | +| Full Contracts | 通过 | 713 cases on the pre-final correction candidate, cf4d562d8bf1413f945d8f23e5d1d712; later Core/modifier corrections require final verification; earlier failed operations remain Failed | +| Controlled final performance | 待验收 | Must run without overlapping builds/tests; original/current first clean graph regression remains recorded | +| Original FlappyBird common-input comparison | 阻塞 | Current plugin source requires APIs absent from the original production baseline; no compatibility shim or substituted input | +| macOS physical/manual acceptance | 待验收 | Not executed: no local Mac; remote automated CI is reported separately and does not replace manual acceptance | + +Native deployment ownership is recorded by the actual SDK in native-test-ownership.json: four of the selected twelve projects declare the Editor native product. Shared builders do not override project declarations. Earlier initial Pending rows above are the execution baseline, not a claim that the corresponding implementations are absent. + +The native color candidate using ordinary integer conversion failed for NaN. The standard native-conversion correction passed all numeric comparisons; a separate overstrict test assertion was corrected to the actual owned-context lifetime. Production admission was not weakened. + +## Final Core and native input boundary verification + +- The complete formal Core suite passes 49 cases after the second allocation correction. A prepared context previously allocated the same 2,096 bytes per property capture as a fresh context. The official output now measures 1,888 fresh / 1,168 prepared with identical payload bytes; this is an allocation observation, not a controlled CPU claim. Scratch collections remain operation-owned, and foreign contexts rebind all required owners without mutating caller services. +- The copied diagnostic Core harness failed TypeRef registration even with unchanged production source. It is not production acceptance evidence; the actual SDK project passes TypeRef and all remaining Core tests. +- macOS CI at 660962a5 passed builds, renderer smoke, architecture and Wiki, but failed seven native input cases. Windows and both managed CI jobs passed. Native ImGui swaps Control/Super internally in command-key mode; presentation snapshots now restore physical Core semantics and list navigation consumes that same boundary. Fixtures declare their native convention, and a four-case real-input matrix covers both primary modifiers and both native modes. Final verification and ARM64 CI are pending. +- PR #26 and the parent issue remain incomplete. Full workload/product/input/visual/lifetime acceptance must retain its actual limitations; automated fixture success does not certify physical devices or the entire original plan. + +Final working-tree verification: the twelve-project compilation, Core 49 / Scene 53, Workflows 305, Visual 136, Lifecycle 11, Editor Debug/Release, Build entry, six 300-frame FlappyBird renderer smokes, three official companion script/IDE projections, and the 241-project architecture/Wiki gate pass. Evidence uses `final-candidate*`, `products/final-candidate-*`, `companion-projections-final` and `wiki-final-candidate`. Controlled performance and the new committed-source CI remain separate pending gates; this does not close the parent or the original full acceptance matrix. diff --git a/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md new file mode 100644 index 000000000..109bdf088 --- /dev/null +++ b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md @@ -0,0 +1,269 @@ +# Editor UI 闭合实施与验收记录 + +[执行台账](../EDITOR_UI_CLOSURE_PLAN.md) · [设计规范](../../../docs/architecture/EDITOR_UI_DESIGN_STANDARD.md) · [总 issue #27](https://github.com/Wolfiora/InnoEngine/issues/27) · [PR #26](https://github.com/Wolfiora/InnoEngine/pull/26) + +## 结论与适用范围 + +本轮已经完成实质性源码实现和多项真实原生、SDK、插件及产品验证;**尚不能声明原计划全部闭合,也不能把“企业级、现代级、现象级”作为已经通过的测试等级**。PR 保持 Draft,未授权自动合并。各 issue 维持 Open,完整验收的条目才可由合并时的 `Fixes` 关闭。 + +主要实现提交为 `63d4f7dce8b21daa22a6f7e5a00825be3f46dd2d`;资产刷新修复为 `273c42ad32be511c21fa21b1e99626af708f29c8`。author、committer 均为 Aaron Liao,无 co-author。后续修复提交为 `8dcceb94`(引导 scope)、`e23f4ba5`(源内容完整性)、`14338d61`(SDK 架构 owner)、`081f41f9`(测试临时 owner);均为同一作者及提交者,无 co-author。后续提交 `af6eac31` 修正 smoke 的产品预期;`e053be5b` 统一测试的 SDK 输出 owner;`a4d975ab` 修复 Settings 原子 Apply 与异常退休;`0974c317` 同步当前文档。 + +本机是 Windows;macOS 真实显示器与人工 Metal 操作未执行。模拟 DPI、Native Host 和远程 CI 分别报告,不互相替代。 + +## 最新候选复验状态 + +下表优先于后文按阶段记录的历史结果。原始失败、无效运行和未执行项仍保留,不由新结果改写。 + +| 范围 | 状态与证据 | +| --- | --- | +| 完整十二工程测试图 | 通过;`final-candidate-suites.log/binlog`,0 编译错误;保留 NU1900 漏洞服务网络警告。 | +| Core / Scene | 通过;49 + 53 项,`final-candidate/core-scene-final.trx`。 | +| Workflows | 通过;305 项,`final-candidate/9409749fa03d4badb6f3b34c99a3dcdc`。 | +| Visual / Lifecycle | 通过;分别 136、11 项,`final-candidate` 中对应 profile 的 summary / TRX。套件有交叠,不能直接相加。 | +| Editor Debug / Release、Build 入口 | 通过;`final-candidate-editor-*.log/binlog`、`final-candidate-build-entry.log/binlog`,0 编译错误。 | +| FlappyBird 产品 | 通过;独立的当前 Samples 源码副本,Debug/Release × D3D11/D3D12/Vulkan 六组,每组 300 帧、正常退出;`products/final-candidate-*.log`。该 smoke 不替代完整人工旅程。 | +| Canvas / Rendering2D / FlappyBird | 正式脚本编译及 IDE 投影通过;`companion-projections-final/*/generation.log`。未编辑派生代码或插件包。 | +| 完整 Contracts | 前一修正候选 713 项通过,`unified-correction-final/cf4d562d8bf1413f945d8f23e5d1d712`。后续 Core / modifier 修正后的最终完整 CI 另行记录。 | +| 最终 Wiki / 架构 | 通过;`wiki-final-candidate`,241/241 工程实际归属、API 清单与可编译示例门禁。网络 NU1900 警告原样保留。 | +| 最终成对性能 | 待验收;必须在所有构建、测试和产品进程结束后单独采样。 | +| CI | `660962a5` 的 Windows 及两个 Managed job 通过;ARM64 自动构建、renderer、架构、Wiki 通过,但有七个原生输入失败。已修正呈现边界与 fixture,待新提交 CI。 | + +Core 内部现在只在实际需要时创建回调、循环检测和恢复去重集合,复用 BCL reference equality;上下文仅在全部 owner 身份一致时复用。正式输出的同一属性 bytes 保持一致,分配测量为 fresh 1,888 / prepared 1,168 bytes,原 prepared 为 2,096。新公开边界回归先失败再通过;没有修改 `SerializationContext.With` 的新实例语义。该观察不是整帧或全产品 CPU 收益证明。 + +原生 command-key 模式会在 ImGui 内交换 Control / Super。`EditorUiInput` 现在还原物理 Core 修饰键含义,列表选择复用该边界;原生文本控件保留自身配置。四个真实输入组合验证两种 primary modifier 与两种原生模式。共享 Editor 不增加 OS 检测或平台引用,fixtures 显式选择键盘配置。 + +证据根目录:`D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`。表中的路径均相对此目录。日志、TRX、binlog、截图和大规模输入不进入源码仓库;本记录与执行台账提供可追溯索引。 + +## 实现与职责自查 + +| 位置 | 实际改动与边界 | +| --- | --- | +| Interactions | 静态菜单及快捷键结构按 generation 建索引,动态可用性与请求数据继续即时解析;不建立全局缓存或第二套 Action 系统。 | +| 通用 Graph / Graph.ImGui | 文档与 History 保持中立;节点、端口几何、命中与 pointer intent 可供非 Shader 图使用。Shader controller 保留语义解释和领域修改。 | +| ImGui presentation | Session 拥有 transient state;原生帧准备与绘制分离,同一帧不能提交新的 Zoom/Density。真实 callback 失败结束所属帧并保留原异常。 | +| Domain presentation | Browser、Console、Hierarchy、Settings、Inspector Tag/Layer 的专属值归对应 owner;通用 palette/metrics 只保存基础与复用组件值,删除旧成员后直接更新调用方。 | +| 大视图 | Hierarchy、Browser 使用 revision/展开/过滤决定的中立可见序列与原生裁剪;Console 使用有界槽位与测量缓存;Graph 局部更新几何。 | +| Statistics | 无变化时复用不可变快照,Panel 有界保存 0.5 秒间隔、60 秒窗口、最多 120 个 timing 样本。 | +| Scripting Compiler | 只消费中立冻结输入,不引用 Build、MSBuild 或命名平台;实际 SDK 求值由 Host/Build provider 提供,取消与失败不发布半成品。 | +| Tooling / Build | 读取 SDK 选定的 assets、references、compile items、defines 与输出 owner,不用过期默认 bin/obj 代替当前请求。 | +| AssetFileSystem | 在既有 Core Identity/store 内准备完整候选,复用未变化 entry,失败保留原视图,只退休移除内容;没有 UI fallback 或平行 live-object authority。 | + +已经沿实际消费链复核:Feature 不依赖 Native presentation,Graph 不依赖 Shader 世界观,平台事实没有移入通用执行器,History 继续使用既有中立 payload 协议。跨帧选择和延迟目标使用 Core Identity/弱引用;同步 `EditorInteraction` 对目标的强借用在 XML 和规范中明确,不能误当成长生命周期缓存。 + +新增公开表面仅用于实际消费者:通用 Graph 的呈现输入及交互结果、SDK 冻结脚本输入、真实 Host 调用的帧准备协议和 hierarchy revision。内部 Console 过滤、样式值与呈现缓存没有因测试而公开。 + +没有增加 Events、Identity、History、Serialization、插件发现或缓存管理的平行实现。BGCS 源码、NuGet 包、第三方源码和生成绑定不属于本轮修改;负向程序集内容仅在测试自有副本中构造。没有加入 global/implicit using、IVT、私有反射穿透、兼容 reader 或旧 API 转发。 + +## 已执行证据 + +以下列出当前候选对应的专项结果;它们存在交叠,**不能相加成互不重复的总测试数**。 + +| 验证 | 实际结果 | 证据 | +| --- | --- | --- | +| 完整 SourceDebug 源码编译 | 通过 | `source-frame-final.log`、对应 binlog | +| Windows Editor Debug 构建 | 通过,0 errors | `editor-frame-final.log`、对应 binlog | +| Native ImGui context/frame/metrics/controls | 72 通过 | `e03/all-native-frame-contracts.trx` | +| 统一 Workflows profile | 297 通过 | `unified-final/2a003d59a27a4c22b65ff5ac2b285170/summary.json` 及 TRX | +| 统一 Visual profile | 135 通过 | `unified-final/*/summary.json` 中 Visual 记录及 TRX | +| 统一 Lifecycle profile | 5 个循环用例通过 | `unified-final/*/summary.json` 中 Lifecycle 记录及 TRX | +| 实际 package / loaded assembly 身份 | 普通 3 通过;独立进程 1 通过 | `e16/loaded-package-shared.trx` 与 isolated TRX | +| SDK / C# 14 / IDE 冻结输入 | 11 通过 | `e19/sdk-contract-final.trx` | +| SDK-owned tooling owner | 完整 153 项通过,包含实际 SDK/当前输出 owner 的仓库级检查 | `corrections/Inno.Tooling.Architecture.Tests/formal-complete.trx` | +| Asset pipeline / source identity / writable content | 完整 123 项通过,包括保留时间戳的源与依赖变化、被锁定源的 last-good 诊断 | `corrections/Inno.Assets.Pipeline.Tests/complete.trx` | +| Console Clear / 真正 eviction | 2 通过 | `e07/console-retired-real-before.trx` | +| 实际九类 Panel 100 次循环 | 执行完成;不替代完整资源计数矩阵 | `e10/panel-lifecycle-final.tsv`、产品日志 | + +真实 package/load 负例保持程序集名称及 MVID 相同,仅改变测试副本内容,实际加载后由内容身份拒绝;随后执行 Full GC → finalizers → Full GC 并确认 collectible ALC 不可达。没有通过删除默认 obj 或清理全局 NuGet 使测试变绿。 + +C# 14 用例覆盖 `field`、空条件赋值、Span 重载、泛型 `nameof`、lambda 修饰符、扩展成员与跨插件 IDE 引用。正式输入一致性已验证。额外公开 SDK 探针验证了两个真实 CLI 根、显式 10.0.402 选择、干扰 pack 目录和不兼容宿主,详见下文;其后完成真实 10.0.401/10.0.402 的不同 patch 验证,详见后续复验。 + +### 真实 FlappyBird 操作 + +独立副本 `products/FlappyBird-functional-final` 使用当前正式插件包和 D3D12 Editor: + +1. 正常打开 FlappyBird Scene,Scene/Game、Hierarchy 与 Browser 内容正常。 +2. 使用公开快捷键/可见 toolbar 进入 Play,并返回 Edit;观察实际 action tooltip 和场景恢复。 +3. 返回 Edit 后 Browser 目录树及 `~Authoring` 文件列表保持可见;之前 #43 的空列表问题未再出现。 +4. 从 Edit 菜单打开 Settings,修改 Density,确认 Apply/Cancel 状态;Cancel 后重新打开仍是保存的 Comfortable。 +5. 正常关闭窗口,进程 exit code 0,日志明确记录 product resources released 和 Dispose end。 + +证据:`products/editor-functional-final.log`、`products/FlappyBird-functional-final/Logs/EditorBoot.log`、`e09/flappybird-final-play-stop-browser.png`、`e09/flappybird-final-settings-cancel-reopen.png`。 + +此前同一实现阶段的真实 Inspector 单轴编辑、Undo、Redo 及回到原值保留于 `e09/flappybird-inspector-*.png`。它们是实际功能证据,最终版本完整多功能/失败/取消矩阵仍须独立说明。 + +### Release 工具图回归 + +最终 Release 冷构建发现 `BuildingInsideVisualStudio=false` 与未声明该属性造成两个 generator build identity;binlog 显示两个 Csc 同时写同一输出。SDK 提到可能的防护进程不是本报告确认的根因。已在原有 SDK-negotiated compiler-tool boundary 规范化该宿主事实,没有停止用户进程、关闭安全设置或串行化整个构建。 + +扩展的真实 SDK fixture 在修复前 Debug/Release 均失败,修复后两项通过;每项包含冷构建、未变化热构建和修改 Tool 源码后的重建。证据为 `editor-release-final.binlog`、`e40/release-graph-failure.json`、`e40/ide-graph-before-selected-host.trx`、`e40/ide-graph-after.trx`。随后产品 Release 又复现普通 Catalogs 输出冲突;此失败不能算通过。进一步定位到 Foundation 引导剥除调用方属性却写同一源码输出。`8dcceb94` 把中立引导放到独立且固定的 SDK scope,保留发布参数隔离;27 个完整引导回归通过,包括 Debug/Release 并行图、热构建和 Browser/AOT/RID 请求。证据 `e40/stable-bootstrap.trx`。最终产品重跑另列。第一次直接 VSTest 没有传入选定 DOTNET_HOST_PATH,使用了机器另一个 muxer;该运行不作为图缺陷证据。 + +### 证据纠正与无效运行 + +- 第一轮完整 Contracts 操作缺少其自有 Native deployment,之后又发生与仍运行 testhost 重叠的测试输出构建。该操作已经中断,`contracts-interrupted.json` 明确标为无效,不能计作全部通过。 +- 统一 builder 已传递正式 `InnoTestNativeTarget` 和 `InnoTestNativeProduct`。未放宽文件锁协议、生产校验或超时。 +- Console eviction 初始用例没有达到实际最小容量 16,不能证明 eviction 缺陷。已纠正前提;真实 Clear/evict 测试通过,推测性的 production focus 改动撤回。 +- 诊断 dump 在保留文本栈和原因后,仅从本轮明确 owner 删除;未清理用户文件或 SDK/NuGet 全局缓存。 + +### 后续完整性与编排修复 + +- [#44](https://github.com/Wolfiora/InnoEngine/issues/44):Windows CI 暴露同长度快速改写返回旧内容,保留时间戳的用例确定性复现。可写源及 Source 依赖现验证实际 SHA-256;采用标准库流式读取,保持既有失败诊断/last-good 恢复,不修改 ID、sidecar、BGCS 或只读安装 generation 协议。123 项完整 Asset Pipeline 测试通过。增加必要内容读取的成本单独报告,不能称为免费优化。 +- #41:图检查原本过早 ResolveReferences,给纯 graph fixture 创建输出并对无 Foundation 的 fixture 抛错;改为按语义用途惰性获取框架引用。完整仓库测试改用 SDK 及编译时记录的实际 ArtifactsPath。轻量 fixture 仍为两分钟;新增的完整 SDK 检查独立设置十五分钟预算,实际完整套件为 6 分 13 秒。SDK 单请求与 ALC unload barrier 的原超时没有放宽。 +- 测试临时目录改为显式 `-TemporaryDirectory`;默认保留 OS 环境。本轮短 D: owner 下原失败的 9 个 SDK/CMake 用例通过。深结果目录和源码调整期间中断的旧运行均不计入通过。 + +## 当前关键文件树 + +```text +build/msbuild/Inno.Build.Tasks.targets # 中立 SDK Foundation scope +build/pipeline/Inno.Build/Scripting/ + SdkScriptCompilationEnvironment.cs # SDK -> 中立冻结输入 +src/runtime/scripting/Inno.Scripting.Compiler/ + IScriptCompilationEnvironmentProvider.cs + ScriptCompilationInputs.cs +src/content/assets/Inno.Assets.Pipeline/ + Sources/AssetFileSystem.cs # 候选索引与稳定身份 + Importing/AssetLoader.SourceIO.cs # 内容验证与稳定读取 + Importing/AssetLoader.Dependencies.cs +src/composition/editor/ + framework/Inno.Editor.Interactions/ # Action/Menu/Selection/History + presentation/Inno.Editor.Graph.ImGui/Canvas/ + presentation/Inno.Editor.ImGui/Runtime/EditorUiSession.cs + presentation/Inno.Editor.ImGui/Styling/ + presentation/Inno.Editor.ImGui/Widgets/ + presentation/Inno.Editor.Assets.ImGui/Styling/ + panels/Inno.Editor.Panel.Logging/Console/ + panels/Inno.Editor.Panel.Logging/Styling/ + panels/Inno.Editor.Panel.Hierarchy/Presentation/ + panels/Inno.Editor.Panel.Hierarchy/Styling/ + panels/Inno.Editor.Panel.Inspector/Styling/ + panels/Inno.Editor.Panel.Settings/Styling/ + panels/Inno.Editor.Panel.Stats/ +tools/Inno.Tooling.Architecture/ + DocumentationSourceModels.cs + WikiProjectReferences.cs +tests/Invoke-EditorUiAcceptance.ps1 +tests/Invoke-IsolatedTests.ps1 +tests/fixtures/editor-ui/PanelLifecycle.editor.cs +``` + +该树列出职责 owner 与关键改动;其余已有功能位置保持,完整文件差异以对应提交为准。没有为目录对称新建空项目。 + +## 必须保留的阻塞与未完成项 + +| 项目 | 当前事实及重新验收条件 | +| --- | --- | +| E11 原版 FlappyBird 同输入比较 | 原基线 `b303d9f1` 的正式工具构建成功,但当前插件源码依赖原版不存在的 `EditorUiSession`,正式脚本编译失败。日志 `e11/original-flappy-projection.log`。不能替换输入或加兼容层后称相同输入。需要可在双方正式接口上运行的、经认可的共同产品输入/基线定义。 | +| 原始完整计划文本 | 桌面文件现为闭合计划,hash `cbb7d50a…`;旧原始 UI 文档只有复审记录的 hash,不能把二者说成同一文件。现按获批闭合计划和复审矩阵执行。 | +| 完整工作流 | 已执行多条真实旅程和 handler/contracts,但每类界面正常、失败、取消、关闭重开四条路径尚不能全部逐格记为通过;Settings 跨 owner 部分失败已经真实复现并修复,四项 Native 回归通过;其余完整旅程仍需逐项闭合。 | +| 完整视觉矩阵 | 真实 Native simulated metrics 与部分产品截图已执行;匹配原版/当前的完整截图、所有浮动停靠与物理显示器组合没有全部实测。 | +| 完整生命周期收敛 | 100 次 Panel、Scene/Shader document 和 reload 用例不等于每个 owner 的全部订阅、手势、引用计数已证明收敛。不能用单次 Host 正常退出替代。 | +| #19 多 SDK 环境 | 已验证真实官方 10.0.401/10.0.402、167 个实际 references、Runtime/IDE 一致性及 SDK 切换缓存身份;Mac 实机未执行。 | +| 最终完整 Contracts 与 CI | 分别记录最终运行结果。旧 head 的绿灯、已中断运行或单个测试不替代同候选完整结果。 | +| macOS 人工设备 | 本机无 Mac;远程 ARM64 CI 仅证明其实际自动步骤,不能证明人工 Metal、音频、显示器和操作体验。 | + +因此总 issue #27、E08–E12 等不得被无条件标记完成。没有验证的项继续保持 Open;阻塞没有被改写成缺陷已解决,也没有归责 BGCS。 + +## 十一项质量评价 + +判断针对可复查工程标准,不提供虚构的行业认证或“完美”分数。 + +| 维度 | 判断与证据 | 仍需改进/证明 | +| --- | --- | --- | +| 架构 | 分层已有实质改善:领域、交互、呈现、Host/SDK 有真实消费者和依赖边界。 | 完整最终门禁及所有生命周期 owner 证明完成前,不宣布整个 Editor 达到全部原标准。 | +| 代码整洁度 | 文件按功能 owner 收拢,删除旧转发,API/引用/Wiki 同步。 | 持续审查复杂 Panel 的职责规模;不把更多拆文件当作质量本身。 | +| 代码干净程度 | 修复实际 source identity、frame failure、同帧尺度问题,未用 fallback 掩盖。 | 完整异常、取消、资源收敛矩阵仍需补齐。 | +| 抽象程度 | Graph 有非 Shader 消费者;SDK 输入中立;帧准备有真实 Host 调用。 | 第二种完整图产品旅程与所有扩展失败路径尚不等于全部验证。 | +| 可复用程度 | 复用 Core Identity/Events、既有 History/Serialization 和 Session controls。 | 继续以真实调用方证明复用,不建立通用却无消费者的设施。 | +| 样式统一 | 基础/组件/领域三层归属落实;同帧尺度契约已测。 | 完整匹配截图、窄布局与物理 DPI 体验仍有缺口。 | +| 可拓展程度 | generation-owned 索引、最小 Graph contract、SDK provider,无中心 Shader 白名单。 | 完整 reload/ALC 和第三方实际扩展旅程需要持续验证。 | +| 操作逻辑性 | 键盘、Console 搜索、真实 Play/stop、Settings Cancel 已测。 | 全界面的失败、取消、关闭重开逐格证据尚不完整。 | +| Undo/Redo | 继续单一 History,真实 Inspector edit/undo/redo 与原子 handler 回归有证据。 | 各结构化批量操作、Settings 多 owner 失败、重试、单次 Undo/Redo 已补齐;其余结构化批量修改及 Missing 恢复旅程仍需完整矩阵。 | +| 菜单清晰度 | 静态结构复用、动态状态即时,真实 Edit 菜单 disabled Undo/Redo 正确。 | 全部 area/动态贡献、冲突和可发现性的最终旅程与性能需闭合。 | +| 组件颗粒度 | 通用与领域 owner 更明确,内部缓存/样式未扩大 public 表面。 | 保持公开边界最小且完整;复杂 controller/session 的长期规模仍需定期复核。 | + +总体判断:有实质性工程进展,仍未达到原计划的全部完成条件。不能将局部强项概括为所有十一项已经“超级无敌优秀”。 + +## 资源、提交与后续结果 + +大型输入、baseline、binlog、TRX 与截图集中在 D 盘。保留现行有效产物和活动 reader,维护通过既有 owner/lease 协议进行,不手删协调锁或全局依赖缓存。完成时核对实际磁盘空间、companion/BGCS 状态、最终提交和 CI,并在此追加精确结果。 + +本环境没有 `/System/Library/Sounds/Glass.aiff`,不能播放指定提示音。 + +## Settings 与测试输入 owner 的专项复验 + +### 实际缺陷、失败证据与修复 + +Release Editor 在独立样例副本中复现:Editor Density 已写入后,Build settings 的独占文件锁使 Apply 失败;原 Settings Modal 随后被隔离,留下部分成功的修改。证据为 `products/settings-partial-apply-before.log`、`e08-settings-partial-apply-before.json`、`e09/settings-partial-apply-before.jpg`。这是现行实现的真实失败,不是 BGCS 缺陷。 + +修复沿原有 `EditorInteractions.history` transaction 处理 Editor、Project、Build 的一次 Apply;成功后才接受 draft baseline。失败保留 staged values,局部显示状态并保留完整 Console 诊断。回滚失去完整性时保持 History Faulted,不能继续 Apply。Settings 窗口拥有当前 generation 的借用与原有 reload guard;异常 Drawer 经标准隔离流程退休 transient owner,重建 catalog 后再允许打开。没有增加平行 History、Recovery、Events 或 Identity。 + +`e08-settings/green-official.trx` 四项通过: + +1. Editor/Project 中途失败后全部恢复,重试成功,形成一次 Undo/Redo。 +2. 回滚本身失败后保持 Faulted,修复磁盘前提也不会偷偷恢复 Apply。 +3. 100 次打开/关闭后,公开 Drawer 接收的值可被收集,reload admission 恢复。 +4. Drawer 抛异常后释放 reload guard,菜单禁用直到真实 catalog rebuild,再正常打开与关闭。 + +第 4 项在修复前实际失败(`red-official.trx`)。第 3 项在旧实现也通过:它观察的是 Drawer 获得的副本,**不能证明旧 Session 泄漏,也不证明全部私有 Session 或 ALC 已回收**。移除 Modal 对旧 session 的不必要强引用是代码所有权修正,不能伪称已经复现了该泄漏。各运行使用 SDK 选定的 Editor product props;早期错误选择普通 ProductBuild.props 的诊断运行单列,不作为最终产品构建。 + +### 完整 Contracts 失败与实际 owner + +`unified-stable/554c93c1e0234bb880a7ae94623fbf2f` 是一次完成的失败运行,不能改写成通过:Build.Tests 379 通过、2 失败、2 个平台条件跳过;其他六个普通/隔离文件分别为 26、2、130、153、13、1、1 通过。各 profile 有重叠,不累计成唯一测试总数。 + +两个失败分别为 Wiki 漏传独立 ArtifactsPath,以及 Task fixture 猜测已经退出的默认 Foundation 输出。Wiki 原先已经传了 SDK 入口;问题是输出 owner,不能说成它完全没有 SDK 输入。Task fixture 改为读取实际 MSBuild TargetOutputs,保留文件句柄释放、两次私有 publisher 身份和零 Copy 重放断言。 + +测试 source 项目新增 `TestRepository.GetArtifactsPath(Assembly)` 只读取 SDK 生成的公开 assembly metadata。统一 targets 在 SDK 算出输出之后记录该事实;没有引擎内部访问后门、生产 API 扩大或第二个路径 resolver。六项真实 SDK/metadata 用例通过,包括默认/显式 owner 和损坏 metadata 拒绝。目标注册测试正式复验通过;同轮 Wiki 产生新的 241 工程库存,并指出新增 Dispose 漏 XML 一项,现已补齐。首次直接 VSTest 漏了官方入口的 PATH 环境而用了 .NET 9 muxer,该运行保留但不归责实现。最终完整 Wiki 复验另列。 + +### 多 root 与 pack 干扰 + +`e19-root-probe/result.json` 使用生产公开 SDK capture:已安装根与测试自有 D 盘 CLI 根都明确选择 10.0.402,167 份 framework references 内容一致;D 根只消费自己的 pack 路径,`10.0.999` 干扰目录不被选择。不兼容 .NET 9-only 宿主明确失败,没有 fallback。临时求值工程正常退休。 + +D 根的 host/sdk/shared 使用测试自有 junction 指向现有安装;干扰 pack 是测试副本,不是另一个实际 SDK 安装。没有修改安装 SDK、NuGet 或消费者 global.json。因此这是根选择和干扰拒绝证据,不冒充多个不同 SDK patch 或 Mac 实机矩阵。 + +## 后续实际 SDK 与产品复验 + +以下结果对应 660962a5 及此前实现;后续 Core/Graph 改动需要自己的最终验证。 + +- SDK 10.0.401 选择 reference pack 10.0.12;10.0.402 选择 10.0.13。401 根下存在真实 13 pack 的受控 junction,也继续选择 12。相同 C# 14 源码的 Runtime 编译与实际生成 IDE 工程均通过;167 个 reference 路径及内容在各自操作中一致。官方 401 安装包经 SHA-512 验证,只安装在本次 D 盘 owner。 +- 同一测试 Project 执行 401 → 402 → 402 → 401:新编译、新编译、缓存复用、缓存复用。不同 SDK 使用不同产物。证据 e19-dual-sdk/verified-current-result.json、switch-0.json 至 switch-3.json。引擎副本与当前正式 producer 内容一致。 +- Canvas、Rendering2D、实际 Samples/FlappyBird 正式完成脚本及 IDE 投影;companion 仓库 Git clean。证据 companion-projections//generation.log。 +- Editor Debug/Release 和 Build 入口构建 0 errors;editor-Debug-settings-final.log、editor-Release-settings-final.log、build-entry-settings-final.log。 +- 七个 UI 工程正式图编译 0 warnings/errors,final-ui-suites-build-corrected.log。660962a5 修复统一入口遗漏 SDK 实际 ToolTarget 的问题,没有新增平台推断。 +- Workflows 301、Visual 135、Lifecycle 6 个用例通过,unified-settings-final/ 的 summary/TRX;完整 Wiki 与 Task registration 两项通过,final-owner/final-owner.trx。各项结果存在交叠。 +- Debug/Release × D3D11/D3D12/Vulkan 六个 FlappyBird smoke,各 300 帧且正常退出,products/settings-final--.log。只证明有界启动、渲染和资源退休。 +- 最终真实 Settings:Build 文件独占时 Apply 失败,Editor 设置文件 SHA-256 保持基线;释放故障后重试成功,一次 Undo 恢复两份基线,一次 Redo 恢复两份成功文件。证据 e08-settings/final-failure-atomicity.json、after-single-undo.json、after-single-redo.json 及 e09/settings-atomic-after-*.jpg。随后 Play/stop 和正常退出完成。 + +## 性能复审与修复追踪 + +首次有效比较 e11/pairs-57543dce399247dfa6551b6351621c39:三个负载各五组交替原版/当前进程,共 30 个成功进程。相同冻结输入、SDK、配置及原生命令校验,不代表真实 GPU 或整个产品性能。 + +| 负载 | 原版 median / P95 ms | 修复前当前 median / P95 ms | 修复前当前 bytes/frame | +| --- | --- | --- | --- | +| 10,000 Hierarchy 对象 | 4703.73 / 4959.41 | 470.16 / 511.64 | 655,611,454 | +| 10,000 Assets 目录 | 3678.02 / 4049.01 | 16.29 / 24.56 | 998,359 | +| 2,000 Shader 节点 | 25.00 / 27.55 | 30.21 / 36.83 | 4,171,160 | + +Graph 回退不满足要求;Hierarchy 即使更快,绝对分配仍过高。输入及 2,150 个依赖文件采样前后保持一致,e11/before-correction-integrity.json。更早首轮有准备重叠风险,整轮无效,e11/invalid-first-release.json;未挑选较好数据。 + +真实 [#45](https://github.com/Wolfiora/InnoEngine/issues/45) 跟踪 Core 修复:小属性编码不再预分配 16 KiB;单属性捕获不执行无关 getter。沿既有 Serialization pipeline 实现,没有新 serializer、池、缓存或 schema。修复前 4 项失败,修复后 Core 全部 46 项通过,e45/red.trx、e45/complete.trx。输出 bytes、大小增长、异常及属性恢复有公开契约覆盖。 + +受控副本只替换该 Core producer 的诊断探针,Hierarchy 分配约 655.6 MB → 171.8 MB/frame。该次与 trace 重叠,耗时不能作最终结果;剩余 full Scene dirty 检查成本如实保留,不延长间隔或使用不完整 revision 隐藏直接属性修改。 + +Graph 修复归 [#34](https://github.com/Wolfiora/InnoEngine/issues/34):复用当前窗口绘制 borrow、已测标签宽度及帧 mouse;每次真实绘制保留窗口、帧、线程及 Context 校验。同一修复候选仍需正式构建、回归与五组性能复验。 + +## 浮点边界与测试产品归属复查 + +- 第一轮 Graph 借用/测量修复后的正式七工程图编译成功,6 项 Graph/Settings Native 回归通过;e45/graph-settings.trx。单次诊断为 median 29.55 ms、P95 30.96 ms,尚不足以通过相对原版的性能门槛。 +- 随后的十二工程图编译成功,final-correction-suites.log/binlog;但颜色原生 oracle 在 NaN 上失败:原生结果 2147483648,普通托管转换结果 0。e45/color-oracle.trx。该操作在此停止,未运行后面的 Core/Scene 或统一 profiles,不能记为整体验收通过。 +- 当前修复使用标准库 Single.ConvertToIntegerNative,保留原有 Session/Context/线程准入、饱和与取整协议。原生 oracle 覆盖通道顺序、上下溢出、非有限值、全部半整数边界和 4,096 个确定性输入。既有 Core Color 的 byte 转换使用截断语义,不能直接替换原生 UI 的舍入协议;没有为此增加另一套通用数学系统。 +- 统一测试脚本不再全局设置 InnoTestNativeProduct;它覆盖了各工程的声明,也使带有传递 native 引用的纯契约套件反复准备完整 Editor。实际 Native UI 工程保留自己的产品声明,Build.Tests 根据此前真实脚本引用验收所需依赖显式声明。目标、工具和产品参数仍来自正式 SDK 求值;生产准备协议与校验没有跳过。 +- 原生准备日志中的 processes=0、bindingGenerations=0 证明没有实际 Native 重编译,却不证明输入验证便宜。重复读取约 4.5 GB 的既有构建成本仍保持独立问题,不以减少生产完整性检查来使本次 UI 验收变快。 +- SDK API 清单准备完成 241/241 工程求值;该次未提供当前逻辑脚本 references,且文档当时尚未同步,exit 1 是保留的准备失败,不是最终 Wiki 通过。另有环境 NU1900 审计网络告警;没有关闭审计或忽略告警。 +颜色数值 oracle 在 ConvertToIntegerNative 修复后全部通过。额外断言错误地把 Render 后仍存活的 Context 当成无效 owner;该纯值操作原有准入只验证 Session、Context 和线程。已纠正为 owner 释放后的拒绝断言,没有修改生产准入。DrawList 的窗口/帧校验仍由 RequireDrawingFrame 承担。 + +## 最新统一功能门禁 + +同一生产实现候选完成:Core 序列化 46、Scene 53、Workflows 302、Visual 136、Lifecycle 11 项通过。它们存在重叠,不能相加成独立总数。完整 Contracts、最终产品构建及受控性能仍单独报告,不能由这些结果推断全部通过。 + +真实 UI 扩展的进程隔离用例保留第一代 immutable menu model,连续十轮先编译失败再成功替换,核对 last-good、当前标签和 Action ID;在下一轮前通过现有严格 barrier 确认旧 collectible Action Type 不可达。普通套件还验证了十轮运行脚本成功/失败、十轮删除/恢复、十轮破损插件更新/恢复及十轮 Play/stop/reload。没有缩短 timeout、清除 pending monitor 或放宽回收检查。证据 e10/final-reload-cases.json 和 unified-correction-final 对应 TRX。 + +原生测试产品归属经真实 SDK 求值确认:12 个选定工程中 4 个明确声明产品,其余没有被脚本强制声明。native-test-ownership.json 保存实际结果。该改动消除不必要的准备请求,不用不同输入的一次总构建计时宣称性能收益。 diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs index 2f0ac8f1e..2cd28d719 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.cs @@ -99,7 +99,7 @@ internal void Draw() Groups(draw); Edges(draw); foreach (GraphNodeRecord node in Controller.document.nodes) - Node(draw, node); + Node(draw, node, mouse); Pointer(hovered && !miniMapHovered); if (draft.pointer.isBoxSelecting) { @@ -575,7 +575,8 @@ private void Pointer(bool hovered) private void Node( EditorDrawList draw, - GraphNodeRecord node + GraphNodeRecord node, + Vector2 mouse ) { var rect = Rect(node); if (rect.max.X < m_origin.X || rect.min.X > m_origin.X + m_size.X || rect.max.Y < m_origin.Y || rect.min.Y > m_origin.Y + m_size.Y) @@ -583,7 +584,7 @@ GraphNodeRecord node float zoom = Canvas.zoom; if (!draft.layout.TryGetNode(node.id, out GraphCanvasNode? presentation)) return; - _ = View.DrawNode(presentation, ImGuiApi.ToPackedColor(NodeHeaderColor(node)), Canvas.selectedNodes.Contains(node.id), + _ = View.DrawNode(draw, presentation, ImGuiApi.ToPackedColor(NodeHeaderColor(node)), Canvas.selectedNodes.Contains(node.id), previewPosition: draft.pointer.previewPositions.TryGetValue(node.id, out GraphPosition preview) ? preview : null); ShaderNodePort[] nodePorts = draft.ports[node.id]; if (UsesSplitPortLayout(nodePorts)) @@ -598,7 +599,7 @@ GraphNodeRecord node var endpoint = new GraphEndpoint(node.id, new GraphPortId(port.id)); if (!TryPortPosition(endpoint, port.direction, out Vector2 point)) continue; - if (Vector2.DistanceSquared(point, ImGuiApi.GetMousePos()) <= 64f) + if (Vector2.DistanceSquared(point, mouse) <= 64f) { bool missing = draft.missingPorts.Contains(endpoint); bool optional = port.direction == GraphPortDirection.Input && !port.required; @@ -781,7 +782,7 @@ void Curve( Vector2 b, bool selected = false ) { - View.DrawConnection(a, b, selected ? Color(0.87f, 0.72f, 1f) : Color(0.59f, 0.46f, 0.82f), selected ? 3f : 2f); + View.DrawConnection(draw, a, b, selected ? Color(0.87f, 0.72f, 1f) : Color(0.59f, 0.46f, 0.82f), selected ? 3f : 2f); } } diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs index ee280002b..60371d316 100644 --- a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.Nodes.cs @@ -143,6 +143,9 @@ Vector2 second /// /// Draws a visible neutral node and its ports, borrowing optional content only for this call. /// + /// + /// The current window's frame-local drawing borrow; it is neither retained nor released by this call. + /// /// /// The immutable geometric and textual node input. /// @@ -161,7 +164,14 @@ Vector2 second /// /// True when the node intersects the canvas and effective window clip and was submitted; false when culled. /// + /// + /// The node is null. + /// + /// + /// The canvas is uninitialized or a visible node uses an expired or mismatched drawing borrow. + /// public bool DrawNode( + EditorDrawList draw, GraphCanvasNode node, uint headerColor, bool selected, @@ -178,7 +188,6 @@ public bool DrawNode( if (maximum.X < clip.minimum.X || minimum.X > clip.maximum.X || maximum.Y < clip.minimum.Y || minimum.Y > clip.maximum.Y) return false; float zoom = this.zoom; - EditorDrawList draw = CanvasDrawing.GetDrawList(); draw.PushClip(Vector2.Max(m_origin, minimum - new Vector2(6f * zoom)), Vector2.Min(m_origin + m_size, maximum + new Vector2(6f * zoom)), true); try @@ -200,14 +209,14 @@ public bool DrawNode( Vector2 labelMinimum = minimum + labelBounds.minimum * zoom; Vector2 labelMaximum = minimum + labelBounds.maximum * zoom; float padding = 12f * zoom; - string label = FitLabel(port.label, labelMaximum.X - labelMinimum.X - padding * 2f, zoom); + var label = FitLabel(port.label, labelMaximum.X - labelMinimum.X - padding * 2f, zoom); float textX = port.direction == GraphPortDirection.Input ? labelMinimum.X + padding - : labelMaximum.X - padding - CanvasDrawing.MeasureText(label).X * zoom; + : labelMaximum.X - padding - label.width; draw.PushClip(labelMinimum, labelMaximum, true); try { draw.Text(new Vector2(textX, point.Y - 8f * zoom), - CanvasDrawing.ToPackedColor(port.isOptional ? EditorPalette.textDisabled : EditorPalette.text), label, zoom); + CanvasDrawing.ToPackedColor(port.isOptional ? EditorPalette.textDisabled : EditorPalette.text), label.text, zoom); } finally { @@ -223,16 +232,17 @@ public bool DrawNode( return true; } - private static string FitLabel( + private static (string text, float width) FitLabel( string label, float width, float zoom ) { - if (CanvasDrawing.MeasureText(label).X * zoom <= width) - return label; + float measuredWidth = CanvasDrawing.MeasureText(label).X * zoom; + if (measuredWidth <= width) + return (label, measuredWidth); const string ellipsis = "…"; if (CanvasDrawing.MeasureText(ellipsis).X * zoom > width) - return string.Empty; + return (string.Empty, 0f); int low = 0; int high = label.Length; while (low < high) @@ -246,7 +256,8 @@ float zoom // Avoid splitting a UTF-16 surrogate pair when a label contains supplementary characters. if (low > 0 && low < label.Length && char.IsHighSurrogate(label[low - 1])) low--; - return label[..low] + ellipsis; + string text = label[..low] + ellipsis; + return (text, CanvasDrawing.MeasureText(text).X * zoom); } private static bool Contains( diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs index aeb330536..f2dac06b1 100644 --- a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Canvas/GraphCanvasView.cs @@ -2,6 +2,7 @@ using System.Numerics; using Inno.Core.Graphs; using Inno.Editor.Graph; +using Inno.Editor.ImGui; using CanvasDrawing = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Graph.ImGui; @@ -102,6 +103,9 @@ public void DrawGrid( /// /// Draws a horizontal-tangent connection using the same curve used for hit testing. /// + /// + /// The current window's frame-local drawing borrow; it is neither retained nor released by this call. + /// /// /// The output endpoint in screen space. /// @@ -117,7 +121,11 @@ public void DrawGrid( /// /// An endpoint or derived control point is nonfinite, or the stroke width is not positive and finite. /// + /// + /// The canvas is uninitialized or a visible connection uses an expired or mismatched drawing borrow. + /// public void DrawConnection( + EditorDrawList draw, Vector2 output, Vector2 input, uint color, @@ -141,7 +149,6 @@ public void DrawConnection( if (maximum.X < MathF.Max(m_origin.X, clip.minimum.X) || minimum.X > MathF.Min(m_origin.X + m_size.X, clip.maximum.X) || maximum.Y < MathF.Max(m_origin.Y, clip.minimum.Y) || minimum.Y > MathF.Min(m_origin.Y + m_size.Y, clip.maximum.Y)) return; - var draw = CanvasDrawing.GetDrawList(); draw.PushClip(m_origin, m_origin + m_size, true); try { diff --git a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj index 5b4ed9def..af7c814c8 100644 --- a/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj +++ b/src/composition/editor/presentation/Inno.Editor.Graph.ImGui/Inno.Editor.Graph.ImGui.csproj @@ -11,10 +11,10 @@ - + diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs index ab913acd0..688b71635 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/ImGui.cs @@ -1130,7 +1130,9 @@ public static Vector2 GetMouseDragDelta( public static uint ToPackedColor(Vector4 color) { EditorUiSession.current.EnsureDrawing(); - return RawImGui.ColorConvertFloat4ToU32(color); + return Channel(color.X) | Channel(color.Y) << 8 | Channel(color.Z) << 16 | Channel(color.W) << 24; + + static uint Channel(float value) => unchecked((uint)float.ConvertToIntegerNative(Math.Clamp(value, 0f, 1f) * 255f + 0.5f)); } /// diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs index 762ee89ca..95581416c 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Runtime/EditorUiInput.cs @@ -12,7 +12,7 @@ public readonly struct EditorUiInput /// public bool alt { get; } /// - /// Gets whether Control is held. + /// Gets whether the physical Control modifier is held, independently of native shortcut conventions. /// public bool control { get; } /// @@ -20,7 +20,7 @@ public readonly struct EditorUiInput /// public bool shift { get; } /// - /// Gets whether the platform super key is held. + /// Gets whether the physical Super modifier is held, independently of native shortcut conventions. /// public bool super { get; } /// @@ -47,9 +47,10 @@ public readonly struct EditorUiInput internal unsafe EditorUiInput(ImGuiIO* io) { alt = io->KeyAlt != 0; - control = io->KeyCtrl != 0; + bool swapsModifiers = io->ConfigMacOsxBehaviors != 0; + control = (swapsModifiers ? io->KeySuper : io->KeyCtrl) != 0; shift = io->KeyShift != 0; - super = io->KeySuper != 0; + super = (swapsModifiers ? io->KeyCtrl : io->KeySuper) != 0; wantsText = io->WantTextInput != 0; mouseDelta = ImGui.FromNative(io->MouseDelta); framebufferScale = ImGui.FromNative(io->DisplayFramebufferScale); diff --git a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs index 5091dbb35..2d1d62f43 100644 --- a/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs +++ b/src/composition/editor/presentation/Inno.Editor.ImGui/Widgets/ImGuiWidget.Selection.cs @@ -147,14 +147,15 @@ private static bool HasPrimaryModifier( EditorKeyboardPolicy keyboard, ImGuiIO* io ) { + var input = new EditorUiInput(io); KeyModifier modifiers = KeyModifier.None; - if (io->KeyCtrl != 0) + if (input.control) modifiers |= KeyModifier.Control; - if (io->KeySuper != 0) + if (input.super) modifiers |= KeyModifier.Super; - if (io->KeyAlt != 0) + if (input.alt) modifiers |= KeyModifier.Alt; - if (io->KeyShift != 0) + if (input.shift) modifiers |= KeyModifier.Shift; return (modifiers & keyboard.primaryModifier) == keyboard.primaryModifier; } diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs index 75365ad94..1613f5d32 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; namespace Inno.Core.Serialization; @@ -13,7 +12,7 @@ internal static class BinarySerializationFormat internal static byte[] Encode(SerializationNode root) { - using var stream = new MemoryStream(16 * 1024); + using var stream = new MemoryStream(); using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); writer.Write(C_MAGIC); WriteNode(writer, root); @@ -65,7 +64,8 @@ SerializationNode node return; case ObjectSerializationNode objectNode: writer.Write((byte)NodeKind.Object); - string[] names = [.. objectNode.values.Keys.OrderBy(static name => name, StringComparer.Ordinal)]; + string[] names = [.. objectNode.values.Keys]; + Array.Sort(names, StringComparer.Ordinal); writer.Write(names.Length); for (int i = 0; i < names.Length; i++) { diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs index 727716322..ca0dfb65f 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Inno.Core.Serialization; @@ -13,11 +12,17 @@ internal static SerializationPropertySnapshot CaptureProperty( ConverterRegistryLease converters ) { ArgumentException.ThrowIfNullOrWhiteSpace(propertyName); - return Capture(value, context, converters).FirstOrDefault(snapshot => - string.Equals(snapshot.name, propertyName, StringComparison.Ordinal)) - ?? throw new ArgumentException( - $"Serializable property '{propertyName}' was not found on '{value.GetType().FullName}'.", - nameof(propertyName)); + IReadOnlyList members = context.GetRequired().GetMetadata(value.GetType()).members; + for (int index = 0; index < members.Count; index++) + { + SerializationMemberMetadata member = members[index]; + if ((member.visibility & PropertyVisibility.Serialize) != 0 && + string.Equals(member.name, propertyName, StringComparison.Ordinal)) + return CaptureMember(value, member, context, converters); + } + throw new ArgumentException( + $"Serializable property '{propertyName}' was not found on '{value.GetType().FullName}'.", + nameof(propertyName)); } internal static IReadOnlyList Capture( @@ -33,24 +38,7 @@ ConverterRegistryLease converters if ((member.visibility & PropertyVisibility.Serialize) == 0) continue; - var operation = new SerializationOperation(context, converters); - try - { - SerializationNode node = ValuePipeline.Write( - member.GetValue(value), - member.type, - operation, - AppendPath(member.name), - allowDefaultObject: false); - snapshots.Add(new SerializationPropertySnapshot( - member.name, - member.type, - BinarySerializationFormat.Encode(node))); - } - finally - { - operation.Fail(); - } + snapshots.Add(CaptureMember(value, member, context, converters)); } return snapshots; } @@ -140,6 +128,32 @@ private static void ValidateSnapshots(IReadOnlyList exception is not OutOfMemoryException and not StackOverflowException and diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs index 75c9a1b97..5d1b0a9e0 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs @@ -1,16 +1,15 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace Inno.Core.Serialization; internal sealed class SerializationOperation { private readonly ConverterRegistryLease m_converters; - private readonly List m_completionCallbacks = []; - private readonly HashSet m_scheduledObjects = new(ReferenceComparer.instance); - private readonly List m_scheduledObjectOrder = []; - private readonly Dictionary m_capturePaths = new(ReferenceComparer.instance); + private List? m_completionCallbacks; + private HashSet? m_scheduledObjects; + private List? m_scheduledObjectOrder; + private Dictionary? m_capturePaths; private bool m_isActive = true; internal SerializationOperation( @@ -43,19 +42,19 @@ internal void AddCompletionCallback(Action callback) { ArgumentNullException.ThrowIfNull(callback); EnsureActive(); - m_completionCallbacks.Add(callback); + (m_completionCallbacks ??= []).Add(callback); } internal void ScheduleRestoredObject(ISerializable value) { EnsureActive(); - if (!m_scheduledObjects.Add(value)) + if (!(m_scheduledObjects ??= new(ReferenceEqualityComparer.Instance)).Add(value)) return; - m_scheduledObjectOrder.Add(value); + (m_scheduledObjectOrder ??= []).Add(value); Action? callback = GetMetadata(value.GetType()).restored; if (callback is not null) - m_completionCallbacks.Add(() => callback(value, context)); + (m_completionCallbacks ??= []).Add(() => callback(value, context)); } internal void EnterCapture( @@ -63,49 +62,49 @@ internal void EnterCapture( string path ) { EnsureActive(); - if (m_capturePaths.TryGetValue(value, out string? existingPath)) + Dictionary captures = m_capturePaths ??= new(ReferenceEqualityComparer.Instance); + if (captures.TryGetValue(value, out string? existingPath)) { throw new InvalidOperationException( $"Serialization cycle detected at '{path}'. The same object is already being written at '{existingPath}'."); } - m_capturePaths.Add(value, path); + captures.Add(value, path); } internal Checkpoint CreateCheckpoint() { EnsureActive(); - return new Checkpoint(m_completionCallbacks.Count, m_scheduledObjectOrder.Count); + return new Checkpoint(m_completionCallbacks?.Count ?? 0, m_scheduledObjectOrder?.Count ?? 0); } internal void Rollback(Checkpoint checkpoint) { EnsureActive(); if (checkpoint.callbackCount < 0 || - checkpoint.callbackCount > m_completionCallbacks.Count || + checkpoint.callbackCount > (m_completionCallbacks?.Count ?? 0) || checkpoint.scheduledObjectCount < 0 || - checkpoint.scheduledObjectCount > m_scheduledObjectOrder.Count) + checkpoint.scheduledObjectCount > (m_scheduledObjectOrder?.Count ?? 0)) { throw new InvalidOperationException("The serialization operation checkpoint is invalid."); } - if (m_completionCallbacks.Count > checkpoint.callbackCount) + if (m_completionCallbacks is { } callbacks && callbacks.Count > checkpoint.callbackCount) + callbacks.RemoveRange(checkpoint.callbackCount, callbacks.Count - checkpoint.callbackCount); + if (m_scheduledObjectOrder is { } order) { - m_completionCallbacks.RemoveRange( - checkpoint.callbackCount, - m_completionCallbacks.Count - checkpoint.callbackCount); - } - for (int i = m_scheduledObjectOrder.Count - 1; i >= checkpoint.scheduledObjectCount; i--) - { - m_scheduledObjects.Remove(m_scheduledObjectOrder[i]); - m_scheduledObjectOrder.RemoveAt(i); + for (int i = order.Count - 1; i >= checkpoint.scheduledObjectCount; i--) + { + m_scheduledObjects!.Remove(order[i]); + order.RemoveAt(i); + } } } internal void ExitCapture(object value) { if (m_isActive) - m_capturePaths.Remove(value); + m_capturePaths?.Remove(value); } internal void Complete() @@ -113,16 +112,17 @@ internal void Complete() EnsureActive(); try { - for (int i = 0; i < m_completionCallbacks.Count; i++) - m_completionCallbacks[i](); + if (m_completionCallbacks is { } callbacks) + for (int i = 0; i < callbacks.Count; i++) + callbacks[i](); } finally { m_isActive = false; - m_completionCallbacks.Clear(); - m_scheduledObjects.Clear(); - m_scheduledObjectOrder.Clear(); - m_capturePaths.Clear(); + m_completionCallbacks?.Clear(); + m_scheduledObjects?.Clear(); + m_scheduledObjectOrder?.Clear(); + m_capturePaths?.Clear(); } } @@ -132,10 +132,10 @@ internal void Fail() return; m_isActive = false; - m_completionCallbacks.Clear(); - m_scheduledObjects.Clear(); - m_scheduledObjectOrder.Clear(); - m_capturePaths.Clear(); + m_completionCallbacks?.Clear(); + m_scheduledObjects?.Clear(); + m_scheduledObjectOrder?.Clear(); + m_capturePaths?.Clear(); } internal readonly record struct Checkpoint( @@ -143,36 +143,4 @@ internal readonly record struct Checkpoint( int scheduledObjectCount ); - private sealed class ReferenceComparer : IEqualityComparer - { - internal static ReferenceComparer instance { get; } = new(); - - /// - /// Determines whether this value and the supplied value represent the same logical state. - /// - /// - /// The horizontal or first component. - /// - /// - /// The vertical or second component. - /// - /// - /// when the requested condition is satisfied; otherwise, . - /// - public new bool Equals( - object? x, - object? y - ) => ReferenceEquals(x, y); - - /// - /// Computes a hash code consistent with the implemented equality contract. - /// - /// - /// The object compared with this value. - /// - /// - /// The scalar result calculated from the supplied inputs. - /// - public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); - } } diff --git a/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs b/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs index 31dea123c..8aba706ff 100644 --- a/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs +++ b/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs @@ -154,7 +154,7 @@ public IReadOnlyList CaptureProperties( } /// - /// Captures one persistent property as independently restorable neutral bytes. + /// Captures one persistent property as independently restorable neutral bytes without reading unrelated members. /// /// /// The serializable object containing the requested property. @@ -609,5 +609,12 @@ private void EnsureInitialized() } private SerializationContext CreateContext(SerializationContext? context) - => (context ?? SerializationContext.empty).With(m_types).With(this).With(m_metadata); + { + context ??= SerializationContext.empty; + if (context.TryGet(out TypeCatalog? types) && ReferenceEquals(types, m_types) + && context.TryGet(out SerializationRegistry? registry) && ReferenceEquals(registry, this) + && context.TryGet(out ISerializationMetadataSource? metadata) && ReferenceEquals(metadata, m_metadata)) + return context; + return context.With(m_types).With(this).With(m_metadata); + } } diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 index 606462436..6e54f32d7 100644 --- a/tests/Invoke-EditorUiAcceptance.ps1 +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -55,8 +55,7 @@ if (-not $product.InnoNativeTarget -or -not $product.InnoToolTarget -or } $buildArguments = @("-p:ArtifactsPath=$artifacts", "-p:InnoNativeTarget=$($product.InnoNativeTarget)", "-p:InnoToolTarget=$($product.InnoToolTarget)", - "-p:InnoProductBuildProperties=$($product.InnoProductBuildProperties)", - "-p:InnoTestNativeTarget=$($product.InnoNativeTarget)", '-p:InnoTestNativeProduct=editor') + "-p:InnoProductBuildProperties=$($product.InnoProductBuildProperties)") $profiles = if ($AcceptanceProfile -eq 'All') { @('Contracts', 'Workflows', 'Visual', 'Lifecycle', 'Performance', 'Products') } else { @($AcceptanceProfile) } foreach ($selected in $profiles) { diff --git a/tests/Invoke-IsolatedTests.ps1 b/tests/Invoke-IsolatedTests.ps1 index b9edee234..962ac8f27 100644 --- a/tests/Invoke-IsolatedTests.ps1 +++ b/tests/Invoke-IsolatedTests.ps1 @@ -77,7 +77,7 @@ if ($Target) { $ToolTarget = $selectedTool[0].Trim() } $productArguments += @("-p:InnoNativeTarget=$Target", "-p:InnoToolTarget=$ToolTarget", - "-p:InnoProductBuildProperties=$ProductProperties", "-p:InnoTestNativeTarget=$Target", '-p:InnoTestNativeProduct=editor') + "-p:InnoProductBuildProperties=$ProductProperties") } # Process-global Native state and deliberately retained Faulted owners require separate hosts. diff --git a/tests/build/Inno.Build.Tests/Inno.Build.Tests.csproj b/tests/build/Inno.Build.Tests/Inno.Build.Tests.csproj index ffcc2547a..6b5c4b6e5 100644 --- a/tests/build/Inno.Build.Tests/Inno.Build.Tests.csproj +++ b/tests/build/Inno.Build.Tests/Inno.Build.Tests.csproj @@ -7,6 +7,8 @@ false true true + editor + $(InnoNativeTarget) diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs new file mode 100644 index 000000000..bbed2954a --- /dev/null +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs @@ -0,0 +1,162 @@ +using System; +using Inno.Adapter.Serialization.DotNet; +using Inno.Extensibility.Types; +using Xunit; + +namespace Inno.Core.Serialization.Tests; + +public sealed partial class SerializationBehaviorTests +{ + [Fact] + public void PreparedSerializationContextsAvoidRepeatedOwnerCopies() + { + SerializationContext? prepared = null; + _ = m_serialization.Encode(writer => prepared = writer.context); + var source = new DefaultSample { count = 73 }; + byte[] expected = m_serialization.CapturePropertyData(source, nameof(DefaultSample.count)); + long fresh = Measure(null); + long inherited = Measure(prepared); + + Assert.True(inherited + 256 <= fresh, $"Prepared context allocated {inherited} bytes; fresh context allocated {fresh} bytes."); + + long Measure(SerializationContext? context) + { + for (int index = 0; index < 16; index++) + _ = m_serialization.CapturePropertyData(source, nameof(DefaultSample.count), context); + byte[]? actual = null; + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int index = 0; index < 128; index++) + actual = m_serialization.CapturePropertyData(source, nameof(DefaultSample.count), context); + long allocated = (GC.GetAllocatedBytesForCurrentThread() - before) / 128; + Assert.Equal(expected, actual); + return allocated; + } + } + + [Fact] + public void SerializationRebindsForeignOwnersAndPreservesCallerContext() + { + var metadata = new ReflectionSerializationMetadataSource(); + using var foreign = new SerializationRegistry(m_types, metadata); + var service = new SampleContext(); + SerializationContext? prepared = null; + _ = foreign.Encode(writer => prepared = writer.context, SerializationContext.empty.With(service)); + ISerializationMetadataSource? currentMetadata = null; + _ = m_serialization.Encode(writer => currentMetadata = writer.context.GetRequired()); + + _ = m_serialization.Encode(writer => + { + Assert.Same(m_serialization, writer.context.GetRequired()); + Assert.Same(m_types, writer.context.GetRequired()); + Assert.Same(currentMetadata, writer.context.GetRequired()); + Assert.Same(service, writer.context.GetRequired()); + }, prepared); + + Assert.Same(foreign, prepared!.GetRequired()); + Assert.Same(metadata, prepared.GetRequired()); + } + + [Fact] + public void BinaryObjectKeysRemainOrdinalAndIndependentOfInsertionOrder() + { + string[] keys = ["z", "A", "a", "é", "中文", "😀"]; + byte[] forward = m_serialization.Encode(writer => + { + foreach (string key in keys) + writer.Write(key, key); + }); + byte[] reverse = m_serialization.Encode(writer => + { + for (int index = keys.Length - 1; index >= 0; index--) + writer.Write(keys[index], keys[index]); + }); + + Assert.Equal(forward, reverse); + } + + [Fact] + public void SmallBinaryDocumentsStayWithinABoundedAllocationBudget() + { + byte[] expected = m_serialization.Encode(static writer => writer.Write("value", 73)); + for (int index = 0; index < 16; index++) + _ = m_serialization.Encode(static writer => writer.Write("value", 73)); + + const int C_ITERATIONS = 128; + byte[]? actual = null; + long before = GC.GetAllocatedBytesForCurrentThread(); + for (int index = 0; index < C_ITERATIONS; index++) + actual = m_serialization.Encode(static writer => writer.Write("value", 73)); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + + Assert.Equal(expected, actual); + Assert.InRange(allocated / C_ITERATIONS, 0, 4096); + } + + [Fact] + public void PropertyCaptureDoesNotReadUnrelatedAnnotatedGetters() + { + var source = new SelectiveCaptureSample { count = 73 }; + byte[] data = m_serialization.CapturePropertyData(source, nameof(SelectiveCaptureSample.count)); + var target = new DefaultSample { count = 2, name = "Preserved" }; + + SerializationPropertyRestoreResult result = m_serialization.RestorePropertiesData(target, data); + + Assert.True(result.success); + Assert.Equal(1, result.restoredCount); + Assert.Equal(73, target.count); + Assert.Equal("Preserved", target.name); + Assert.Equal(0, source.unrelatedReadCount); + } + + [Theory] + [InlineData("missing")] + [InlineData(nameof(SelectiveCaptureSample.ignored))] + public void PropertyCaptureRejectsMissingMembersWithoutReadingUnrelatedGetters(string name) + { + var source = new SelectiveCaptureSample(); + + Assert.Throws(() => m_serialization.CapturePropertyData(source, name)); + + Assert.Equal(0, source.unrelatedReadCount); + } + + [Theory] + [InlineData(0)] + [InlineData(63)] + [InlineData(257)] + [InlineData(10_240)] + [InlineData(200_000)] + public void BinaryDocumentsRoundTripAcrossBufferGrowthBoundaries(int length) + { + var payload = new byte[length]; + for (int index = 0; index < payload.Length; index++) + payload[index] = unchecked((byte)index); + + byte[] data = m_serialization.Serialize(new DefaultSample { bytes = payload }); + DefaultSample restored = m_serialization.Deserialize(data); + + Assert.Equal(payload, restored.bytes); + } + + private sealed class SelectiveCaptureSample : ISerializable + { + private int m_unrelatedReads; + + [SerializableProperty] + public int count { get; set; } + + [SerializableProperty] + public int unrelated + { + get + { + m_unrelatedReads++; + throw new InvalidOperationException("An unrelated getter must not be evaluated."); + } + set { } + } + + public int ignored { get; set; } + public int unrelatedReadCount => m_unrelatedReads; + } +} diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.cs index b775400f5..c1727e205 100644 --- a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.cs +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.cs @@ -14,7 +14,7 @@ namespace Inno.Core.Serialization.Tests; -public sealed class SerializationBehaviorTests : IDisposable +public sealed partial class SerializationBehaviorTests : IDisposable { private readonly string m_testRoot = Path.Combine( Path.GetTempPath(), diff --git a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs index 0cd008a8f..4a087a20f 100644 --- a/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs +++ b/tests/editor/Inno.Editor.ImGui.Tests/WidgetLayoutTests.cs @@ -16,6 +16,45 @@ namespace Inno.Editor.ImGui.Tests; public sealed unsafe class WidgetLayoutTests { + [Fact] + public void PackedColorConversionMatchesNativeSaturationRoundingAndChannelOrder() + { + using (NativeContext context = new(1f)) + { + context.Frame(() => + { + BeginWindow("Color format contract", new(400f, 300f)); + try + { + float[] boundaries = [float.NegativeInfinity, -1f, -0f, 0f, 0.5f, 1f, 2f, float.PositiveInfinity, float.NaN]; + foreach (float value in boundaries) + { + Verify(new(value, 0f, 0f, 0f)); + Verify(new(0f, value, 0f, 0f)); + Verify(new(0f, 0f, value, 0f)); + Verify(new(0f, 0f, 0f, value)); + } + for (int channel = 0; channel < 255; channel++) + { + float half = (channel + 0.5f) / 255f; + Verify(new(MathF.BitDecrement(half), half, MathF.BitIncrement(half), 1f)); + } + var random = new Random(0x2DCA); + for (int index = 0; index < 4096; index++) + Verify(new(random.NextSingle() * 3f - 1f, random.NextSingle() * 3f - 1f, + random.NextSingle() * 3f - 1f, random.NextSingle() * 3f - 1f)); + } + finally + { + NativeImGui.End(); + } + }); + } + + Assert.Throws(() => EditorImGui.ToPackedColor(Vector4.One)); + static void Verify(Vector4 value) => Assert.Equal(NativeImGui.ColorConvertFloat4ToU32(value), EditorImGui.ToPackedColor(value)); + } + [Fact] public void StandaloneMetricsApplyImmediatelyAndRetainedMetricsDoNotRootTheirRetiredSession() { diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs index ebf1982f1..e1f4b6233 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/GraphCanvasNativeTests.cs @@ -84,8 +84,9 @@ bool down controller.MoveNodes(positions); if (result.output is GraphEndpoint output && result.input is GraphEndpoint input) _ = controller.Connect(output, input); + var draw = Ui.GetDrawList(); foreach (GraphCanvasNode node in layout.nodes) - _ = view.DrawNode(node, 0xFF504030, state.selectedNodes.Contains(node.id), + _ = view.DrawNode(draw, node, 0xFF504030, state.selectedNodes.Contains(node.id), previewPosition: pointer.previewPositions.TryGetValue(node.id, out GraphPosition position) ? position : null); } finally @@ -116,17 +117,18 @@ public void NonShaderNodeDrawingRestoresItsClipAfterAnExtensionThrows() { ImGuiWindow* window = NativeGui.Raw.GetCurrentWindow(); int clipDepth = window->DrawList->ClipRectStack.Size; + var draw = Ui.GetDrawList(); var view = new GraphCanvasView(state, new(40f, 60f), new(540f, 300f)); var failure = new InvalidOperationException("Workflow node extension failed."); - Assert.Same(failure, Assert.Throws(() => view.DrawNode(node, 0xFF504030, true, + Assert.Same(failure, Assert.Throws(() => view.DrawNode(draw, node, 0xFF504030, true, () => throw failure))); Assert.Equal(clipDepth, window->DrawList->ClipRectStack.Size); - Assert.True(view.DrawNode(node, 0xFF504030, false)); + Assert.True(view.DrawNode(draw, node, 0xFF504030, false)); var offscreen = new GraphCanvasNode(new("workflow.hidden"), new(5000f, 5000f), new(200f, 100f), 24f, "Hidden", []); - Assert.False(view.DrawNode(offscreen, 0xFF504030, false, () => throw new InvalidOperationException("Culled content must not run."))); + Assert.False(view.DrawNode(draw, offscreen, 0xFF504030, false, () => throw new InvalidOperationException("Culled content must not run."))); var outsideWindow = new GraphCanvasNode(new("workflow.clipped-parent"), new(900f, 900f), new(200f, 100f), 24f, "Clipped by parent", []); var oversized = new GraphCanvasView(state, new(40f, 60f), new(1800f, 1800f)); - Assert.False(oversized.DrawNode(outsideWindow, 0xFF504030, false, () => throw new InvalidOperationException("Parent-clipped content must not run."))); + Assert.False(oversized.DrawNode(draw, outsideWindow, 0xFF504030, false, () => throw new InvalidOperationException("Parent-clipped content must not run."))); Assert.Equal(clipDepth, window->DrawList->ClipRectStack.Size); } finally diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs index 4fda4f1c8..3a8a60346 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs @@ -39,6 +39,12 @@ internal NativePanelHost(params string[] assemblies) : this(false, assemblies) { internal NativePanelHost( bool supportsVertexOffsets, params string[] assemblies + ) : this(supportsVertexOffsets, new EditorKeyboardPolicy(KeyModifier.Control, "Super"), assemblies) { } + + internal NativePanelHost( + bool supportsVertexOffsets, + EditorKeyboardPolicy keyboard, + params string[] assemblies ) { m_validateVertexOffsets = supportsVertexOffsets; m_ownsServices = true; @@ -52,7 +58,7 @@ params string[] assemblies m_types = new TypeCatalog(m_modules, new ReflectionTypeCatalogSource()); console = new EditorConsole(logs, diagnostics, new InactivePlayMode()); console.Start(); - runtime = new EditorInteractionRuntime(new EditorContext(m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Super")), m_types, logs, [console]); + runtime = new EditorInteractionRuntime(new EditorContext(m_root, keyboard), m_types, logs, [console]); runtime.Start(); ui = new EditorUiSession(); m_nativeContext = CreateNativeContext(ui, supportsVertexOffsets); @@ -178,6 +184,8 @@ bool supportsVertexOffsets if (supportsVertexOffsets) io->BackendFlags |= (int)ImGuiBackendFlags.RendererHasVtxOffset; io->ConfigFlags |= (int)ImGuiConfigFlags.NavEnableKeyboard; + // Fixture input uses an explicit Control convention on every runner. + io->ConfigMacOsxBehaviors = 0; io->Fonts->RendererHasTextures = 1; io->IniFilename = null; using (ui.Enter()) diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs index 3d6ee9222..9e94ba578 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/TreeKeyboardOwnershipNativeTests.cs @@ -1,5 +1,7 @@ using System; using System.Numerics; +using Inno.Core.Input; +using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Native.ImGui; using Xunit; @@ -10,10 +12,19 @@ namespace Inno.Editor.PlayMode.Tests; public sealed unsafe class TreeKeyboardOwnershipNativeTests { - [Fact] - public void ListActivationDoesNotToggleDisclosureOrStealAnOverlayButton() - { - using var host = new NativePanelHost(); + [Theory] + [InlineData(false, KeyModifier.Control)] + [InlineData(true, KeyModifier.Control)] + [InlineData(false, KeyModifier.Super)] + [InlineData(true, KeyModifier.Super)] + public void ListActivationDoesNotToggleDisclosureOrStealAnOverlayButton( + bool commandKeyBehavior, + KeyModifier primaryModifier + ) { + using var host = new NativePanelHost(false, new EditorKeyboardPolicy(primaryModifier, "Super")); + ImGuiIO* io = NativeImGui.Raw.GetIO(); + io->ConfigMacOsxBehaviors = commandKeyBehavior ? (byte)1 : (byte)0; + ImGuiKey primaryKey = primaryModifier == KeyModifier.Super ? ImGuiKey.ImGuiModSuper : ImGuiKey.ImGuiModCtrl; bool rowFocus = true; bool overlayFocus = false; bool open = false; @@ -66,15 +77,21 @@ public void ListActivationDoesNotToggleDisclosureOrStealAnOverlayButton() host.Frame(draw); host.Frame(draw); rowFocus = false; - ImGuiIO* io = NativeImGui.Raw.GetIO(); - NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 1); + NativeImGui.Raw.AddKeyEvent(io, primaryKey, 1); NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 1); host.Frame(draw); Assert.True(open); Assert.Equal(1, toggles); Assert.Equal(0, overlayPresses); + host.Frame(() => + { + var input = Ui.GetInput(); + Assert.Equal(primaryModifier == KeyModifier.Control, input.control); + Assert.Equal(primaryModifier == KeyModifier.Super, input.super); + draw(); + }); NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.Space, 0); - NativeImGui.Raw.AddKeyEvent(io, ImGuiKey.ImGuiModCtrl, 0); + NativeImGui.Raw.AddKeyEvent(io, primaryKey, 0); host.Frame(draw); overlayFocus = true; host.Frame(draw); diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs new file mode 100644 index 000000000..a259e0902 --- /dev/null +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs @@ -0,0 +1,79 @@ +using System; +using Inno.Editor.Core; +using Inno.Editor.Interactions; +using Inno.Scripting.Compiler; +using Inno.Scripting.Reload; +using Xunit; + +namespace Inno.Editor.Scripting.Tests; + +public sealed partial class ScriptingPipelineTests +{ + [Fact] + [Trait("Acceptance", "Lifecycle")] + [Trait("ProcessIsolation", "Required")] + public void TenActionMenuGenerationsRetireTypesAndPreserveLastGoodAfterFailure() + { + m_fixture.WriteVersionedBehavior(1); + WriteAction(1); + var reloads = new EditorReloadCoordinator(); + using ScriptReloadHost reload = m_fixture.CreateReloadHost(reloads); + reload.Start(); + ScriptCompilationResult first = m_fixture.CompilePending(reload); + Assert.True(first.success, FormatDiagnostics(first)); + Assert.True(reload.ApplyPendingReload()); + using EditorInteractionRuntime runtime = m_fixture.CreateEditorRuntime(reloads); + runtime.Start(); + EditorMenuModel retainedMenu = runtime.interactions.For("tests/menu-lifecycle").BuildMenu(); + Assert.Equal("Generation 1", Assert.Single(retainedMenu.items).label); + WeakReference previous = CaptureActiveType(m_fixture, "ReloadedAction", 1); + + for (int version = 2; version <= 11; version++) + { + m_fixture.Write("Editor/LifecycleAction.editor.cs", "public sealed class ReloadedAction {"); + ScriptCompilationResult failed = m_fixture.CompilePending(reload); + Assert.False(failed.success); + Assert.False(reload.ApplyPendingReload()); + AssertMenu(runtime, version - 1); + + WriteAction(version); + ScriptCompilationResult replacement = m_fixture.CompilePending(reload); + Assert.True(replacement.success, FormatDiagnostics(replacement)); + Assert.True(reload.ApplyPendingReload()); + CompleteUnloadVerification(reload); + Assert.False(previous.IsAlive, "The retired Action type survived the public generation barrier."); + AssertMenu(runtime, version); + previous = CaptureActiveType(m_fixture, "ReloadedAction", version); + } + + Assert.Equal("Generation 1", Assert.Single(retainedMenu.items).label); + GC.KeepAlive(retainedMenu); + + void WriteAction(int version) + { + m_fixture.Write("Editor/LifecycleAction.editor.cs", $$""" + using InnoEditor.Interactions; + + [EditorAction("tests.lifecycle.action", "tests/menu-lifecycle")] + [EditorMenu("tests/menu-lifecycle", "Generation")] + public sealed class ReloadedAction : EditorAction + { + public int version => {{version}}; + protected override EditorActionState Query(EditorActionContext context) + => new(true, true, false, "Generation {{version}}", null); + protected override void Execute(EditorActionContext context) { } + } + """); + } + } + + private static void AssertMenu( + EditorInteractionRuntime runtime, + int version + ) { + EditorMenuItem item = Assert.Single(runtime.interactions.For("tests/menu-lifecycle").BuildMenu().items); + Assert.Equal("Generation " + version, item.label); + Assert.Equal("tests.lifecycle.action", item.actionId); + Assert.True(item.status.isEnabled); + } +} diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs index 7f0b72183..c1abee2b2 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.cs @@ -39,7 +39,7 @@ using Xunit; namespace Inno.Editor.Scripting.Tests; -public sealed class ScriptingPipelineTests : IDisposable +public sealed partial class ScriptingPipelineTests : IDisposable { private readonly ScriptingFixture m_fixture = new(); public void Dispose() => m_fixture.Dispose(); @@ -1254,6 +1254,7 @@ public void SourceArtifactsAreCatalogedButRuntimeExportContainsNoSourceFiles() } [Fact] + [Trait("Acceptance", "Lifecycle")] public void SuccessfulReloadActivatesCandidateAndFailedCompilationRetainsIt() { m_fixture.WriteVersionedBehavior(1); @@ -1262,20 +1263,23 @@ public void SuccessfulReloadActivatesCandidateAndFailedCompilationRetainsIt() ScriptCompilationResult firstResult = m_fixture.CompilePending(reload); Assert.True(firstResult.success, FormatDiagnostics(firstResult)); Assert.True(reload.ApplyPendingReload()); - WeakReference first = CaptureActiveType(m_fixture, "VersionedBehavior", expectedVersion: 1); - m_fixture.WriteVersionedBehavior(2); - ScriptCompilationResult secondResult = m_fixture.CompilePending(reload); - Assert.True(secondResult.success, FormatDiagnostics(secondResult)); - Assert.True(reload.ApplyPendingReload()); - CompleteUnloadVerification(reload); - Assert.False(first.IsAlive, "The first script generation remained reachable after its reload barrier."); - Type second = m_fixture.ResolveActiveType("VersionedBehavior"); - Assert.Equal(2, ReadVersion(second)); - m_fixture.Write("VersionedBehavior.cs", "using InnoEngine.Scene; public sealed class VersionedBehavior : GameBehavior {"); - ScriptCompilationResult failed = m_fixture.CompilePending(reload); - Assert.False(failed.success); - Assert.False(reload.ApplyPendingReload()); - Assert.Same(second, m_fixture.ResolveActiveType("VersionedBehavior")); + WeakReference active = CaptureActiveType(m_fixture, "VersionedBehavior", expectedVersion: 1); + for (int version = 2; version <= 11; version++) + { + m_fixture.Write("VersionedBehavior.cs", "using InnoEngine.Scene; public sealed class VersionedBehavior : GameBehavior {"); + ScriptCompilationResult failed = m_fixture.CompilePending(reload); + Assert.False(failed.success); + Assert.False(reload.ApplyPendingReload()); + AssertRetainedActiveType(m_fixture, active, version - 1); + + m_fixture.WriteVersionedBehavior(version); + ScriptCompilationResult replacement = m_fixture.CompilePending(reload); + Assert.True(replacement.success, FormatDiagnostics(replacement)); + Assert.True(reload.ApplyPendingReload()); + CompleteUnloadVerification(reload); + Assert.False(active.IsAlive, "The previous script generation remained reachable after its reload barrier."); + active = CaptureActiveType(m_fixture, "VersionedBehavior", version); + } } [Fact] @@ -1362,6 +1366,7 @@ public sealed class ReloadableBehavior : GameBehavior [Theory] [InlineData(false)] [InlineData(true)] + [Trait("Acceptance", "Lifecycle")] public void RemovedOrBrokenUpdatedPluginCommitsUnavailableGenerationAndRecovers(bool updateInPlace) { using var fixture = new ScriptingFixture(WriteUnavailableGenerationPlugin); @@ -1395,38 +1400,43 @@ public sealed class DependentBehavior : GameBehavior string installedPlugin = Path.Combine(fixture.projectRoot, "Plugins", "unavailability.iplugin"); string detachedPlugin = Path.Combine(fixture.projectRoot, "UnavailabilityPlugin.detached"); byte[] validPluginPackage = File.ReadAllBytes(installedPlugin); - if (updateInPlace) - { - WriteUnavailableGenerationPlugin(fixture.projectRoot, fixture.host.serialization, "this Plugin update is deliberately invalid C#"); - } - else + for (int cycle = 0; cycle < 10; cycle++) { - File.Move(installedPlugin, detachedPlugin); - } + if (updateInPlace) + { + WriteUnavailableGenerationPlugin(fixture.projectRoot, fixture.host.serialization, "this Plugin update is deliberately invalid C#"); + } + else + { + File.Move(installedPlugin, detachedPlugin); + } - Assert.True(fixture.RefreshPlugins()); - ScriptCompilationResult unavailable = fixture.CompilePendingPluginReload(reload); - Assert.False(unavailable.success); - Assert.Contains(unavailable.diagnostics, static diagnostic => diagnostic.severity == DiagnosticSeverity.Error); - Assert.True(reload.ApplyPendingReload()); - Assert.DoesNotContain(fixture.host.modules.modules, static module => module.domain is AssemblyDomain.InnoPlugin or AssemblyDomain.InnoScripting); - Assert.Equal(updateInPlace ? 1 : 0, fixture.activePlugins.Count); - MissingGameComponent missingPlugin = Assert.IsType(state.pluginOwner.GetComponents().Single(component => component is not Transform)); - MissingGameComponent missingScript = Assert.IsType(state.scriptOwner.GetComponents().Single(component => component is not Transform)); - Assert.Equal(state.pluginComponentId, missingPlugin.identity.persistentId); - Assert.Equal(state.scriptComponentId, missingScript.identity.persistentId); - Assert.Equal("UnavailabilityPlugin.PluginBehavior", missingPlugin.missingTypeName); - Assert.Equal("DependentBehavior", missingScript.missingTypeName); - CompleteUnloadVerification(reload); - if (updateInPlace) - File.WriteAllBytes(installedPlugin, validPluginPackage); - else - File.Move(detachedPlugin, installedPlugin); - Assert.True(fixture.RefreshPlugins()); - ScriptCompilationResult recoveredCompilation = fixture.CompilePendingPluginReload(reload); - Assert.True(recoveredCompilation.success, FormatDiagnostics(recoveredCompilation)); - Assert.True(reload.ApplyPendingReload()); - AssertRecoveredAvailability(state); + Assert.True(fixture.RefreshPlugins()); + ScriptCompilationResult unavailable = fixture.CompilePendingPluginReload(reload); + Assert.False(unavailable.success); + Assert.Contains(unavailable.diagnostics, static diagnostic => diagnostic.severity == DiagnosticSeverity.Error); + Assert.True(reload.ApplyPendingReload()); + Assert.DoesNotContain(fixture.host.modules.modules, static module => module.domain is AssemblyDomain.InnoPlugin or AssemblyDomain.InnoScripting); + Assert.Equal(updateInPlace ? 1 : 0, fixture.activePlugins.Count); + MissingGameComponent missingPlugin = Assert.IsType(state.pluginOwner.GetComponents().Single(component => component is not Transform)); + MissingGameComponent missingScript = Assert.IsType(state.scriptOwner.GetComponents().Single(component => component is not Transform)); + Assert.Equal(state.pluginComponentId, missingPlugin.identity.persistentId); + Assert.Equal(state.scriptComponentId, missingScript.identity.persistentId); + Assert.Equal("UnavailabilityPlugin.PluginBehavior", missingPlugin.missingTypeName); + Assert.Equal("DependentBehavior", missingScript.missingTypeName); + CompleteUnloadVerification(reload); + if (updateInPlace) + File.WriteAllBytes(installedPlugin, validPluginPackage); + else + File.Move(detachedPlugin, installedPlugin); + Assert.True(fixture.RefreshPlugins()); + ScriptCompilationResult recoveredCompilation = fixture.CompilePendingPluginReload(reload); + Assert.True(recoveredCompilation.success, FormatDiagnostics(recoveredCompilation)); + Assert.True(reload.ApplyPendingReload()); + AssertRecoveredAvailability(state); + CompleteUnloadVerification(reload); + Assert.Equal(GenerationState.Ready, fixture.host.modules.generations.state); + } } [MethodImpl(MethodImplOptions.NoInlining)] @@ -1668,6 +1678,7 @@ private static (Guid persistentId, Guid stableTypeId) LoadScriptAssetAndUnloadSc } [Fact] + [Trait("Acceptance", "Lifecycle")] public void ContentPlayStopReloadPlaySoakRetiresEverySessionBeforeTheNextGeneration() { using var fixture = new ScriptingFixture(WriteUnavailableGenerationPlugin); @@ -1694,7 +1705,7 @@ public void ContentPlayStopReloadPlaySoakRetiresEverySessionBeforeTheNextGenerat fixture.host.logs ); using IDisposable registration = reloads.RegisterGuard(play); - for (int iteration = 0; iteration < 8; iteration++) + for (int iteration = 0; iteration < 10; iteration++) { Assert.True(play.EnterPlayMode()); play.AdvanceTransition(); @@ -1899,6 +1910,18 @@ public void FailedResultHasNoActivationOrRuntimeArtifacts() } private static int ReadVersion(Type type) => (int)type.GetProperty("version")!.GetValue(Activator.CreateInstance(type))!; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void AssertRetainedActiveType( + ScriptingFixture fixture, + WeakReference expected, + int expectedVersion + ) { + Type active = fixture.ResolveActiveType("VersionedBehavior"); + Assert.Same(expected.Target, active); + Assert.Equal(expectedVersion, ReadVersion(active)); + } + [MethodImpl(MethodImplOptions.NoInlining)] private static WeakReference CaptureActiveType( ScriptingFixture fixture, From 14c4d0b83c6c13b08d7b0961d222a72c1795e4f2 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 17:00:31 +0800 Subject: [PATCH 22/54] docs(editor): record controlled performance results and remaining acceptance --- .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 8 + ...EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md | 22 +- .../README.md | 16 + .../frames.json | 1642 +++++++++++++++++ .../identity.json | 10 + .../menus.json | 792 ++++++++ .../summary.json | 102 + 7 files changed, 2590 insertions(+), 2 deletions(-) create mode 100644 records/architecture/acceptance/editor-ui-performance-2026-10-28/README.md create mode 100644 records/architecture/acceptance/editor-ui-performance-2026-10-28/frames.json create mode 100644 records/architecture/acceptance/editor-ui-performance-2026-10-28/identity.json create mode 100644 records/architecture/acceptance/editor-ui-performance-2026-10-28/menus.json create mode 100644 records/architecture/acceptance/editor-ui-performance-2026-10-28/summary.json diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index d43af7e76..9b2b0fb69 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -205,3 +205,11 @@ The native color candidate using ordinary integer conversion failed for NaN. The - PR #26 and the parent issue remain incomplete. Full workload/product/input/visual/lifetime acceptance must retain its actual limitations; automated fixture success does not certify physical devices or the entire original plan. Final working-tree verification: the twelve-project compilation, Core 49 / Scene 53, Workflows 305, Visual 136, Lifecycle 11, Editor Debug/Release, Build entry, six 300-frame FlappyBird renderer smokes, three official companion script/IDE projections, and the 241-project architecture/Wiki gate pass. Evidence uses `final-candidate*`, `products/final-candidate-*`, `companion-projections-final` and `wiki-final-candidate`. Controlled performance and the new committed-source CI remain separate pending gates; this does not close the parent or the original full acceptance matrix. + +## Committed correction and controlled measurements + +- Correction commit: `f7dce89c0d91ecb0f2333b8ca3117fefc71db96a`, pushed to editor-ui; author and committer Aaron Liao, no co-author. +- The 30 alternating large-workload processes and five menu/statistics process pairs completed. All 12,778 frozen input/dependency files match before/after. Graph CPU/P95 no longer exceeds the 10% regression threshold; all three sampled workload allocations decrease. +- Absolute hierarchy performance remains poor: 345.14 ms median, 402.69 ms P95 and 139.06 MB allocated per frame. New issue #46 owns the remaining complete-scene dirty-check bottleneck and the required correctness/invalidation design; no timer relaxation or partial cache is used. +- Exact-commit managed Windows/macOS and NuGet dependency CI pass. Full native Windows/macOS CI is still running; no completion or Ready claim is made. +- Full E08/E09/E10/E11 and the parent retain the limitations in the report. Relative performance improvement and focused green suites do not establish whole-plan completion. diff --git a/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md index 109bdf088..00e095587 100644 --- a/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md +++ b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md @@ -25,13 +25,29 @@ | Canvas / Rendering2D / FlappyBird | 正式脚本编译及 IDE 投影通过;`companion-projections-final/*/generation.log`。未编辑派生代码或插件包。 | | 完整 Contracts | 前一修正候选 713 项通过,`unified-correction-final/cf4d562d8bf1413f945d8f23e5d1d712`。后续 Core / modifier 修正后的最终完整 CI 另行记录。 | | 最终 Wiki / 架构 | 通过;`wiki-final-candidate`,241/241 工程实际归属、API 清单与可编译示例门禁。网络 NU1900 警告原样保留。 | -| 最终成对性能 | 待验收;必须在所有构建、测试和产品进程结束后单独采样。 | -| CI | `660962a5` 的 Windows 及两个 Managed job 通过;ARM64 自动构建、renderer、架构、Wiki 通过,但有七个原生输入失败。已修正呈现边界与 fixture,待新提交 CI。 | +| 最终成对性能 | 三类负载各五组交替原版/当前进程、共 30 次,以及菜单/统计五组对照均完成;输入与依赖 12,778 文件、583,137,380 bytes 前后完全一致。详见下方性能结果。完整 E11 产品/交互矩阵仍未闭合。 | +| CI | `f7dce89c` 的 Windows/macOS Managed 与 NuGet job 通过;完整 Windows/macOS ARM64 job 已通过 Debug 及真实 renderer smoke,正在执行 Release/后续测试。旧 `660962a5` 的七个 Mac 输入失败已修正,仍须等待新提交的完整原生套件确认。 | Core 内部现在只在实际需要时创建回调、循环检测和恢复去重集合,复用 BCL reference equality;上下文仅在全部 owner 身份一致时复用。正式输出的同一属性 bytes 保持一致,分配测量为 fresh 1,888 / prepared 1,168 bytes,原 prepared 为 2,096。新公开边界回归先失败再通过;没有修改 `SerializationContext.With` 的新实例语义。该观察不是整帧或全产品 CPU 收益证明。 原生 command-key 模式会在 ImGui 内交换 Control / Super。`EditorUiInput` 现在还原物理 Core 修饰键含义,列表选择复用该边界;原生文本控件保留自身配置。四个真实输入组合验证两种 primary modifier 与两种原生模式。共享 Editor 不增加 OS 检测或平台引用,fixtures 显式选择键盘配置。 +## 最终受控性能结果 + +源码候选为 `f7dce89c0d91ecb0f2333b8ca3117fefc71db96a`;原生产基线为 `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`,原版只移植验收 harness,不修改生产实现。两侧使用同一 .NET 10.0.402、Debug 配置、机器、冻结数据和 Native command-validation 方法;各进程预热五帧,再采样十帧,五组进程交替顺序运行,没有构建/测试/产品并发。原始各组数据保留在 [性能证据目录](editor-ui-performance-2026-10-28/README.md)。 + +| 负载 | 原版 median / P95 | 当前 median / P95 | 原版 → 当前分配/帧 | +| --- | --- | --- | --- | +| 10,000 对象层级 | 5117.21 / 5594.09 ms | 345.14 / 402.69 ms | 709.02 → 139.06 MB | +| 10,000 资源目录 | 4104.39 / 4320.28 ms | 18.73 / 27.58 ms | 249.23 → 0.98 MB | +| 2,000 Shader 节点 | 29.01 / 33.17 ms | 28.78 / 33.39 ms | 4.36 → 3.63 MB | + +这里 MB 使用十进制字节。图负载的 P95 为 +0.65%,CPU 中位数相同,前次超过 10% 的回退已消除;三类负载均通过本次相对回退与分配门槛。菜单打开的每次分配 8544.29 → 4240.14 bytes,时间 10.61 → 6.18 µs;无变化统计读取 16792 → 400 bytes,时间 30.40 → 0.24 µs。后两项是公开菜单/统计契约负载,不能等同完整产品帧收益。 + +**绝对表现仍有严重限制:345 ms/帧、139 MB/帧的大层级负载不能称为优秀。** 当前 `IsDirty` 仍周期性序列化完整 Scene 再散列,检查超过既有 100 ms 周期时会持续占用后续帧。[#46](https://github.com/Wolfiora/InnoEngine/issues/46) 已记录真实调用链、职责、设计约束和验收要求;没有用延长周期、隐藏检查或不完整 mutation cache 掩盖。#45 的 Core 改良不代表 #46 或完整 E07/E11 已完成。 + +本次是 Native command-validation / headless public-contract 比较,不是 GPU 或完整 FlappyBird 产品帧性能认证。原版与当前 FlappyBird API 输入不相容,以及滚动、搜索、属性拖动、冷启动等完整性能矩阵仍按对应 issue 保持未闭合。 + 证据根目录:`D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`。表中的路径均相对此目录。日志、TRX、binlog、截图和大规模输入不进入源码仓库;本记录与执行台账提供可追溯索引。 ## 实现与职责自查 @@ -186,6 +202,8 @@ tests/fixtures/editor-ui/PanelLifecycle.editor.cs 大型输入、baseline、binlog、TRX 与截图集中在 D 盘。保留现行有效产物和活动 reader,维护通过既有 owner/lease 协议进行,不手删协调锁或全局依赖缓存。完成时核对实际磁盘空间、companion/BGCS 状态、最终提交和 CI,并在此追加精确结果。 +最终采样后通过正式 cache clean 先 dry-run 再执行:逻辑 inventory 10,787,641,712 bytes,保护 174,384,757,退休 10,613,256,955,skipped 0。C 盘实际可用从 92.090 增至 93.501 GiB,D 盘约 97.782 GiB;hardlink 的逻辑回收量不能当作实际卷空间。日志为 `cache-after-final-preview.log` 与 `cache-after-final-maintenance.log`。没有清理 SDK/NuGet 全局缓存或用户内容。 + 本环境没有 `/System/Library/Sounds/Glass.aiff`,不能播放指定提示音。 ## Settings 与测试输入 owner 的专项复验 diff --git a/records/architecture/acceptance/editor-ui-performance-2026-10-28/README.md b/records/architecture/acceptance/editor-ui-performance-2026-10-28/README.md new file mode 100644 index 000000000..8d3841fcf --- /dev/null +++ b/records/architecture/acceptance/editor-ui-performance-2026-10-28/README.md @@ -0,0 +1,16 @@ +# Editor UI controlled performance evidence + +[Acceptance report](../EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md) · [E11](https://github.com/Wolfiora/InnoEngine/issues/38) + +Production baseline: `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`. Corrected source: `f7dce89c0d91ecb0f2333b8ca3117fefc71db96a`. The local SDK-built binaries were frozen from that source working tree before commit metadata publication; compiled-source/PDB provenance and the runtime content hashes are retained in the evidence owner. + +- [Frame measurements](frames.json): five alternating original/current process pairs per workload; five warmup and ten measured frames per process. +- [Menu/statistics measurements](menus.json): five alternating process pairs, five samples per workload/process, 2,000 operations per sample. +- [Summary](summary.json): aggregated medians and ratios; frame allocation and CPU normalized per measured frame. +- [Identity receipt](identity.json): every workload input and frozen runtime dependency matched before and after sampling. + +All functional assertions pass. Shader P95 is +0.65%, CPU is unchanged and allocation decreases. Relative CPU/P95 regression is below 10% in all three recorded workloads. + +This is native command validation and public headless contract measurement. It does not establish GPU, complete FlappyBird, cold-start, scroll/search/property-drag or physical-display performance. The remaining large-scene dirty-state cost is tracked in [#46](https://github.com/Wolfiora/InnoEngine/issues/46); full original/current FlappyBird comparison remains blocked by the documented API/input mismatch. + +Raw TRX, per-process logs, full SHA-256 manifests, SDK/device identity and invalidated prior trials remain under `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27/e11`. Invalid or overlapping runs are not included in these final measurements. diff --git a/records/architecture/acceptance/editor-ui-performance-2026-10-28/frames.json b/records/architecture/acceptance/editor-ui-performance-2026-10-28/frames.json new file mode 100644 index 000000000..d5ab79277 --- /dev/null +++ b/records/architecture/acceptance/editor-ui-performance-2026-10-28/frames.json @@ -0,0 +1,1642 @@ +[ + { + "source": "original", + "workload": "hierarchy", + "pair": 0, + "started": "2026-10-28T16:03:42.5737556+08:00", + "finished": "2026-10-28T16:04:57.5770970+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4544.1741, + 4577.1725, + 4505.1724, + 4545.2887, + 4529.2605, + 4478.4727, + 4515.6421, + 4527.4402, + 4504.702, + 4564.9014 + ], + "median": 4528.350350000001, + "p95": 4577.1725, + "allocatedBytes": 7090186312, + "totalAllocatedBytes": 7090266264, + "cpuMilliseconds": 45265.625, + "gen0Collections": 827, + "gen1Collections": 108, + "gen2Collections": 14 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-0-original" + }, + { + "source": "current", + "workload": "hierarchy", + "pair": 0, + "started": "2026-10-28T16:04:57.5968582+08:00", + "finished": "2026-10-28T16:05:08.5999727+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 359.7007, + 342.8073, + 318.4149, + 363.1619, + 331.1138, + 353.3101, + 317.5365, + 347.4637, + 299.6789, + 347.9906 + ], + "median": 345.1355, + "p95": 363.1619, + "allocatedBytes": 1390615488, + "totalAllocatedBytes": 1390700048, + "cpuMilliseconds": 3390.625, + "gen0Collections": 146, + "gen1Collections": 51, + "gen2Collections": 14 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-0-current" + }, + { + "source": "current", + "workload": "hierarchy", + "pair": 1, + "started": "2026-10-28T16:05:08.6037632+08:00", + "finished": "2026-10-28T16:05:19.4986513+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 352.2365, + 304.302, + 402.6929, + 301.7208, + 333.3494, + 353.3288, + 307.5506, + 351.763, + 332.1294, + 355.3793 + ], + "median": 342.5562, + "p95": 402.6929, + "allocatedBytes": 1390612064, + "totalAllocatedBytes": 1390690504, + "cpuMilliseconds": 3437.5, + "gen0Collections": 145, + "gen1Collections": 53, + "gen2Collections": 13 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-1-current" + }, + { + "source": "original", + "workload": "hierarchy", + "pair": 1, + "started": "2026-10-28T16:05:19.5010919+08:00", + "finished": "2026-10-28T16:06:48.9566882+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 6580.8696, + 7101.9513, + 5058.1122, + 5646.3278, + 5532.3301, + 6175.6381, + 5559.743, + 6030.9225, + 5810.8294, + 5679.6545 + ], + "median": 5745.24195, + "p95": 7101.9513, + "allocatedBytes": 7090198928, + "totalAllocatedBytes": 7090284544, + "cpuMilliseconds": 58625, + "gen0Collections": 828, + "gen1Collections": 112, + "gen2Collections": 15 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-1-original" + }, + { + "source": "original", + "workload": "hierarchy", + "pair": 2, + "started": "2026-10-28T16:06:48.9598849+08:00", + "finished": "2026-10-28T16:08:13.7292672+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 5015.7623, + 5201.5373, + 5103.3021, + 5015.0432, + 5089.7225, + 5287.1732, + 5257.3071, + 5290.2842, + 5083.1108, + 5131.1104 + ], + "median": 5117.206249999999, + "p95": 5290.2842, + "allocatedBytes": 7090167352, + "totalAllocatedBytes": 7090241640, + "cpuMilliseconds": 51203.125, + "gen0Collections": 826, + "gen1Collections": 116, + "gen2Collections": 13 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-2-original" + }, + { + "source": "current", + "workload": "hierarchy", + "pair": 2, + "started": "2026-10-28T16:08:13.7319446+08:00", + "finished": "2026-10-28T16:08:24.7942640+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 360.0098, + 341.4601, + 364.5159, + 299.3946, + 336.7417, + 359.6279, + 316.2664, + 329.1443, + 334.2019, + 306.258 + ], + "median": 335.47180000000003, + "p95": 364.5159, + "allocatedBytes": 1390614232, + "totalAllocatedBytes": 1390680048, + "cpuMilliseconds": 3500, + "gen0Collections": 142, + "gen1Collections": 51, + "gen2Collections": 11 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-2-current" + }, + { + "source": "current", + "workload": "hierarchy", + "pair": 3, + "started": "2026-10-28T16:08:24.7966407+08:00", + "finished": "2026-10-28T16:08:37.4379711+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 368.1309, + 319.0127, + 414.3714, + 306.6487, + 380.56, + 411.0422, + 329.4236, + 365.7039, + 372.9448, + 319.5688 + ], + "median": 366.9174, + "p95": 414.3714, + "allocatedBytes": 1390612128, + "totalAllocatedBytes": 1390690400, + "cpuMilliseconds": 3671.875, + "gen0Collections": 145, + "gen1Collections": 51, + "gen2Collections": 13 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-3-current" + }, + { + "source": "original", + "workload": "hierarchy", + "pair": 3, + "started": "2026-10-28T16:08:37.4419288+08:00", + "finished": "2026-10-28T16:10:03.9240682+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 5058.1373, + 5728.451, + 5414.9813, + 5606.8391, + 4959.2011, + 4908.2882, + 5291.0175, + 5129.1555, + 5090.3556, + 5101.146 + ], + "median": 5115.15075, + "p95": 5728.451, + "allocatedBytes": 7090190832, + "totalAllocatedBytes": 7090265120, + "cpuMilliseconds": 52062.5, + "gen0Collections": 826, + "gen1Collections": 110, + "gen2Collections": 13 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-3-original" + }, + { + "source": "original", + "workload": "hierarchy", + "pair": 4, + "started": "2026-10-28T16:10:03.9270355+08:00", + "finished": "2026-10-28T16:11:28.2924597+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 5383.1875, + 5594.0916, + 5071.3082, + 5463.9791, + 5070.7983, + 5478.7259, + 5091.3866, + 5214.7863, + 5295.1281, + 5398.5025 + ], + "median": 5339.1578, + "p95": 5594.0916, + "allocatedBytes": 7090199904, + "totalAllocatedBytes": 7090296848, + "cpuMilliseconds": 52953.125, + "gen0Collections": 830, + "gen1Collections": 122, + "gen2Collections": 17 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-4-original" + }, + { + "source": "current", + "workload": "hierarchy", + "pair": 4, + "started": "2026-10-28T16:11:28.2963797+08:00", + "finished": "2026-10-28T16:11:39.7952066+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-hierarchy-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 359.7635, + 312.2592, + 422.9317, + 326.3216, + 343.9195, + 362.283, + 322.3128, + 389.681, + 385.7471, + 387.7434 + ], + "median": 361.02325, + "p95": 422.9317, + "allocatedBytes": 1390621520, + "totalAllocatedBytes": 1390699864, + "cpuMilliseconds": 3703.125, + "gen0Collections": 145, + "gen1Collections": 53, + "gen2Collections": 13 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\hierarchy", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\hierarchy-4-current" + }, + { + "source": "original", + "workload": "assets", + "pair": 0, + "started": "2026-10-28T16:11:39.7982345+08:00", + "finished": "2026-10-28T16:13:56.5309264+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4109.2795, + 4088.6515, + 3999.0073, + 4162.9321, + 4084.1437, + 4214.3914, + 4099.5033, + 4152.8808, + 4179.1978, + 4091.9468 + ], + "median": 4104.3914, + "p95": 4214.3914, + "allocatedBytes": 2492323840, + "totalAllocatedBytes": 2492330088, + "cpuMilliseconds": 40984.375, + "gen0Collections": 297, + "gen1Collections": 7, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-0-original" + }, + { + "source": "current", + "workload": "assets", + "pair": 0, + "started": "2026-10-28T16:13:56.5340317+08:00", + "finished": "2026-10-28T16:15:07.2815735+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 20.3641, + 16.7308, + 18.9641, + 19.7994, + 19.392, + 17.2206, + 17.007, + 23.9667, + 18.5614, + 19.3518 + ], + "median": 19.15795, + "p95": 23.9667, + "allocatedBytes": 9801832, + "totalAllocatedBytes": 9802824, + "cpuMilliseconds": 218.75, + "gen0Collections": 1, + "gen1Collections": 1, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-0-current" + }, + { + "source": "current", + "workload": "assets", + "pair": 1, + "started": "2026-10-28T16:15:07.2838128+08:00", + "finished": "2026-10-28T16:16:22.0548770+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 19.1997, + 21.0307, + 17.9876, + 17.6283, + 18.2606, + 22.8607, + 21.9787, + 28.6163, + 17.9572, + 17.7507 + ], + "median": 18.730150000000002, + "p95": 28.6163, + "allocatedBytes": 9801832, + "totalAllocatedBytes": 9802824, + "cpuMilliseconds": 218.75, + "gen0Collections": 1, + "gen1Collections": 1, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-1-current" + }, + { + "source": "original", + "workload": "assets", + "pair": 1, + "started": "2026-10-28T16:16:22.0576549+08:00", + "finished": "2026-10-28T16:18:34.9130369+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4154.0909, + 4011.8881, + 4245.3433, + 4236.046, + 4023.3207, + 4050.0702, + 4020.5302, + 4189.9033, + 5159.6965, + 4401.8816 + ], + "median": 4171.9971000000005, + "p95": 5159.6965, + "allocatedBytes": 2492321744, + "totalAllocatedBytes": 2492328064, + "cpuMilliseconds": 41937.5, + "gen0Collections": 297, + "gen1Collections": 6, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-1-original" + }, + { + "source": "original", + "workload": "assets", + "pair": 2, + "started": "2026-10-28T16:18:34.9153885+08:00", + "finished": "2026-10-28T16:20:46.3904341+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4228.8878, + 4023.8318, + 4147.0834, + 4063.2924, + 4153.7989, + 4136.9028, + 4071.6927, + 4018.7011, + 4066.1196, + 4062.0817 + ], + "median": 4068.90615, + "p95": 4228.8878, + "allocatedBytes": 2492324328, + "totalAllocatedBytes": 2492330648, + "cpuMilliseconds": 40843.75, + "gen0Collections": 297, + "gen1Collections": 6, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-2-original" + }, + { + "source": "current", + "workload": "assets", + "pair": 2, + "started": "2026-10-28T16:20:46.3928668+08:00", + "finished": "2026-10-28T16:21:57.1447238+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 18.4949, + 18.1745, + 17.7758, + 18.112, + 19.1794, + 19.7925, + 17.578, + 27.5775, + 18.8044, + 17.5712 + ], + "median": 18.334699999999998, + "p95": 27.5775, + "allocatedBytes": 9801832, + "totalAllocatedBytes": 9802824, + "cpuMilliseconds": 203.125, + "gen0Collections": 1, + "gen1Collections": 1, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-2-current" + }, + { + "source": "current", + "workload": "assets", + "pair": 3, + "started": "2026-10-28T16:21:57.1472311+08:00", + "finished": "2026-10-28T16:23:09.7396555+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 20.2968, + 25.8852, + 18.8486, + 17.9423, + 17.6235, + 17.8499, + 18.368, + 19.702, + 19.7826, + 17.9619 + ], + "median": 18.6083, + "p95": 25.8852, + "allocatedBytes": 9801832, + "totalAllocatedBytes": 9802488, + "cpuMilliseconds": 218.75, + "gen0Collections": 1, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-3-current" + }, + { + "source": "original", + "workload": "assets", + "pair": 3, + "started": "2026-10-28T16:23:09.7418381+08:00", + "finished": "2026-10-28T16:25:22.3243487+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4106.8955, + 4131.254, + 4041.1692, + 4076.6107, + 4034.3314, + 4288.4148, + 4320.2758, + 4173.3871, + 4073.8151, + 4132.8669 + ], + "median": 4119.07475, + "p95": 4320.2758, + "allocatedBytes": 2492317408, + "totalAllocatedBytes": 2492318064, + "cpuMilliseconds": 41250, + "gen0Collections": 296, + "gen1Collections": 4, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-3-original" + }, + { + "source": "original", + "workload": "assets", + "pair": 4, + "started": "2026-10-28T16:25:22.3265446+08:00", + "finished": "2026-10-28T16:27:37.6558499+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "EE19B2630EDB373B87DF53EF3C59E718DF24ED94168E54FA22453580E9EF89CA", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 4030.6327, + 4482.4197, + 4233.7477, + 3995.7064, + 4112.4682, + 4059.7616, + 4055.1546, + 4094.5436, + 4105.3343, + 4057.1702 + ], + "median": 4077.1526, + "p95": 4482.4197, + "allocatedBytes": 2492451848, + "totalAllocatedBytes": 2492458096, + "cpuMilliseconds": 41031.25, + "gen0Collections": 297, + "gen1Collections": 5, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-4-original" + }, + { + "source": "current", + "workload": "assets", + "pair": 4, + "started": "2026-10-28T16:27:37.6581447+08:00", + "finished": "2026-10-28T16:28:48.6853168+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.PlayMode.Tests\\debug\\Inno.Editor.PlayMode.Tests.dll", + "assemblySha256": "CC9BE56906B16F2DDA9A9D392C364E0DF9B06BC28C83F92B6FE4F1753E2D2152", + "measurement": { + "workload": "10000-asset-directory-rows", + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 18.2745, + 20.0993, + 19.7436, + 18.9603, + 19.0007, + 18.9517, + 27.968, + 19.594, + 18.4732, + 18.5726 + ], + "median": 18.9805, + "p95": 27.968, + "allocatedBytes": 9801832, + "totalAllocatedBytes": 9802824, + "cpuMilliseconds": 218.75, + "gen0Collections": 1, + "gen1Collections": 1, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1000x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\assets", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\assets-4-current" + }, + { + "source": "original", + "workload": "shader", + "pair": 0, + "started": "2026-10-28T16:28:48.6890063+08:00", + "finished": "2026-10-28T16:28:52.5429390+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "18E566F57495ECA1E83C263B062E8E080164CB5FC784446826D3FF7AF221CEB1", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 30.5814, + 29.5842, + 33.1747, + 29.47, + 28.9357, + 24.9811, + 26.6448, + 25.1961, + 29.6176, + 28.205 + ], + "vertices": [ + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260 + ], + "indices": [ + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183 + ], + "median": 29.202849999999998, + "p95": 33.1747, + "allocatedBytes": 43650008, + "totalAllocatedBytes": 43655608, + "cpuMilliseconds": 296.875, + "gen0Collections": 5, + "gen1Collections": 1, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-0-original" + }, + { + "source": "current", + "workload": "shader", + "pair": 0, + "started": "2026-10-28T16:28:52.5458715+08:00", + "finished": "2026-10-28T16:28:56.4131971+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "0617697D54B6FD939873F019D85C50A89000BEDA249ED31D560FAA1F40E97DA0", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 27.7437, + 25.2206, + 26.6718, + 26.9135, + 27.2015, + 29.138, + 28.4928, + 26.8191, + 31.3695, + 28.8592 + ], + "vertices": [ + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030 + ], + "indices": [ + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625 + ], + "median": 27.4726, + "p95": 31.3695, + "allocatedBytes": 36299208, + "totalAllocatedBytes": 36299864, + "cpuMilliseconds": 312.5, + "gen0Collections": 4, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-0-current" + }, + { + "source": "current", + "workload": "shader", + "pair": 1, + "started": "2026-10-28T16:28:56.4160231+08:00", + "finished": "2026-10-28T16:29:00.0548846+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "0617697D54B6FD939873F019D85C50A89000BEDA249ED31D560FAA1F40E97DA0", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 27.7515, + 27.2683, + 32.7437, + 29.2941, + 26.7266, + 38.1894, + 29.7327, + 26.9976, + 23.8018, + 24.024 + ], + "vertices": [ + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030 + ], + "indices": [ + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625 + ], + "median": 27.509900000000002, + "p95": 38.1894, + "allocatedBytes": 36299208, + "totalAllocatedBytes": 36299864, + "cpuMilliseconds": 328.125, + "gen0Collections": 4, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-1-current" + }, + { + "source": "original", + "workload": "shader", + "pair": 1, + "started": "2026-10-28T16:29:00.0582048+08:00", + "finished": "2026-10-28T16:29:03.7091488+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "18E566F57495ECA1E83C263B062E8E080164CB5FC784446826D3FF7AF221CEB1", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 29.1145, + 28.8873, + 28.1969, + 32.0744, + 27.8212, + 26.8951, + 27.6526, + 28.6891, + 30.5919, + 28.656 + ], + "vertices": [ + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260 + ], + "indices": [ + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183 + ], + "median": 28.67255, + "p95": 32.0744, + "allocatedBytes": 43644720, + "totalAllocatedBytes": 43645376, + "cpuMilliseconds": 343.75, + "gen0Collections": 4, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-1-original" + }, + { + "source": "original", + "workload": "shader", + "pair": 2, + "started": "2026-10-28T16:29:03.7118566+08:00", + "finished": "2026-10-28T16:29:07.3934812+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "18E566F57495ECA1E83C263B062E8E080164CB5FC784446826D3FF7AF221CEB1", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 28.076, + 29.9392, + 27.0486, + 30.9332, + 33.9416, + 27.8708, + 30.0089, + 27.3673, + 29.9677, + 25.878 + ], + "vertices": [ + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260 + ], + "indices": [ + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183 + ], + "median": 29.0076, + "p95": 33.9416, + "allocatedBytes": 43649968, + "totalAllocatedBytes": 43655568, + "cpuMilliseconds": 328.125, + "gen0Collections": 5, + "gen1Collections": 1, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-2-original" + }, + { + "source": "current", + "workload": "shader", + "pair": 2, + "started": "2026-10-28T16:29:07.3959883+08:00", + "finished": "2026-10-28T16:29:11.1072652+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "0617697D54B6FD939873F019D85C50A89000BEDA249ED31D560FAA1F40E97DA0", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 30.1302, + 28.672, + 29.3068, + 28.8909, + 30.6755, + 32.4223, + 28.1081, + 28.5942, + 27.2028, + 27.2339 + ], + "vertices": [ + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030 + ], + "indices": [ + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625 + ], + "median": 28.78145, + "p95": 32.4223, + "allocatedBytes": 36299208, + "totalAllocatedBytes": 36299864, + "cpuMilliseconds": 312.5, + "gen0Collections": 4, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-2-current" + }, + { + "source": "current", + "workload": "shader", + "pair": 3, + "started": "2026-10-28T16:29:11.1109802+08:00", + "finished": "2026-10-28T16:29:14.7454997+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "0617697D54B6FD939873F019D85C50A89000BEDA249ED31D560FAA1F40E97DA0", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 32.3963, + 31.3383, + 35.4828, + 38.1821, + 34.0223, + 35.9638, + 27.1198, + 26.995, + 23.6151, + 23.7369 + ], + "vertices": [ + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030 + ], + "indices": [ + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625 + ], + "median": 31.8673, + "p95": 38.1821, + "allocatedBytes": 36299208, + "totalAllocatedBytes": 36299864, + "cpuMilliseconds": 375, + "gen0Collections": 4, + "gen1Collections": 0, + "gen2Collections": 0 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-3-current" + }, + { + "source": "original", + "workload": "shader", + "pair": 3, + "started": "2026-10-28T16:29:14.7483684+08:00", + "finished": "2026-10-28T16:29:18.3034075+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "18E566F57495ECA1E83C263B062E8E080164CB5FC784446826D3FF7AF221CEB1", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 29.8139, + 29.3704, + 29.569, + 28.2562, + 29.527, + 29.4158, + 28.9626, + 27.2142, + 31.9686, + 24.1255 + ], + "vertices": [ + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260 + ], + "indices": [ + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183 + ], + "median": 29.3931, + "p95": 31.9686, + "allocatedBytes": 43649648, + "totalAllocatedBytes": 43655248, + "cpuMilliseconds": 281.25, + "gen0Collections": 5, + "gen1Collections": 1, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-3-original" + }, + { + "source": "original", + "workload": "shader", + "pair": 4, + "started": "2026-10-28T16:29:18.3068835+08:00", + "finished": "2026-10-28T16:29:21.8302222+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\original-managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "18E566F57495ECA1E83C263B062E8E080164CB5FC784446826D3FF7AF221CEB1", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 26.2107, + 25.7971, + 28.3931, + 26.3431, + 29.1429, + 27.2567, + 26.4123, + 24.9495, + 35.8829, + 32.7815 + ], + "vertices": [ + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260, + 182260 + ], + "indices": [ + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183, + 456183 + ], + "median": 26.8345, + "p95": 35.8829, + "allocatedBytes": 43648952, + "totalAllocatedBytes": 43659568, + "cpuMilliseconds": 312.5, + "gen0Collections": 6, + "gen1Collections": 2, + "gen2Collections": 2 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-4-original" + }, + { + "source": "current", + "workload": "shader", + "pair": 4, + "started": "2026-10-28T16:29:21.8339628+08:00", + "finished": "2026-10-28T16:29:25.9175380+08:00", + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\managed\\bin\\Inno.Editor.Scripting.Tests\\debug\\Inno.Editor.Scripting.Tests.dll", + "assemblySha256": "0617697D54B6FD939873F019D85C50A89000BEDA249ED31D560FAA1F40E97DA0", + "measurement": { + "workload": "2000-shader-nodes", + "nodeCount": 2000, + "edgeCount": 1003, + "configuration": "Debug", + "samples": [ + { + "repetition": 0, + "milliseconds": [ + 30.3504, + 27.9832, + 31.2512, + 32.0933, + 32.527, + 30.7038, + 29.6007, + 30.5141, + 33.3918, + 28.7334 + ], + "vertices": [ + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030, + 184030 + ], + "indices": [ + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625, + 458625 + ], + "median": 30.60895, + "p95": 33.3918, + "allocatedBytes": 36305672, + "totalAllocatedBytes": 36311656, + "cpuMilliseconds": 312.5, + "gen0Collections": 5, + "gen1Collections": 1, + "gen2Collections": 1 + } + ], + "framework": ".NET 10.0.13", + "viewport": "1200x800", + "warmupFrames": 5, + "measuredFramesPerRepetition": 10, + "sharedInput": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\shared-input\\shader-graph", + "consumer": "native-command-offset-validation", + "supportsVertexOffsets": true + }, + "evidence": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\pairs-f78ce703511e45d28d031a7617df2d8b\\shader-4-current" + } +] diff --git a/records/architecture/acceptance/editor-ui-performance-2026-10-28/identity.json b/records/architecture/acceptance/editor-ui-performance-2026-10-28/identity.json new file mode 100644 index 000000000..91f66a58a --- /dev/null +++ b/records/architecture/acceptance/editor-ui-performance-2026-10-28/identity.json @@ -0,0 +1,10 @@ +{ + "manifestBefore": "e11/final-integrity-before.json", + "bytes": 583137380, + "menuEvidence": "e11/menu-pairs-fe8261b77205455fba2c1877a46058cb", + "manifestSha256": "aa6887634f004af8ab7ca931a9d40267f142d4942a00fd5d54c220cf57e1665e", + "manifestAfter": "e11/final-integrity-after.json", + "files": 12778, + "unchanged": true, + "frameEvidence": "e11/pairs-f78ce703511e45d28d031a7617df2d8b" +} diff --git a/records/architecture/acceptance/editor-ui-performance-2026-10-28/menus.json b/records/architecture/acceptance/editor-ui-performance-2026-10-28/menus.json new file mode 100644 index 000000000..71aa124e0 --- /dev/null +++ b/records/architecture/acceptance/editor-ui-performance-2026-10-28/menus.json @@ -0,0 +1,792 @@ +[ + { + "source": "original", + "pair": 0, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-original\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "A1A613678F86D92A55A076CE94DCD783C2A8F35C0C805AA78ACBEC4D405045A7", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 18.6294, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 19.0826, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 18.3725, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 17.387, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 17.2846, + "bytes": 17088576 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 55.1675, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 54.9601, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 60.2469, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 90.0206, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 50.6861, + "bytes": 33584000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\0-original.log" + }, + { + "source": "current", + "pair": 0, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-current\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "EF1D88745210722CBE88E57648A2A71C17327C93E97942250F26250D2867A2F4", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 22.1647, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 22.4778, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 18.2068, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 16.5582, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 17.0201, + "bytes": 8480288 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 0.8537, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 0.9072, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 0.7585, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 0.6141, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 1.1192, + "bytes": 800000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\0-current.log" + }, + { + "source": "current", + "pair": 1, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-current\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "EF1D88745210722CBE88E57648A2A71C17327C93E97942250F26250D2867A2F4", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 11.3777, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 13.8577, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 12.754, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 16.0016, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 12.6139, + "bytes": 8480288 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 0.4503, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 0.4985, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 0.4814, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 0.4283, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 0.6805, + "bytes": 800000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\1-current.log" + }, + { + "source": "original", + "pair": 1, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-original\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "A1A613678F86D92A55A076CE94DCD783C2A8F35C0C805AA78ACBEC4D405045A7", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 33.0158, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 40.2395, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 41.404, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 34.6911, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 28.8193, + "bytes": 17088576 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 61.6503, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 55.4783, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 70.4781, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 63.7655, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 73.3136, + "bytes": 33584000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\1-original.log" + }, + { + "source": "original", + "pair": 2, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-original\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "A1A613678F86D92A55A076CE94DCD783C2A8F35C0C805AA78ACBEC4D405045A7", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 21.8951, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 37.0477, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 28.086, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 25.533, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 31.4486, + "bytes": 17088576 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 68.8253, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 64.711, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 76.2951, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 60.8004, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 88.8199, + "bytes": 33584000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\2-original.log" + }, + { + "source": "current", + "pair": 2, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-current\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "EF1D88745210722CBE88E57648A2A71C17327C93E97942250F26250D2867A2F4", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 11.742, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 10.4581, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 11.1254, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 11.3832, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 11.3605, + "bytes": 8480288 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 0.3976, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 0.4273, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 0.4232, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 0.4422, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 0.6269, + "bytes": 800000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\2-current.log" + }, + { + "source": "current", + "pair": 3, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-current\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "EF1D88745210722CBE88E57648A2A71C17327C93E97942250F26250D2867A2F4", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 12.8404, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 14.1383, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 12.3513, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 11.7707, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 12.3834, + "bytes": 8480288 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 0.6417, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 0.5945, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 0.4527, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 0.4681, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 0.7121, + "bytes": 800000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\3-current.log" + }, + { + "source": "original", + "pair": 3, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-original\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "A1A613678F86D92A55A076CE94DCD783C2A8F35C0C805AA78ACBEC4D405045A7", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 22.8985, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 21.2117, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 19.301, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 19.2881, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 18.9335, + "bytes": 17088576 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 56.6941, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 53.7564, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 61.306, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 54.1069, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 64.2283, + "bytes": 33584000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\3-original.log" + }, + { + "source": "original", + "pair": 4, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-original\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "A1A613678F86D92A55A076CE94DCD783C2A8F35C0C805AA78ACBEC4D405045A7", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 19.4387, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 18.8756, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 19.387, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 25.0294, + "bytes": 17088576 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 16.9238, + "bytes": 17088576 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 49.6064, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 59.5073, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 50.6363, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 70.6603, + "bytes": 33584000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 44.074, + "bytes": 33584000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\4-original.log" + }, + { + "source": "current", + "pair": 4, + "assembly": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-harness-current\\bin\\Release\\net10.0\\MenuHarness.dll", + "sha256": "EF1D88745210722CBE88E57648A2A71C17327C93E97942250F26250D2867A2F4", + "samples": [ + { + "workload": "menu-open", + "sample": 0, + "iterations": 2000, + "milliseconds": 11.1479, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 1, + "iterations": 2000, + "milliseconds": 12.1451, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 2, + "iterations": 2000, + "milliseconds": 11.8925, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 3, + "iterations": 2000, + "milliseconds": 11.9273, + "bytes": 8480288 + }, + { + "workload": "menu-open", + "sample": 4, + "iterations": 2000, + "milliseconds": 11.5446, + "bytes": 8480288 + }, + { + "workload": "statistics-unchanged", + "sample": 0, + "iterations": 2000, + "milliseconds": 0.3991, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 1, + "iterations": 2000, + "milliseconds": 0.439, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 2, + "iterations": 2000, + "milliseconds": 0.4236, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 3, + "iterations": 2000, + "milliseconds": 0.4351, + "bytes": 800000 + }, + { + "workload": "statistics-unchanged", + "sample": 4, + "iterations": 2000, + "milliseconds": 0.7316, + "bytes": 800000 + } + ], + "log": "D:\\InnoEngine-Verification\\editor-ui-closure-2026-10-27\\e11\\menu-pairs-fe8261b77205455fba2c1877a46058cb\\4-current.log" + } +] diff --git a/records/architecture/acceptance/editor-ui-performance-2026-10-28/summary.json b/records/architecture/acceptance/editor-ui-performance-2026-10-28/summary.json new file mode 100644 index 000000000..a073896ab --- /dev/null +++ b/records/architecture/acceptance/editor-ui-performance-2026-10-28/summary.json @@ -0,0 +1,102 @@ +{ + "frames": [ + { + "workload": "hierarchy", + "frameMedianMs": { + "original": 5117.206249999999, + "current": 345.1355, + "ratio": 0.06744607958688396 + }, + "frameP95Ms": { + "original": 5594.0916, + "current": 402.6929, + "ratio": 0.07198539616333777 + }, + "allocatedBytesPerFrame": { + "original": 709019083.2, + "current": 139061423.2, + "ratio": 0.19613213028396523 + }, + "cpuMsPerFrame": { + "original": 5206.25, + "current": 350.0, + "ratio": 0.06722689075630252 + } + }, + { + "workload": "assets", + "frameMedianMs": { + "original": 4104.3914, + "current": 18.730150000000002, + "ratio": 0.004563441488548095 + }, + "frameP95Ms": { + "original": 4320.2758, + "current": 27.5775, + "ratio": 0.006383273030856039 + }, + "allocatedBytesPerFrame": { + "original": 249232384.0, + "current": 980183.2, + "ratio": 0.003932808346446664 + }, + "cpuMsPerFrame": { + "original": 4103.125, + "current": 21.875, + "ratio": 0.005331302361005331 + } + }, + { + "workload": "shader", + "frameMedianMs": { + "original": 29.0076, + "current": 28.78145, + "ratio": 0.9922037672885726 + }, + "frameP95Ms": { + "original": 33.1747, + "current": 33.3918, + "ratio": 1.006544143579294 + }, + "allocatedBytesPerFrame": { + "original": 4364964.8, + "current": 3629920.8, + "ratio": 0.8316036821190402 + }, + "cpuMsPerFrame": { + "original": 31.25, + "current": 31.25, + "ratio": 1.0 + } + } + ], + "menu": [ + { + "workload": "menu-open", + "millisecondsPerOperation": { + "original": 0.01060585, + "current": 0.00617565, + "ratio": 0.582287133987375 + }, + "bytesPerOperation": { + "original": 8544.288, + "current": 4240.144, + "ratio": 0.49625480789037074 + } + }, + { + "workload": "statistics-unchanged", + "millisecondsPerOperation": { + "original": 0.030400200000000002, + "current": 0.0002407, + "ratio": 0.007917711067690344 + }, + "bytesPerOperation": { + "original": 16792.0, + "current": 400.0, + "ratio": 0.023820867079561697 + } + } + ], + "scope": "Median of five process-level samples for frame workloads; menu samples include five samples per process across five alternating process pairs. Native command validation only; no whole-product/GPU claim." +} \ No newline at end of file From 6df06585afb8f72dcb9d67abffe181be2b2d17c1 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 19:00:01 +0800 Subject: [PATCH 23/54] perf(serialization): bound encoding buffers and converter lookup contention Calculate the exact current binary payload size before writing the owned result with BinaryWriter. Reuse the existing generation-owned converter cache for concurrent lookups while preserving single construction and last-lease retirement. Add independent byte, allocation and concurrent lifetime regressions; preserve all public APIs and serialized bytes. --- docs/core/Inno.Core.Serialization.md | 2 +- .../BinarySerializationFormat.Sizing.cs | 67 +++++++++ .../Internal/BinarySerializationFormat.cs | 28 ++-- .../Internal/ConverterRegistry.cs | 25 +--- .../SerializationBehaviorTests.Concurrency.cs | 64 ++++++++ .../SerializationBehaviorTests.Encoding.cs | 137 ++++++++++++++++++ 6 files changed, 293 insertions(+), 30 deletions(-) create mode 100644 src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.Sizing.cs create mode 100644 tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Concurrency.cs create mode 100644 tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs diff --git a/docs/core/Inno.Core.Serialization.md b/docs/core/Inno.Core.Serialization.md index 26696b0e6..f784413e6 100644 --- a/docs/core/Inno.Core.Serialization.md +++ b/docs/core/Inno.Core.Serialization.md @@ -299,7 +299,7 @@ static (float x, float y)[] RoundTripPoints( - Compatible property restore 只跳过单个成员的数据/类型不兼容;对象结构或 restore hook 错误仍由上层事务处理。 - 序列化层只实现当前二进制契约,不包含旧 schema 的兼容读取或迁移分支。不兼容数据会直接失败。 -单属性快照 CapturePropertyData 只读取指定的持久属性;其他 getter 不会因捕获该属性而执行。二进制格式与完整对象捕获保持一致,缓冲区按实际内容增长。 +单属性快照 CapturePropertyData 只读取指定的持久属性;其他 getter 不会因捕获该属性而执行。二进制节点编码先计算确定大小,再用标准库 BinaryWriter 写入独立的结果数组,避免增长缓冲区及最终复制。当前 magic、字段顺序、UTF-8 长度编码和调用方拥有的 bytes 语义保持一致;这是非公开实现,不增加格式或缓存协议。 内部序列化操作仅在需要时创建回调、恢复去重与循环检测集合;不使用跨操作对象池。已有上下文只有在 TypeCatalog、Registry 和 metadata source 均属于当前 owner 时才复用,否则重新绑定这些依赖并保留调用方服务。`SerializationContext.With` 仍返回新的不可变上下文,确定性键排序和当前二进制格式保持不变。 diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.Sizing.cs b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.Sizing.cs new file mode 100644 index 000000000..a2de2837c --- /dev/null +++ b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.Sizing.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Text; + +namespace Inno.Core.Serialization; + +internal static partial class BinarySerializationFormat +{ + private static int GetEncodedSize(SerializationNode node) + { + int payload = node switch + { + NullSerializationNode => 0, + ScalarSerializationNode scalar => GetScalarSize(scalar.value), + BinarySerializationNode binary => checked(sizeof(int) + binary.value.Length), + ObjectSerializationNode value => GetObjectSize(value), + ArraySerializationNode value => GetArraySize(value), + MapSerializationNode value => GetMapSize(value), + _ => throw new InvalidDataException($"Unsupported serialization node '{node.GetType().FullName}'.") + }; + return checked(sizeof(byte) + payload); + } + + private static int GetScalarSize(object value) => value switch + { + bool or byte or sbyte => 1, + short or ushort => 2, + int or uint or float => 4, + long or ulong or double => 8, + decimal or Guid => 16, + string text => GetEncodedStringSize(text), + _ => throw new InvalidDataException($"Unsupported scalar runtime type '{value.GetType().FullName}'.") + }; + + private static int GetEncodedStringSize(string value) + { + int bytes = Encoding.UTF8.GetByteCount(value); + int prefix = 1 + BitOperations.Log2((uint)bytes) / 7; + return checked(prefix + bytes); + } + + private static int GetObjectSize(ObjectSerializationNode node) + { + int size = sizeof(int); + foreach (KeyValuePair member in node.values) + size = checked(size + GetEncodedStringSize(member.Key) + GetEncodedSize(member.Value)); + return size; + } + + private static int GetArraySize(ArraySerializationNode node) + { + int size = sizeof(int); + foreach (SerializationNode value in node.values) + size = checked(size + GetEncodedSize(value)); + return size; + } + + private static int GetMapSize(MapSerializationNode node) + { + int size = sizeof(int); + foreach (KeyValuePair entry in node.values) + size = checked(size + GetEncodedSize(entry.Key) + GetEncodedSize(entry.Value)); + return size; + } +} diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs index 1613f5d32..d07a0f003 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs @@ -5,20 +5,13 @@ namespace Inno.Core.Serialization; -internal static class BinarySerializationFormat +internal static partial class BinarySerializationFormat { private const string C_MAGIC = "INNO-BINARY-CURRENT"; private const int C_MAX_COLLECTION_COUNT = 16_777_216; internal static byte[] Encode(SerializationNode root) - { - using var stream = new MemoryStream(); - using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); - writer.Write(C_MAGIC); - WriteNode(writer, root); - writer.Flush(); - return stream.ToArray(); - } + => Encode(root, includeMagic: true); internal static SerializationNode Decode(ReadOnlySpan bytes) { @@ -235,12 +228,23 @@ string name ) => path == "$" ? $"$.{name}" : $"{path}.{name}"; private static byte[] EncodeNode(SerializationNode node) - { - using var stream = new MemoryStream(); + => Encode(node, includeMagic: false); + + private static byte[] Encode( + SerializationNode node, + bool includeMagic + ) { + int length = checked(GetEncodedSize(node) + (includeMagic ? GetEncodedStringSize(C_MAGIC) : 0)); + var data = new byte[length]; + using var stream = new MemoryStream(data, writable: true); using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); + if (includeMagic) + writer.Write(C_MAGIC); WriteNode(writer, node); writer.Flush(); - return stream.ToArray(); + if (stream.Position != length) + throw new InvalidDataException("The serialization encoder did not fill its calculated output size."); + return data; } private static int CompareBytes( diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs b/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs index f05e9fafe..0bf3ae028 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -242,19 +243,6 @@ internal ConverterRegistryLease Capture() } } - internal ConverterInvoker? Resolve(Type valueType) - { - ConverterRegistrySnapshot snapshot = current; - lock (snapshot.sync) - { - if (snapshot.cache.TryGetValue(valueType, out ConverterInvoker? cached)) - return cached; - ConverterInvoker? resolved = ResolveSnapshot(snapshot, valueType); - snapshot.cache.Add(valueType, resolved); - return resolved; - } - } - /// /// Builds a validated result from the current immutable input snapshot. /// @@ -303,8 +291,8 @@ TypeCacheSnapshot types ) { private TypeCacheSnapshot? m_types = types; - internal readonly object sync = new(); - internal readonly Dictionary cache = []; + internal readonly Lock sync = new(); + internal readonly ConcurrentDictionary cache = new(); internal readonly Dictionary converterInstances = []; internal IReadOnlyList registrations = registrations; private int m_referenceCount = 1; @@ -385,12 +373,15 @@ internal ConverterRegistryLease(ConverterRegistry.ConverterRegistrySnapshot snap ConverterRegistry.ConverterRegistrySnapshot snapshot = m_snapshot ?? throw new ObjectDisposedException(nameof(ConverterRegistryLease)); Type normalizedType = Nullable.GetUnderlyingType(valueType) ?? valueType; + if (snapshot.cache.TryGetValue(normalizedType, out ConverterInvoker? cached)) + return cached; lock (snapshot.sync) { - if (snapshot.cache.TryGetValue(normalizedType, out ConverterInvoker? cached)) + if (snapshot.cache.TryGetValue(normalizedType, out cached)) return cached; ConverterInvoker? resolved = ConverterRegistry.ResolveSnapshot(snapshot, normalizedType); - snapshot.cache.Add(normalizedType, resolved); + if (!snapshot.cache.TryAdd(normalizedType, resolved)) + throw new InvalidOperationException("The serialization converter resolution was published twice."); return resolved; } } diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Concurrency.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Concurrency.cs new file mode 100644 index 000000000..520497b77 --- /dev/null +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Concurrency.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Inno.Core.Serialization.Converters; +using Xunit; + +namespace Inno.Core.Serialization.Tests; + +public sealed partial class SerializationBehaviorTests +{ + [Fact] + public void ConcurrentCapturedOperationsPublishOneConverterAndRetireItAfterTheLastLease() + { + ConcurrentCaptureConverter.constructions = 0; + ConcurrentCaptureConverter.disposals = 0; + using SerializationGeneration generation = m_serialization.CaptureGeneration(); + var scalar = new DefaultSample { count = 73, name = "Pinned" }; + byte[] expected = generation.Serialize(scalar); + + Parallel.For(0, 256, new ParallelOptions { MaxDegreeOfParallelism = 8 }, index => + { + var value = new ConcurrentCaptureValue(index); + byte[] bytes = generation.Serialize(value); + Assert.Equal(index, generation.Deserialize(bytes).count); + Assert.Equal(expected, generation.Serialize(scalar)); + }); + + Assert.Equal(1, ConcurrentCaptureConverter.constructions); + m_serialization.Dispose(); + Assert.Equal(0, ConcurrentCaptureConverter.disposals); + Assert.Equal(expected, generation.Serialize(scalar)); + generation.Dispose(); + Assert.Equal(1, ConcurrentCaptureConverter.disposals); + Assert.Throws(() => generation.Serialize(scalar)); + } +} + +internal sealed class ConcurrentCaptureValue(int count) : ISerializable +{ + internal int count { get; } = count; +} + +internal sealed class ConcurrentCaptureConverter : SerializationConverter, IDisposable +{ + internal static int constructions; + internal static int disposals; + + /// + /// Records construction of the converter owned by the captured generation. + /// + public ConcurrentCaptureConverter() => Interlocked.Increment(ref constructions); + + /// + public override void Write( + SerializationWriter writer, + ConcurrentCaptureValue value + ) => writer.Write("count", value.count); + + /// + public override ConcurrentCaptureValue Read(SerializationReader reader) => new(reader.Read("count")); + + /// + public void Dispose() => Interlocked.Increment(ref disposals); +} diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs new file mode 100644 index 000000000..187352102 --- /dev/null +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Text; +using Xunit; + +namespace Inno.Core.Serialization.Tests; + +public sealed partial class SerializationBehaviorTests +{ + [Fact] + public void EncodedScalarBytesMatchTheBinaryWriterContract() + { + object?[] values = + [ + null, false, true, byte.MaxValue, sbyte.MinValue, short.MinValue, ushort.MaxValue, + int.MinValue, uint.MaxValue, long.MinValue, ulong.MaxValue, + float.NegativeInfinity, float.PositiveInfinity, float.NaN, -0f, float.Epsilon, + double.NegativeInfinity, double.PositiveInfinity, double.NaN, -0d, double.Epsilon, + decimal.MinValue, decimal.MaxValue, new decimal(0, 0, 0, true, 28), + string.Empty, "English / 中文 / 🧪", Guid.Empty, Guid.Parse("62ce926a-e339-493d-bb74-743ed44ef37a") + ]; + foreach (object? value in values) + Assert.Equal(EncodeWithBinaryWriter(value), m_serialization.Encode(writer => writer.Write("value", value, value?.GetType() ?? typeof(string)))); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(63)] + [InlineData(127)] + [InlineData(128)] + [InlineData(16_383)] + [InlineData(16_384)] + public void EncodedStringsKeepUtf8AndSevenBitLengthSemantics(int length) + { + string[] values = + [ + new string('a', length), new string('中', length), + new string((char)0xD800, length), "🧪" + new string('a', length) + ]; + foreach (string value in values) + Assert.Equal(EncodeWithBinaryWriter(value), m_serialization.Encode(writer => writer.Write("value", value))); + } + + [Fact] + public void LargeBinaryEncodingAllocatesOnlyTheOwnedCaptureAndResult() + { + var source = new byte[1_000_000]; + source[0] = 73; + source[^1] = 19; + for (int index = 0; index < 4; index++) + _ = m_serialization.Encode(writer => writer.Write("value", source)); + long before = GC.GetAllocatedBytesForCurrentThread(); + byte[] data = m_serialization.Encode(writer => writer.Write("value", source)); + long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + Assert.InRange(allocated, source.Length * 2, source.Length * 2 + 8192); + source[0] = 0; + byte[] restored = m_serialization.Decode(data, reader => reader.Read("value")); + Assert.Equal(73, restored[0]); + Assert.Equal(19, restored[^1]); + } + + private static byte[] EncodeWithBinaryWriter(object? value) + { + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); + writer.Write("INNO-BINARY-CURRENT"); + writer.Write((byte)20); + writer.Write(1); + writer.Write("value"); + switch (value) + { + case null: + writer.Write((byte)0); + break; + case bool scalar: + writer.Write((byte)1); + writer.Write(scalar); + break; + case byte scalar: + writer.Write((byte)2); + writer.Write(scalar); + break; + case sbyte scalar: + writer.Write((byte)3); + writer.Write(scalar); + break; + case short scalar: + writer.Write((byte)4); + writer.Write(scalar); + break; + case ushort scalar: + writer.Write((byte)5); + writer.Write(scalar); + break; + case int scalar: + writer.Write((byte)6); + writer.Write(scalar); + break; + case uint scalar: + writer.Write((byte)7); + writer.Write(scalar); + break; + case long scalar: + writer.Write((byte)8); + writer.Write(scalar); + break; + case ulong scalar: + writer.Write((byte)9); + writer.Write(scalar); + break; + case float scalar: + writer.Write((byte)10); + writer.Write(scalar); + break; + case double scalar: + writer.Write((byte)11); + writer.Write(scalar); + break; + case decimal scalar: + writer.Write((byte)12); + writer.Write(scalar); + break; + case string scalar: + writer.Write((byte)13); + writer.Write(scalar); + break; + case Guid scalar: + writer.Write((byte)14); + writer.Write(scalar.ToByteArray()); + break; + default: throw new InvalidOperationException("The independent byte oracle received an unsupported scalar."); + } + writer.Flush(); + return stream.ToArray(); + } +} From e2e6ca32f7aa07d9cfab230edcf1d2ad396e92fe Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 19:12:39 +0800 Subject: [PATCH 24/54] fix(editor): balance Shader settings scopes and presentation source contracts Release Shader output, variant, source, implementation and technique row IDs through local finally blocks, including removal and early exit. Balance the capability tree on failure. Consolidate duplicate facade aliases in 21 files and remove three stale XML summaries. Enforce the confirmed source patterns through the existing architecture command with eight additional positive and negative fixtures. --- AGENTS.md | 1 + .../MathematicsPropertyDrawers.cs | 6 +- .../PropertyDrawing/NumericPropertyDrawer.cs | 3 +- .../GamePresentationSetting.cs | 3 - .../GameViewBackgroundSetting.cs | 3 +- .../GameViewPanel.cs | 12 +- .../Layers/GameLayersSetting.cs | 19 +- .../Presentation/InspectorCardControls.cs | 7 +- .../Presentation/InspectorTargetHeader.cs | 10 +- .../Targets/GameTagsSetting.cs | 11 +- .../Targets/TransformInspectionDrawer.cs | 3 +- .../SceneViewBackgroundSetting.cs | 3 +- .../SceneViewPanel.cs | 28 +- .../Presentation/BuildSettingsPresentation.cs | 3 +- .../Presentation/SettingsPageView.cs | 20 +- .../Canvas/ShaderEditorCanvas.Controls.cs | 65 +++-- .../Canvas/ShaderEditorCanvas.Parameters.cs | 21 +- .../Canvas/ShaderEditorCanvas.PassSettings.cs | 240 +++++++++--------- .../ShaderEditorCanvas.SourceSettings.cs | 173 +++++++------ .../Canvas/ShaderEditorCanvas.Techniques.cs | 218 ++++++++-------- .../Exporting/GameExportModal.cs | 6 +- .../Exporting/PluginExportModal.cs | 6 +- .../Settings/IconSettings.cs | 5 +- .../ArchitectureCommandTests.cs | 8 + .../CSharpStyleValidator.cs | 1 + .../EditorPresentationSourceValidator.cs | 66 +++++ 26 files changed, 505 insertions(+), 436 deletions(-) create mode 100644 tools/Inno.Tooling.Architecture/EditorPresentationSourceValidator.cs diff --git a/AGENTS.md b/AGENTS.md index a6e95d04f..8eeefd70f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -192,6 +192,7 @@ - UI 与插件事件的订阅、分发及生命周期统一复用 `Inno.Core.Events`。领域可以定义自己的事件类型及适配入口,但不能另建队列、监听器注册表或事件总线来取代核心事件管理。 ## 23. 源码可读性与排版 +- 同一文件针对同一类型只保留一个语义清楚的别名,避免多个 facade 别名交替表达同一调用。XML 文档必须对应当前真实成员,一个成员只保留一个有意义的 summary,不保留已删除成员的说明。 - 可读性是所有手写源码的持续要求,覆盖 InnoEngine、Canvas 与 Rendering2D;生成的 Bindings 和第三方 `extern` 源码不纳入手工排版。改进排版时不得改变执行顺序、初始化顺序、公开契约、序列化数据、诊断或用户体验。 - 优先让读者一眼看清声明和意图:字段、属性、无参数或单参数函数、简单表达式在合理行宽内能完整写成一行时,保持一行;不得仅因机械行宽规则把名称、箭头表达式或成员修饰符随意拆散。 - 所有参数超过一个的函数声明(包括方法、构造函数、局部函数、委托、运算符、主构造函数和 Lambda)必须在 `(` 后换行,每个参数独占一行,最后一个参数后换行,并让 `)` 与声明起始行对齐。紧随参数列表的函数体左花括号与 `)` 同行,写成 `) {`;表达式体、约束和初始化器则在 `)` 后接其语法所需内容。无参数及单参数函数不强制展开。新增和修改源码均执行此规则;自动格式化不能覆盖该规则。 diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs index 4944f06d2..8a3b1b51e 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/MathematicsPropertyDrawers.cs @@ -1,10 +1,8 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using Inno.Core.Mathematics; using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; using NumericsVector4 = System.Numerics.Vector4; @@ -13,7 +11,7 @@ internal static unsafe class ComponentFieldDrawer { internal static float GetWidth(int count) { - float spacing = UiDrawing.GetLayout().itemSpacing.X; + float spacing = Ui.GetLayout().itemSpacing.X; return MathF.Max(1f, (Ui.GetContentRegionAvailable().X - spacing * (count - 1)) / count); } @@ -250,7 +248,7 @@ public void Draw(PropertyDrawContext context) Color value = context.GetValue() is Color current ? current : default; var nativeValue = new NumericsVector4(value.r, value.g, value.b, value.a); var flags = context.hdrColor ? Inno.Editor.ImGui.ImGuiColorEditFlags.Hdr | Inno.Editor.ImGui.ImGuiColorEditFlags.Float | Inno.Editor.ImGui.ImGuiColorEditFlags.InputRgb | Inno.Editor.ImGui.ImGuiColorEditFlags.DisplayRgb : Inno.Editor.ImGui.ImGuiColorEditFlags.None; - if (EditorImGui.ColorEditLinear4($"##{context.path}", ref nativeValue, flags)) + if (Ui.ColorEditLinear4($"##{context.path}", ref nativeValue, flags)) { context.SetValue(new Color(nativeValue.X, nativeValue.Y, nativeValue.Z, nativeValue.W)); } diff --git a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs index 7232b5132..c2feafdc9 100644 --- a/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs +++ b/src/composition/editor/framework/Inno.Editor.Inspection/PropertyDrawing/NumericPropertyDrawer.cs @@ -2,7 +2,6 @@ using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Globalization; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Inspection; @@ -81,7 +80,7 @@ public void Draw(PropertyDrawContext context) _ = context.TryGetTextState(C_TEXT_STATE, out string? text); text ??= Convert.ToString(rawValue, CultureInfo.InvariantCulture) ?? "0"; - if (EditorImGui.InputText($"##{context.path}", ref text, C_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue)) + if (Ui.InputText($"##{context.path}", ref text, C_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue)) { if (TryConvert(text, type, out object? converted)) { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs index 4b65ea47e..ba83d4480 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GamePresentationSetting.cs @@ -8,9 +8,6 @@ namespace Inno.Editor.Panel.GameView; [ProjectSettingPath("Project/Player/Presentation")] internal sealed class GamePresentationSetting : ProjectSettingEditor { - /// - /// Gets the stable project-setting identity edited by this presentation. - /// /// /// Gets the section that groups Player presentation controls. /// diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewBackgroundSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewBackgroundSetting.cs index 35051025a..6a51af7ee 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewBackgroundSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewBackgroundSetting.cs @@ -1,7 +1,6 @@ using System.Numerics; using Inno.Editor.ImGui; using Inno.Editor.Settings; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.GameView; @@ -33,7 +32,7 @@ protected override void OnDraw(EditorSettingObject setting) { Vector4 value = ReadVector(setting); Ui.SetNextItemWidth(-1f); - if (EditorImGui.ColorEditLinear4("##game_view_background", ref value)) + if (Ui.ColorEditLinear4("##game_view_background", ref value)) setting.SetAsSingleArray("value", [value.X, value.Y, value.Z, value.W]); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs index 4f262ac0d..00544e39b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.GameView/GameViewPanel.cs @@ -1,6 +1,4 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Inno.References; using System; using System.Collections.Generic; @@ -81,7 +79,7 @@ protected override void OnDraw(EditorContext context) if (available.X <= 0f || available.Y <= 0f) return; RefreshPresentationSettings(); - float framebufferScale = UiDrawing.GetInput().framebufferScale.X; + float framebufferScale = Ui.GetInput().framebufferScale.X; if (!float.IsFinite(framebufferScale) || framebufferScale <= 0f) framebufferScale = 1f; GameViewportLayout layout = CalculateLayout(available, m_presentation, framebufferScale); @@ -94,7 +92,7 @@ protected override void OnDraw(EditorContext context) Vector2 mouse = Ui.GetMousePos(); Vector2 local = mouse - outputOrigin; bool inside = local.X >= 0f && local.Y >= 0f && local.X < layout.size.X && local.Y < layout.size.Y; - var viewport = UiDrawing.GetViewport(); + var viewport = Ui.GetViewport(); bool gameViewFocused = m_playMode.isPlaying && Ui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows); bool imageHovered = inside && Ui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows); bool focused = m_inputCapture.Report(viewport.id, outputOrigin - viewport.position, layout.size, gameViewFocused, imageHovered); @@ -110,7 +108,7 @@ protected override void OnDraw(EditorContext context) bool leftReleased = m_leftPointerActive && Ui.IsMouseReleased(ImGuiMouseButton.Left); if (leftReleased || !focused) m_leftPointerActive = false; - var io = UiDrawing.GetInput(); + var io = Ui.GetInput(); KeyModifier modifiers = KeyModifier.None; if (io.alt) modifiers |= KeyModifier.Alt; @@ -149,7 +147,7 @@ protected override void OnDraw(EditorContext context) Vector2 origin = Ui.GetCursorScreenPosition(); if (m_presentation.preserveAspectRatio) { - EditorImGui.DrawFilledRectangle(origin, origin + available, Ui.ToPackedColor(new Vector4(0f, 0f, 0f, EditorPalette.opacityOpaque)), 0); + Ui.DrawFilledRectangle(origin, origin + available, Ui.ToPackedColor(new Vector4(0f, 0f, 0f, EditorPalette.opacityOpaque)), 0); } Ui.SetCursorScreenPosition(origin + layout.offset); @@ -218,7 +216,7 @@ private void DrawUnavailable( string message ) { Vector2 minimum = Ui.GetCursorScreenPosition(); - EditorImGui.DrawFilledRectangle(minimum, minimum + size, Ui.ToPackedColor(m_backgroundColor), 0); + Ui.DrawFilledRectangle(minimum, minimum + size, Ui.ToPackedColor(m_backgroundColor), 0); Ui.PushStyleColor(ImGuiCol.Text, EditorPalette.textDisabled); try { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs index ae5d6a0b6..b4d38694c 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Layers/GameLayersSetting.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; @@ -10,7 +9,6 @@ using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; using Inno.Scene.Layers; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; @@ -22,9 +20,6 @@ internal sealed class GameLayersSetting : ProjectSettingEditor private string m_error = string.Empty; private string? []? m_observedNames; /// - /// Gets the stable project-setting identity used for discovery and persistence. - /// - /// /// Gets the presentation section that groups this setting. /// public override string section => "Definitions"; @@ -139,7 +134,7 @@ ref string value Ui.PushStyleColor(ImGuiCol.FrameBgActive, EditorPalette.transparent); try { - bool submitted = EditorImGui.InputTextWithHint(id, hint, ref value, C_LAYER_NAME_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); + bool submitted = Ui.InputTextWithHint(id, hint, ref value, C_LAYER_NAME_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); return submitted || Ui.IsItemDeactivatedAfterEdit(); } finally @@ -153,7 +148,7 @@ private void DrawLayerToolbar( string? [] names, int definedCount ) { - float spacing = UiDrawing.GetLayout().itemSpacing.X; + float spacing = Ui.GetLayout().itemSpacing.X; EditorUI.current.widgets.LabelChip("Defined", EditorPalette.collectionRowAlternate); Ui.SameLine(0f, 0f); DrawReadOnlyCount($"{definedCount} / {GameLayer.C_MAX_COUNT}", 78f * EditorUI.current.widgets.style.zoom); @@ -165,15 +160,15 @@ private static void DrawReadOnlyCount( string value, float width ) { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); Vector2 minimum = Ui.GetCursorScreenPosition(); Vector2 size = new(MathF.Max(1f, width), Ui.GetFrameHeight()); Ui.Dummy(size); Vector2 maximum = minimum + size; - EditorImGui.DrawFilledRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.FrameBg), style.frameRounding); - EditorImGui.DrawRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.Border), style.frameRounding, EditorUI.current.widgets.style.borderSize); + Ui.DrawFilledRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.FrameBg), style.frameRounding); + Ui.DrawRectangle(minimum, maximum, Ui.GetStyleColor(ImGuiCol.Border), style.frameRounding, EditorUI.current.widgets.style.borderSize); Vector2 textSize = Ui.MeasureText(value); - EditorImGui.DrawText(minimum + (size - textSize) * 0.5f, Ui.GetStyleColor(ImGuiCol.Text), value); + Ui.DrawText(minimum + (size - textSize) * 0.5f, Ui.GetStyleColor(ImGuiCol.Text), value); } private static void DrawLayerTableHeader() @@ -196,7 +191,7 @@ string label Ui.Text(label); } - private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().framePadding.X); + private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + Ui.GetLayout().framePadding.X); private void DrawAddLayer( GameLayerCatalog setting, string? [] names, diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs index bde4b828e..8a9721041 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorCardControls.cs @@ -1,5 +1,4 @@ using EditorUI = Inno.Editor.ImGui.EditorUiSession; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Numerics; using Inno.Core.Identity; @@ -214,11 +213,11 @@ bool dimmed padding.X * 2f + gripSize.X + EditorUI.current.widgets.style.headerControlSpacing + titleSize.X, MathF.Max(gripSize.Y, titleSize.Y) + padding.Y * 2f ); - EditorImGui.DrawFilledRectangle(origin, origin + size, Ui.ToPackedColor(EditorPalette.cardHeader), EditorUI.current.widgets.style.frameRounding); + Ui.DrawFilledRectangle(origin, origin + size, Ui.ToPackedColor(EditorPalette.cardHeader), EditorUI.current.widgets.style.frameRounding); uint color = Ui.ToPackedColor(dimmed ? EditorPalette.cardDisabledText : EditorPalette.text); Vector2 textOrigin = origin + padding; - EditorImGui.DrawText(textOrigin, color, ImGuiIcon.GripVertical); - EditorImGui.DrawText(new Vector2(textOrigin.X + gripSize.X + EditorUI.current.widgets.style.headerControlSpacing, textOrigin.Y), color, title); + Ui.DrawText(textOrigin, color, ImGuiIcon.GripVertical); + Ui.DrawText(new Vector2(textOrigin.X + gripSize.X + EditorUI.current.widgets.style.headerControlSpacing, textOrigin.Y), color, title); Ui.Dummy(size); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs index b78a37cbc..8a571c9a9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Presentation/InspectorTargetHeader.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; @@ -6,7 +5,6 @@ using Inno.Editor.ImGui; using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Interactions; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; @@ -112,13 +110,13 @@ private void DrawNameRow( float rowHeight ) { Vector2 lockSize = EditorUI.current.widgets.GetCompactIconSize(); - float itemSpacing = UiDrawing.GetLayout().itemSpacing.X; + float itemSpacing = Ui.GetLayout().itemSpacing.X; float nameWidth = MathF.Max(1f, Ui.GetContentRegionAvailable().X - lockSize.X - itemSpacing); (string name, Action? nameSetter) = drawer.BindName(context); if (nameSetter is not null) { Ui.SetNextItemWidth(nameWidth); - if (EditorImGui.InputText($"##inspector_target_name_{GetTargetId(context.target)}", ref name, C_NAME_BUFFER_SIZE, ImGuiInputTextFlags.None)) + if (Ui.InputText($"##inspector_target_name_{GetTargetId(context.target)}", ref name, C_NAME_BUFFER_SIZE, ImGuiInputTextFlags.None)) { nameSetter(name); } @@ -129,11 +127,11 @@ float rowHeight { Ui.AlignTextToFramePadding(); Vector2 textOrigin = Ui.GetCursorScreenPosition(); - EditorDrawList drawList = EditorImGui.GetDrawList(); + EditorDrawList drawList = Ui.GetDrawList(); drawList.PushClip(textOrigin, textOrigin + new Vector2(nameWidth, rowHeight)); try { - EditorImGui.DrawText(textOrigin, Ui.GetStyleColor(ImGuiCol.Text), name); + Ui.DrawText(textOrigin, Ui.GetStyleColor(ImGuiCol.Text), name); } finally { diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs index 176c85df5..79ed086e7 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/GameTagsSetting.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; @@ -8,7 +7,6 @@ using Inno.Editor.ImGui.ImGuiWidget; using Inno.Editor.Settings; using Inno.Scene; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Inspector; @@ -19,9 +17,6 @@ internal sealed class GameTagsSetting : ProjectSettingEditor private string m_error = string.Empty; private string m_newTag = string.Empty; /// - /// Gets the stable project-setting identity used for discovery and persistence. - /// - /// /// Gets the presentation section that groups this setting. /// public override string section => "Definitions"; @@ -47,10 +42,10 @@ protected override void OnDraw(GameTagCatalog setting) private void DrawAddRow(GameTagCatalog setting) { - float spacing = UiDrawing.GetLayout().itemSpacing.X; + float spacing = Ui.GetLayout().itemSpacing.X; float actionWidth = EditorUI.current.widgets.GetCompactIconSize().X; Ui.SetNextItemWidth(MathF.Max(1f, Ui.GetContentRegionAvailable().X - actionWidth - spacing)); - bool submit = EditorImGui.InputTextWithHint("##new_project_tag", "New tag", ref m_newTag, C_TAG_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); + bool submit = Ui.InputTextWithHint("##new_project_tag", "New tag", ref m_newTag, C_TAG_BUFFER_SIZE, ImGuiInputTextFlags.EnterReturnsTrue); Ui.SameLine(0f, spacing); submit |= EditorUI.current.widgets.ClickableIcon("add_project_tag", ImGuiIcon.Plus, "Add project tag"); if (!submit || string.IsNullOrWhiteSpace(m_newTag)) @@ -139,5 +134,5 @@ string label Ui.Text(label); } - private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().framePadding.X); + private static void InsetPlainCell() => Ui.SetCursorPosX(Ui.GetCursorPosX() + Ui.GetLayout().framePadding.X); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs index 543aad1fa..919a295e9 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Inspector/Targets/TransformInspectionDrawer.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using Inno.Core.Mathematics; @@ -146,7 +145,7 @@ Action apply ) { EditorUI.current.widgets.PropertyRow("transform." + label, label, () => { - float width = MathF.Max(1f, (Ui.GetContentRegionAvailable().X - UiDrawing.GetLayout().itemSpacing.X * 2f) / 3f); + float width = MathF.Max(1f, (Ui.GetContentRegionAvailable().X - Ui.GetLayout().itemSpacing.X * 2f) / 3f); bool changed = EditorUI.current.widgets.AxisDragFloat(label, "X", ref value.x, width); Ui.SameLine(0f, -1f); changed |= EditorUI.current.widgets.AxisDragFloat(label, "Y", ref value.y, width); diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs index b54711e94..a69226a30 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewBackgroundSetting.cs @@ -1,7 +1,6 @@ using System.Numerics; using Inno.Editor.ImGui; using Inno.Editor.Settings; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.SceneView; @@ -33,7 +32,7 @@ protected override void OnDraw(EditorSettingObject setting) { Vector4 value = ReadVector(setting); Ui.SetNextItemWidth(-1f); - if (EditorImGui.ColorEditLinear4("##scene_view_background", ref value)) + if (Ui.ColorEditLinear4("##scene_view_background", ref value)) setting.SetAsSingleArray("value", [value.X, value.Y, value.Z, value.W]); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs index 14a8fc5e4..8417a698b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.SceneView/SceneViewPanel.cs @@ -1,6 +1,4 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Inno.References; using Inno.Core.Identity; using System; @@ -125,7 +123,7 @@ protected override void OnDraw(EditorContext context) bool viewportClicked = Ui.IsItemClicked(ImGuiMouseButton.Left); toolbar = CreateManipulationToolbarLayout(minimum, maximum); toolbarHovered = IsManipulationToolbarHovered(toolbar); - EditorDrawList drawList = EditorImGui.GetDrawList(); + EditorDrawList drawList = Ui.GetDrawList(); IReadOnlyList<(Vector2 center, EditorGizmoIcon icon)> icons; bool toolbarOwnsPointer; bool gizmoOwnsPointer; @@ -145,7 +143,7 @@ protected override void OnDraw(EditorContext context) } bool iconOwnsPointer = SelectGizmoIcons(icons, viewportClicked && !toolbarOwnsPointer && !gizmoOwnsPointer && !navigationOwnsPointer); - if (!viewportClicked || toolbarOwnsPointer || gizmoOwnsPointer || iconOwnsPointer || navigationOwnsPointer || UiDrawing.GetInput().alt) + if (!viewportClicked || toolbarOwnsPointer || gizmoOwnsPointer || iconOwnsPointer || navigationOwnsPointer || Ui.GetInput().alt) return; Vector2 mouse = Ui.GetMousePos(); float x = (mouse.X - minimum.X) / Math.Max(1f, maximum.X - minimum.X); @@ -274,15 +272,15 @@ Vector2 maximum int height = Math.Max(1, (int)(maximum.Y - minimum.Y)); EditorGizmoFrame frame = m_rendering.CollectGizmos(C_VIEWPORT_ID, width, height); EngineMatrix worldToClip = space.projectionMatrix * space.viewMatrix; - EditorDrawList draw = EditorImGui.GetDrawList(); + EditorDrawList draw = Ui.GetDrawList(); uint white = Ui.ToPackedColor(Vector4.One); uint outline = Ui.ToPackedColor(new Vector4(0f, 0f, 0f, 0.75f)); foreach (EditorGizmoLine line in frame.lines) { if (!TryProject(line.start, out Vector2 start) || !TryProject(line.end, out Vector2 end)) continue; - EditorImGui.DrawLine(start + Vector2.One, end + Vector2.One, outline, 3.5f); - EditorImGui.DrawLine(start, end, white, 2.5f); + Ui.DrawLine(start + Vector2.One, end + Vector2.One, outline, 3.5f); + Ui.DrawLine(start, end, white, 2.5f); } float iconSize = 24f * EditorUI.current.widgets.style.zoom; @@ -294,7 +292,7 @@ Vector2 maximum displayIcons.Add((center, icon)); string glyph = GizmoIconGlyph(icon.iconId); float fontScale = iconSize / Ui.GetFontSize(); - Vector2 size = EditorImGui.MeasureText(glyph, fontScale); + Vector2 size = Ui.MeasureText(glyph, fontScale); Vector2 origin = center - size * 0.5f; draw.Text(origin + Vector2.One, outline, glyph, fontScale); draw.Text(origin, white, glyph, fontScale); @@ -387,7 +385,7 @@ Vector2 maximum if (!SupportsMode(profile, navigation.mode)) navigation.mode = profile.defaultMode; - EditorUiInput io = UiDrawing.GetInput(); + EditorUiInput io = Ui.GetInput(); if (m_navigationDrag != NavigationDrag.None && !Ui.IsMouseButtonDown(m_navigationDragButton)) { m_navigationDrag = NavigationDrag.None; @@ -631,8 +629,8 @@ string message ) { Vector2 minimum = Ui.GetCursorScreenPosition(); Vector2 maximum = minimum + size; - EditorDrawList drawList = EditorImGui.GetDrawList(); - EditorImGui.DrawFilledRectangle(minimum, maximum, Ui.ToPackedColor(m_backgroundColor), 0); + EditorDrawList drawList = Ui.GetDrawList(); + Ui.DrawFilledRectangle(minimum, maximum, Ui.ToPackedColor(m_backgroundColor), 0); Ui.PushStyleColor(ImGuiCol.Text, EditorPalette.textDisabled); try { @@ -647,10 +645,10 @@ string message private static Inno.Core.Mathematics.Color ToEngineColor(Vector4 value) => new(value.X, value.Y, value.Z, value.W); private bool DrawManipulationToolbar(ManipulationToolbarLayout layout) { - EditorDrawList drawList = EditorImGui.GetDrawList(); + EditorDrawList drawList = Ui.GetDrawList(); Vector4 background = EditorPalette.targetHeader; background.W = EditorPalette.opacityMedium; - EditorImGui.DrawFilledRectangle(layout.minimum, layout.maximum, Ui.ToPackedColor(background), EditorUI.current.widgets.style.frameRounding); + Ui.DrawFilledRectangle(layout.minimum, layout.maximum, Ui.ToPackedColor(background), EditorUI.current.widgets.style.frameRounding); float itemX = layout.minimum.X + layout.padding; float itemY = layout.minimum.Y + layout.padding; try @@ -674,7 +672,7 @@ private bool DrawManipulationToolbar(ManipulationToolbarLayout layout) itemY += layout.itemSize.Y + layout.sectionSpacing; float separatorY = itemY - layout.sectionSpacing * 0.5f; - EditorImGui.DrawLine( + Ui.DrawLine( new Vector2(layout.minimum.X + layout.padding, separatorY), new Vector2(layout.maximum.X - layout.padding, separatorY), Ui.ToPackedColor(Vector4.One), @@ -743,7 +741,7 @@ bool selected Vector2 minimum = Ui.GetItemMinimum(); Vector2 maximum = Ui.GetItemMaximum(); float thickness = EditorUI.current.widgets.style.interactionOverlayThickness; - EditorImGui.DrawFilledRectangle(minimum, new Vector2(minimum.X + thickness, maximum.Y), Ui.ToPackedColor(Vector4.One), 0); + Ui.DrawFilledRectangle(minimum, new Vector2(minimum.X + thickness, maximum.Y), Ui.ToPackedColor(Vector4.One), 0); } return pressed; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs index e633c2710..d5647db0a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/BuildSettingsPresentation.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Inno.Build; using Inno.Build.Managed; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Panel.Settings; @@ -168,7 +167,7 @@ private static bool DrawTextValue( Action apply ) { Ui.SetNextItemWidth(-1f); - if (!EditorImGui.InputText("##value", ref value, C_TEXT_CAPACITY)) + if (!Ui.InputText("##value", ref value, C_TEXT_CAPACITY)) return false; apply(value); return true; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs index 0954cb21e..bda719e53 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.Settings/Presentation/SettingsPageView.cs @@ -1,7 +1,5 @@ using System.Numerics; -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; -using EditorImGui = Inno.Editor.ImGui.ImGui; using System; using System.Collections.Generic; using System.Linq; @@ -140,7 +138,7 @@ int fieldIndex try { _ = Ui.TableSetColumnIndex(0); - Ui.SetCursorPosX(Ui.GetCursorPosX() + UiDrawing.GetLayout().windowPadding.X); + Ui.SetCursorPosX(Ui.GetCursorPosX() + Ui.GetLayout().windowPadding.X); EditorUI.current.widgets.PropertyLabel(setting.label, setting.description); _ = Ui.TableSetColumnIndex(1); bool groupStarted = false; @@ -172,8 +170,8 @@ int fieldIndex private static void DrawFieldBackgroundGutters(uint background) { - var table = UiDrawing.GetTableBounds(); - var window = UiDrawing.GetWindowBounds(); + var table = Ui.GetTableBounds(); + var window = Ui.GetWindowBounds(); float tableMinimumX = table.minimumX; float tableMaximumX = table.maximumX; float rowMinimumY = table.rowMinimum; @@ -182,18 +180,18 @@ private static void DrawFieldBackgroundGutters(uint background) float windowMaximumX = window.maximumX; if (rowMaximumY <= rowMinimumY) return; - EditorDrawList drawList = EditorImGui.GetDrawList(); + EditorDrawList drawList = Ui.GetDrawList(); drawList.PushClip(new Vector2(windowMinimumX, rowMinimumY), new Vector2(windowMaximumX, rowMaximumY), false); try { if (tableMinimumX > windowMinimumX) { - EditorImGui.DrawFilledRectangle(new System.Numerics.Vector2(windowMinimumX, rowMinimumY), new System.Numerics.Vector2(tableMinimumX, rowMaximumY), background, 0); + Ui.DrawFilledRectangle(new System.Numerics.Vector2(windowMinimumX, rowMinimumY), new System.Numerics.Vector2(tableMinimumX, rowMaximumY), background, 0); } if (tableMaximumX < windowMaximumX) { - EditorImGui.DrawFilledRectangle(new System.Numerics.Vector2(tableMaximumX, rowMinimumY), new System.Numerics.Vector2(windowMaximumX, rowMaximumY), background, 0); + Ui.DrawFilledRectangle(new System.Numerics.Vector2(tableMaximumX, rowMinimumY), new System.Numerics.Vector2(windowMaximumX, rowMaximumY), background, 0); } } finally @@ -204,13 +202,13 @@ private static void DrawFieldBackgroundGutters(uint background) private static float GetResetColumnWidth() { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); return style.itemSpacing.X + Ui.MeasureText("Reset").X + style.framePadding.X * 2f + style.windowPadding.X; } private static bool DrawResetButton(bool enabled) { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); Ui.SetCursorPosX(Ui.GetCursorPosX() + style.itemSpacing.X); Ui.BeginDisabled(!enabled); try @@ -223,5 +221,5 @@ private static bool DrawResetButton(bool enabled) } } - private static void PushDisabledText() => Ui.PushStyleColor(ImGuiCol.Text, UiDrawing.GetStyleColorValue(ImGuiCol.TextDisabled)); + private static void PushDisabledText() => Ui.PushStyleColor(ImGuiCol.Text, Ui.GetStyleColorValue(ImGuiCol.TextDisabled)); } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs index 2c55a7877..e57e0c728 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Controls.cs @@ -9,7 +9,6 @@ using Inno.Rendering.Assets; using Inno.Rendering.Shaders; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; @@ -54,7 +53,7 @@ private void Controls(GraphNodeRecord node) int index = Read(node, "index", 0); InspectorRow("extract.component", "Component", () => { - if (ImGuiApi.InputInt("##component", ref index, 1, 100, 0) && index >= 0) + if (EditorImGui.InputInt("##component", ref index, 1, 100, 0) && index >= 0) Set(node, "index", index); }); break; @@ -63,7 +62,7 @@ private void Controls(GraphNodeRecord node) bool level = Read(node, "explicitLevel", false); InspectorRow("sample.explicit-level", "Explicit LOD", () => { - if (ImGuiApi.Checkbox("##explicit-level", ref level)) + if (EditorImGui.Checkbox("##explicit-level", ref level)) Set(node, "explicitLevel", level); }); break; @@ -95,8 +94,8 @@ private void Controls(GraphNodeRecord node) ); InspectorRow("storage.contract", "Contract", () => { - if (ImGuiApi.Button("Storage Contract…")) - ImGuiApi.OpenPopup("##storage"); + if (EditorImGui.Button("Storage Contract…")) + EditorImGui.OpenPopup("##storage"); }); if (StoragePopup(ref resource)) Set(node, "resource", resource); @@ -141,7 +140,7 @@ private void GraphCall(GraphNodeRecord node) continue; } - if (!ImGuiApi.Selectable(candidate.displayName + " · " + entry.assetPath.localPath, id == selected)) + if (!EditorImGui.Selectable(candidate.displayName + " · " + entry.assetPath.localPath, id == selected)) continue; using var transaction = owner.interactions.history.BeginTransaction("Assign Graph Node"); Set(node, "sourceId", id); @@ -216,7 +215,7 @@ private void GraphInterfaceMetadata() int createOrder = settings.createOrder; InspectorRow("node-interface.order", "Order", () => { - bool changed = ImGuiApi.InputInt("##order", ref createOrder, 1, 100, 0); + bool changed = EditorImGui.InputInt("##order", ref createOrder, 1, 100, 0); Gesture(); if (changed) { @@ -231,7 +230,7 @@ private void GraphInterfaceMetadata() try { foreach (ShaderGraphNodeKind kind in Enum.GetValues()) - if (ImGuiApi.Selectable(kind.ToString(), settings.kind == kind)) + if (EditorImGui.Selectable(kind.ToString(), settings.kind == kind)) { settings.kind = kind; SetGraphNodeSettings(settings); @@ -251,7 +250,7 @@ private void GraphInterfaceMetadata() try { foreach (ShaderGraphNodeEffect effect in Enum.GetValues()) - if (ImGuiApi.Selectable(effect.ToString(), settings.effect == effect)) + if (EditorImGui.Selectable(effect.ToString(), settings.effect == effect)) { settings.effect = effect; SetGraphNodeSettings(settings); @@ -296,10 +295,10 @@ private void GraphPorts( bool inputs, Action apply ) { - ImGuiApi.SeparatorText(inputs ? "Inputs" : "Outputs"); + EditorImGui.SeparatorText(inputs ? "Inputs" : "Outputs"); for (int index = 0; index < ports.Length; index++) { - ImGuiApi.PushId(index); + EditorImGui.PushId(index); try { ShaderGraphNodePortDefinition port = ports[index]; @@ -350,7 +349,7 @@ Action apply } ) - if (ImGuiApi.Selectable(type, port.type.id == type)) + if (EditorImGui.Selectable(type, port.type.id == type)) { ShaderGraphNodePortDefinition[] changedPorts = ports.ToArray(); changedPorts[index] = new() @@ -375,7 +374,7 @@ Action apply bool required = port.required; InspectorRow("node-port.required", "Required", () => { - if (!ImGuiApi.Checkbox("##required", ref required)) + if (!EditorImGui.Checkbox("##required", ref required)) return; ShaderGraphNodePortDefinition[] changedPorts = ports.ToArray(); changedPorts[index] = new() @@ -390,7 +389,7 @@ Action apply InspectorRow("node-port.remove", "", () => { - if (ImGuiApi.SmallButton("Remove")) + if (EditorImGui.SmallButton("Remove")) apply(ports.Where(( _, item @@ -399,7 +398,7 @@ Action apply } finally { - ImGuiApi.PopId(); + EditorImGui.PopId(); } } @@ -433,7 +432,7 @@ private void Scalar(GraphNodeRecord node) } ) - if (ImGuiApi.Selectable(candidate, type == candidate)) + if (EditorImGui.Selectable(candidate, type == candidate)) { using var transaction = owner.interactions.history.BeginTransaction("Change Constant Type"); Set(node, "type", candidate); @@ -478,7 +477,7 @@ private void Scalar(GraphNodeRecord node) bool boolean = Read(node, "value", false); InspectorRow("constant.value", "Value", () => { - if (ImGuiApi.Checkbox("##value", ref boolean)) + if (EditorImGui.Checkbox("##value", ref boolean)) Set(node, "value", boolean); }); break; @@ -486,7 +485,7 @@ private void Scalar(GraphNodeRecord node) int integer = Read(node, "value", 0); InspectorRow("constant.value", "Value", () => { - bool integerChanged = ImGuiApi.InputInt("##value", ref integer, 1, 100, 0); + bool integerChanged = EditorImGui.InputInt("##value", ref integer, 1, 100, 0); Gesture(); if (integerChanged) Set(node, "value", integer, true); @@ -517,7 +516,7 @@ private void Source(GraphNodeRecord node) try { foreach (AssetFileEntry entry in owner.assets.GetFileSystemEntries(includeDirectories: false).Where(static entry => entry.assetPath.localPath.EndsWith(".ishadersource", StringComparison.OrdinalIgnoreCase))) - if (ImGuiApi.Selectable(entry.assetPath.ToString(), owner.AssetId(entry) == selected)) + if (EditorImGui.Selectable(entry.assetPath.ToString(), owner.AssetId(entry) == selected)) { using var transaction = owner.interactions.history.BeginTransaction("Assign Shader Function"); Set(node, "sourceId", owner.AssetId(entry)); @@ -546,7 +545,7 @@ private void Source(GraphNodeRecord node) try { foreach (string function in library.exports) - if (ImGuiApi.Selectable(function, function == selectedFunction)) + if (EditorImGui.Selectable(function, function == selectedFunction)) Set(node, "function", function); } finally @@ -582,7 +581,7 @@ private void Input(GraphNodeRecord node) try { foreach (ShaderIrInputKind kind in Enum.GetValues()) - if (ImGuiApi.Selectable(kind.ToString(), input.kind == kind)) + if (EditorImGui.Selectable(kind.ToString(), input.kind == kind)) { input.kind = kind; if (kind == ShaderIrInputKind.Storage) @@ -632,8 +631,8 @@ private void Input(GraphNodeRecord node) { InspectorRow("input.storage", "Contract", () => { - if (ImGuiApi.Button("Storage Contract…")) - ImGuiApi.OpenPopup("##storage"); + if (EditorImGui.Button("Storage Contract…")) + EditorImGui.OpenPopup("##storage"); }); ShaderGraphType resource = input.type; if (StoragePopup(ref resource)) @@ -673,7 +672,7 @@ private void Input(GraphNodeRecord node) } ) - if (ImGuiApi.Selectable(candidate, type == candidate)) + if (EditorImGui.Selectable(candidate, type == candidate)) { input.type = new() { @@ -700,7 +699,7 @@ private void Input(GraphNodeRecord node) EditorUI.current.widgets.DrawItemTooltip("Stage semantic, not a native expression. Builtins must be supported by the selected stage and target; compute invocation IDs use uint3."); int location = input.location; bool locationChanged = false; - InspectorRow("input.location", "Location", () => locationChanged = ImGuiApi.InputInt("##location", ref location, 1, 100, 0)); + InspectorRow("input.location", "Location", () => locationChanged = EditorImGui.InputInt("##location", ref location, 1, 100, 0)); Gesture(); if (locationChanged && location >= 0) { @@ -718,7 +717,7 @@ private void Input(GraphNodeRecord node) bool expanded = draft.expandedPreviews.Contains(node.id); InspectorRow("input.preview", "Preview", () => { - if (ImGuiApi.Checkbox("##preview", ref expanded)) + if (EditorImGui.Checkbox("##preview", ref expanded)) { if (expanded) draft.expandedPreviews.Add(node.id); @@ -758,8 +757,8 @@ private void Output(GraphNodeRecord node) ); InspectorRow("output.settings", "Settings", () => { - if (ImGuiApi.Button("Stage & Pass Settings…")) - ImGuiApi.OpenPopup("##stage-settings"); + if (EditorImGui.Button("Stage & Pass Settings…")) + EditorImGui.OpenPopup("##stage-settings"); }); StageSettingsPopup(node, stage); if (stage.stage == ShaderStage.Compute) @@ -767,7 +766,7 @@ private void Output(GraphNodeRecord node) int x = stage.threadsX, y = stage.threadsY, z = stage.threadsZ; InspectorRow("output.threads-x", "Threads X", () => { - if (ImGuiApi.InputInt("##threads-x", ref x, 1, 100, 0) && x > 0) + if (EditorImGui.InputInt("##threads-x", ref x, 1, 100, 0) && x > 0) { stage.threadsX = x; Set(node, "settings", stage); @@ -775,7 +774,7 @@ private void Output(GraphNodeRecord node) }); InspectorRow("output.threads-y", "Threads Y", () => { - if (ImGuiApi.InputInt("##threads-y", ref y, 1, 100, 0) && y > 0) + if (EditorImGui.InputInt("##threads-y", ref y, 1, 100, 0) && y > 0) { stage.threadsY = y; Set(node, "settings", stage); @@ -783,7 +782,7 @@ private void Output(GraphNodeRecord node) }); InspectorRow("output.threads-z", "Threads Z", () => { - if (ImGuiApi.InputInt("##threads-z", ref z, 1, 100, 0) && z > 0) + if (EditorImGui.InputInt("##threads-z", ref z, 1, 100, 0) && z > 0) { stage.threadsZ = z; Set(node, "settings", stage); @@ -806,7 +805,7 @@ string[] values try { foreach (string candidate in values) - if (ImGuiApi.Selectable(candidate, current == candidate)) + if (EditorImGui.Selectable(candidate, current == candidate)) Set(node, key, candidate); } finally @@ -818,7 +817,7 @@ string[] values private void Gesture() { - if (ImGuiApi.IsItemActivated()) + if (EditorImGui.IsItemActivated()) draft.valueGesture = Guid.NewGuid().ToString("N"); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs index 169050c8d..566435989 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Parameters.cs @@ -8,7 +8,6 @@ using Inno.Editor.Shaders; using Inno.Editor.Shaders.ImGui; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; @@ -16,15 +15,15 @@ internal sealed partial class ShaderEditorCanvas { private bool StoragePopup(ref ShaderGraphType type) { - ImGuiApi.SetNextWindowSize(new(490, 390), (ImGuiCond.Appearing)); - if (!ImGuiApi.BeginPopup("##storage")) + EditorImGui.SetNextWindowSize(new(490, 390), (ImGuiCond.Appearing)); + if (!EditorImGui.BeginPopup("##storage")) return false; bool changed = false; try { bool image = type.isImage; bool imageChanged = false; - InspectorRow("storage.image", "Storage Image", () => imageChanged = ImGuiApi.Checkbox("##image", ref image)); + InspectorRow("storage.image", "Storage Image", () => imageChanged = EditorImGui.Checkbox("##image", ref image)); if (imageChanged) { type.isImage = image; @@ -62,7 +61,7 @@ private bool StoragePopup(ref ShaderGraphType type) bool array = type.isArray; bool arrayChanged = false; - InspectorRow("storage.array", "Array Layers", () => arrayChanged = ImGuiApi.Checkbox("##array", ref array)); + InspectorRow("storage.array", "Array Layers", () => arrayChanged = EditorImGui.Checkbox("##array", ref array)); if (arrayChanged) { type.isArray = array; @@ -96,7 +95,7 @@ private bool StoragePopup(ref ShaderGraphType type) } ) - if (ImGuiApi.Selectable(candidate, current == candidate)) + if (EditorImGui.Selectable(candidate, current == candidate)) selected = candidate; } finally @@ -118,7 +117,7 @@ private bool StoragePopup(ref ShaderGraphType type) } finally { - ImGuiApi.EndPopup(); + EditorImGui.EndPopup(); } return changed; @@ -165,7 +164,7 @@ private void DrawParameter(ShaderGraphInputSettings input) bool color = property.type == ShaderPropertyType.Color; InspectorRow("parameter.color", "Color", () => { - if (ImGuiApi.Checkbox("##color", ref color)) + if (EditorImGui.Checkbox("##color", ref color)) { property.type = color ? ShaderPropertyType.Color : ShaderPropertyType.Vector4; MaterialValue converted = property.defaultValue; @@ -220,7 +219,7 @@ private void DrawParameter(ShaderGraphInputSettings input) bool visible = presentation.visible; InspectorRow("parameter.visible", "Visible in Material", () => { - if (ImGuiApi.Checkbox("##visible", ref visible)) + if (EditorImGui.Checkbox("##visible", ref visible)) { presentation.visible = visible; SavePresentation(false); @@ -231,7 +230,7 @@ private void DrawParameter(ShaderGraphInputSettings input) bool range = presentation.hasRange; InspectorRow("parameter.range", "Range", () => { - if (ImGuiApi.Checkbox("##range", ref range)) + if (EditorImGui.Checkbox("##range", ref range)) { presentation.hasRange = range; SavePresentation(false); @@ -278,7 +277,7 @@ void Save( bool continuous ) { definition.properties[index] = value; - CommitDefinition(definition, "Edit Shader Parameter", continuous && ImGuiApi.IsAnyItemActive()); + CommitDefinition(definition, "Edit Shader Parameter", continuous && EditorImGui.IsAnyItemActive()); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs index 975fb4812..87d5b9fab 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.PassSettings.cs @@ -6,7 +6,6 @@ using Inno.Rendering; using Inno.Rendering.Shaders; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; @@ -16,14 +15,14 @@ private void StageSettingsPopup( GraphNodeRecord node, ShaderGraphStageSettings stage ) { - ImGuiApi.SetNextWindowSize(new(540, 600), (ImGuiCond.Appearing)); - if (!ImGuiApi.BeginPopup("##stage-settings")) + EditorImGui.SetNextWindowSize(new(540, 600), (ImGuiCond.Appearing)); + if (!EditorImGui.BeginPopup("##stage-settings")) return; try { ShaderDefinition definition = ShaderGraphDocument.ReadDefinition(Controller.document, owner.serialization, owner.context); bool definitionChanged = false; - ImGuiApi.SeparatorText("Shader"); + EditorImGui.SeparatorText("Shader"); string name = definition.name; bool nameChanged = false; InspectorRow("settings.name", "Name", () => nameChanged = EditorImGui.InputText("##name", ref name, 256)); @@ -34,7 +33,7 @@ ShaderGraphStageSettings stage } Gesture(); - ImGuiApi.SeparatorText(stage.stage + " Interface"); + EditorImGui.SeparatorText(stage.stage + " Interface"); ShaderGraphPassProgram[] programs = ShaderGraphPrograms.Read(Controller.document, owner.serialization, owner.context); string activePass = draft.inspectedPass; if (!definition.passes.Any(pass => pass.name == activePass)) @@ -46,7 +45,7 @@ ShaderGraphStageSettings stage try { foreach (ShaderPassDefinition available in definition.passes) - if (ImGuiApi.Selectable(available.name, activePass == available.name)) + if (EditorImGui.Selectable(available.name, activePass == available.name)) { activePass = available.name; draft.inspectedPass = activePass; @@ -61,7 +60,7 @@ ShaderGraphStageSettings stage bool assigned = assignment.stages?.Contains(node.id.value, StringComparer.Ordinal) == true; bool assignmentChanged = false; if (activePass.Length != 0) - InspectorRow("settings.shared-stage", "Shared Stage", () => assignmentChanged = ImGuiApi.Checkbox("##shared-stage", ref assigned)); + InspectorRow("settings.shared-stage", "Shared Stage", () => assignmentChanged = EditorImGui.Checkbox("##shared-stage", ref assigned)); if (assignmentChanged) { var stageIds = (assignment.stages ?? []).Where(id => id != node.id.value).Select(static id => new GraphNodeId(id)).ToList(); @@ -77,65 +76,70 @@ ShaderGraphStageSettings stage for (int i = 0; i < stage.outputs.Length; i++) { - ImGuiApi.PushId(i); - ShaderGraphOutput output = stage.outputs[i]; - bool changed = false; - string id = output.id; - bool idChanged = false; - InspectorRow("output.id", "Port ID", () => idChanged = EditorImGui.InputText("##port-id", ref id, 128)); - if (idChanged) + EditorImGui.PushId(i); + try { - output.id = id; - changed = true; - } + ShaderGraphOutput output = stage.outputs[i]; + bool changed = false; + string id = output.id; + bool idChanged = false; + InspectorRow("output.id", "Port ID", () => idChanged = EditorImGui.InputText("##port-id", ref id, 128)); + if (idChanged) + { + output.id = id; + changed = true; + } - Gesture(); - ShaderIrOutputKind kind = output.kind; - if (EnumControl("Destination", ref kind)) - { - output.kind = kind; - changed = true; - } + Gesture(); + ShaderIrOutputKind kind = output.kind; + if (EnumControl("Destination", ref kind)) + { + output.kind = kind; + changed = true; + } - string semantic = output.semantic ?? ""; - bool semanticChanged = false; - InspectorRow("output.semantic", "Semantic", () => semanticChanged = EditorImGui.InputText("##semantic", ref semantic, 128)); - if (semanticChanged) - { - output.semantic = semantic; - changed = true; - } + string semantic = output.semantic ?? ""; + bool semanticChanged = false; + InspectorRow("output.semantic", "Semantic", () => semanticChanged = EditorImGui.InputText("##semantic", ref semantic, 128)); + if (semanticChanged) + { + output.semantic = semantic; + changed = true; + } - Gesture(); - int location = output.location; - bool locationChanged = false; - InspectorRow("output.location", "Location", () => locationChanged = ImGuiApi.InputInt("##location", ref location, 1, 100, 0)); - if (locationChanged) - { - output.location = location; - changed = true; - } + Gesture(); + int location = output.location; + bool locationChanged = false; + InspectorRow("output.location", "Location", () => locationChanged = EditorImGui.InputInt("##location", ref location, 1, 100, 0)); + if (locationChanged) + { + output.location = location; + changed = true; + } - Gesture(); - if (changed) - { - stage.outputs[i] = output; - Set(node, "settings", stage, true); - } + Gesture(); + if (changed) + { + stage.outputs[i] = output; + Set(node, "settings", stage, true); + } + + if (EditorImGui.SmallButton("Remove Output")) + { + stage.outputs = stage.outputs.Where(( + _, + index + ) => index != i).ToArray(); + Set(node, "settings", stage); + break; + } - if (ImGuiApi.SmallButton("Remove Output")) + EditorImGui.Separator(); + } + finally { - stage.outputs = stage.outputs.Where(( - _, - index - ) => index != i).ToArray(); - Set(node, "settings", stage); - ImGuiApi.PopId(); - break; + EditorImGui.PopId(); } - - ImGuiApi.Separator(); - ImGuiApi.PopId(); } if (CenteredAddButton("Add Output")) @@ -153,7 +157,7 @@ ShaderGraphStageSettings stage } int passIndex = Array.FindIndex(definition.passes, pass => pass.name == activePass); - if (passIndex >= 0 && ImGuiApi.CollapsingHeader("Pass State")) + if (passIndex >= 0 && EditorImGui.CollapsingHeader("Pass State")) { ShaderPassDefinition pass = definition.passes[passIndex]; ShaderRenderState state = pass.renderState; @@ -188,8 +192,8 @@ ShaderGraphStageSettings stage bool depthWrite = state.depthWrite, multisampling = state.multisampling; bool depthWriteChanged = false, multisamplingChanged = false; - InspectorRow("state.depth-write", "Depth Write", () => depthWriteChanged = ImGuiApi.Checkbox("##depth-write", ref depthWrite)); - InspectorRow("state.multisampling", "Multisampling", () => multisamplingChanged = ImGuiApi.Checkbox("##multisampling", ref multisampling)); + InspectorRow("state.depth-write", "Depth Write", () => depthWriteChanged = EditorImGui.Checkbox("##depth-write", ref depthWrite)); + InspectorRow("state.multisampling", "Multisampling", () => multisamplingChanged = EditorImGui.Checkbox("##multisampling", ref multisampling)); if (depthWriteChanged) { state.depthWrite = depthWrite; @@ -208,25 +212,25 @@ ShaderGraphStageSettings stage return; try { - if (ImGuiApi.Selectable("Opaque")) + if (EditorImGui.Selectable("Opaque")) { state.blend = RenderBlendState.opaque; changed = true; } - if (ImGuiApi.Selectable("Alpha")) + if (EditorImGui.Selectable("Alpha")) { state.blend = RenderBlendState.alpha; changed = true; } - if (ImGuiApi.Selectable("Premultiplied")) + if (EditorImGui.Selectable("Premultiplied")) { state.blend = RenderBlendState.premultiplied; changed = true; } - if (ImGuiApi.Selectable("Additive")) + if (EditorImGui.Selectable("Additive")) { state.blend = RenderBlendState.additive; changed = true; @@ -239,7 +243,7 @@ ShaderGraphStageSettings stage }); int mask = state.colorWriteMask; bool maskChanged = false; - InspectorRow("state.write-mask", "RGBA Write Mask", () => maskChanged = ImGuiApi.InputInt("##write-mask", ref mask, 1, 100, 0)); + InspectorRow("state.write-mask", "RGBA Write Mask", () => maskChanged = EditorImGui.InputInt("##write-mask", ref mask, 1, 100, 0)); if (maskChanged) { state.colorWriteMask = (byte)Math.Clamp(mask, 0, 15); @@ -247,11 +251,11 @@ ShaderGraphStageSettings stage } RenderBlendState blend = state.blend; - if (ImGuiApi.TreeNode("Custom Blending", 0)) + if (EditorImGui.TreeNode("Custom Blending", 0)) { bool enabled = blend.enabled; bool enabledChanged = false; - InspectorRow("blend.enabled", "Enabled", () => enabledChanged = ImGuiApi.Checkbox("##enabled", ref enabled)); + InspectorRow("blend.enabled", "Enabled", () => enabledChanged = EditorImGui.Checkbox("##enabled", ref enabled)); if (enabledChanged) { blend.enabled = enabled; @@ -296,7 +300,7 @@ ShaderGraphStageSettings stage changed = true; } - ImGuiApi.TreePop(); + EditorImGui.TreePop(); } state.blend = blend; @@ -316,69 +320,73 @@ ShaderGraphStageSettings stage } definitionChanged |= TechniqueControls(definition); - if (ImGuiApi.CollapsingHeader("Variants")) + if (EditorImGui.CollapsingHeader("Variants")) { for (int i = 0; i < definition.keywords.Length; i++) { - ImGuiApi.PushId("keyword-" + i); - ShaderKeywordDefinition keyword = definition.keywords[i]; - string id = keyword.id; - bool keywordChanged = false; - InspectorRow("variant.keyword", "Keyword", () => keywordChanged = EditorImGui.InputText("##keyword", ref id, 128)); - if (keywordChanged) + EditorImGui.PushId("keyword-" + i); + try { - keyword.id = id; - definitionChanged = true; - } + ShaderKeywordDefinition keyword = definition.keywords[i]; + string id = keyword.id; + bool keywordChanged = false; + InspectorRow("variant.keyword", "Keyword", () => keywordChanged = EditorImGui.InputText("##keyword", ref id, 128)); + if (keywordChanged) + { + keyword.id = id; + definitionChanged = true; + } - Gesture(); - for (int j = 0; j < keyword.options.Length; j++) - { - string option = keyword.options[j]; - bool optionChanged = false, remove = false; - InspectorRow("variant.option", "Option " + (j + 1), () => + Gesture(); + for (int j = 0; j < keyword.options.Length; j++) { - optionChanged = EditorImGui.InputText("##option", ref option, 128); - ImGuiApi.SameLine(0f, -1f); - remove = ImGuiApi.SmallButton("Remove"); - }); - if (optionChanged) + string option = keyword.options[j]; + bool optionChanged = false, remove = false; + InspectorRow("variant.option", "Option " + (j + 1), () => + { + optionChanged = EditorImGui.InputText("##option", ref option, 128); + EditorImGui.SameLine(0f, -1f); + remove = EditorImGui.SmallButton("Remove"); + }); + if (optionChanged) + { + keyword.options[j] = option; + definitionChanged = true; + } + + Gesture(); + if (remove) + { + keyword.options = keyword.options.Where(( + _, + index + ) => index != j).ToArray(); + definitionChanged = true; + break; + } + } + + if (CenteredAddButton("Add Option")) { - keyword.options[j] = option; + keyword.options = [..keyword.options, "Option" + keyword.options.Length]; definitionChanged = true; } - Gesture(); - if (remove) + definition.keywords[i] = keyword; + if (EditorImGui.SmallButton("Remove Keyword")) { - keyword.options = keyword.options.Where(( + definition.keywords = definition.keywords.Where(( _, index - ) => index != j).ToArray(); + ) => index != i).ToArray(); definitionChanged = true; break; } } - - if (CenteredAddButton("Add Option")) - { - keyword.options = [..keyword.options, "Option" + keyword.options.Length]; - definitionChanged = true; - } - - definition.keywords[i] = keyword; - if (ImGuiApi.SmallButton("Remove Keyword")) + finally { - definition.keywords = definition.keywords.Where(( - _, - index - ) => index != i).ToArray(); - definitionChanged = true; - ImGuiApi.PopId(); - break; + EditorImGui.PopId(); } - - ImGuiApi.PopId(); } if (CenteredAddButton("Add Keyword")) @@ -395,13 +403,13 @@ ShaderGraphStageSettings stage ShaderGraphDocument.definitionKey, ShaderGraphDocument.Encode(owner.serialization.Serialize(definition, owner.context), owner.serialization, owner.context) ); - Controller.ReplaceDocument(candidate, "Edit Shader Settings", ImGuiApi.IsAnyItemActive() ? draft.valueGesture : null); + Controller.ReplaceDocument(candidate, "Edit Shader Settings", EditorImGui.IsAnyItemActive() ? draft.valueGesture : null); owner.Changed(draft); } } finally { - ImGuiApi.EndPopup(); + EditorImGui.EndPopup(); } } @@ -420,7 +428,7 @@ ref T value try { foreach (T candidate in Enum.GetValues()) - if (ImGuiApi.Selectable(candidate.ToString(), candidate.Equals(current))) + if (EditorImGui.Selectable(candidate.ToString(), candidate.Equals(current))) { current = candidate; changed = true; diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs index 6ba83640c..9e711558a 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.SourceSettings.cs @@ -7,7 +7,6 @@ using Inno.Assets.Pipeline; using Inno.Rendering.Assets; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets.Authoring; namespace Inno.Editor.Panel.ShaderEditor; @@ -19,28 +18,28 @@ private void SourceSettings(Guid sourceId) return; InspectorRow("source.actions", "Source", () => { - if (ImGuiApi.SmallButton("Show in File Browser") + if (EditorImGui.SmallButton("Show in File Browser") && owner.interactions.TryGetModule(out var browser) && browser is not null) RevealShaderFunction.Reveal(owner, browser, sourceId); - ImGuiApi.SameLine(0f, -1f); - if (ImGuiApi.SmallButton("Import Settings…")) + EditorImGui.SameLine(0f, -1f); + if (EditorImGui.SmallButton("Import Settings…")) { LoadSourceSettings(info.assetPath, sourceId); - ImGuiApi.OpenPopup("##source-settings"); + EditorImGui.OpenPopup("##source-settings"); } }); - ImGuiApi.SetNextWindowSize(new(580, 470), (ImGuiCond.Appearing)); - if (!ImGuiApi.BeginPopup("##source-settings")) + EditorImGui.SetNextWindowSize(new(580, 470), (ImGuiCond.Appearing)); + if (!EditorImGui.BeginPopup("##source-settings")) return; try { - ImGuiApi.Text(info.assetPath.ToString()); - ImGuiApi.Separator(); + EditorImGui.Text(info.assetPath.ToString()); + EditorImGui.Separator(); if (draft.settingsSource != sourceId) LoadSourceSettings(info.assetPath, sourceId); bool readOnly = !owner.assets.TryGetFileSystemEntry(info.assetPath, out AssetFileEntry entry) || entry.isReadOnly; - ImGuiApi.BeginDisabled(readOnly); + EditorImGui.BeginDisabled(readOnly); try { var settings = owner.serialization.Deserialize(draft.sourceSettings, owner.context); @@ -52,7 +51,7 @@ private void SourceSettings(Guid sourceId) try { foreach (string language in owner.frontends.languageIds) - if (ImGuiApi.Selectable(language, settings.languageId == language)) + if (EditorImGui.Selectable(language, settings.languageId == language)) { settings.languageId = language; changed = true; @@ -83,7 +82,7 @@ private void SourceSettings(Guid sourceId) int catalogOrder = settings.catalogOrder; bool orderChanged = false; - InspectorRow("source.catalog-order", "Catalog Order", () => orderChanged = ImGuiApi.InputInt("##catalog-order", ref catalogOrder, 1, 100, 0)); + InspectorRow("source.catalog-order", "Catalog Order", () => orderChanged = EditorImGui.InputInt("##catalog-order", ref catalogOrder, 1, 100, 0)); if (orderChanged) { settings.catalogOrder = catalogOrder; @@ -91,36 +90,40 @@ private void SourceSettings(Guid sourceId) } EditorUI.current.widgets.WrappedText("Catalog paths are declared by the source library and group its exported functions in Shader creation tools. They do not affect compilation or runtime assets."); - ImGuiApi.SeparatorText("Exported Functions"); + EditorImGui.SeparatorText("Exported Functions"); for (int i = 0; i < settings.exports.Length; i++) { - ImGuiApi.PushId("export." + i); - string function = settings.exports[i]; - bool functionChanged = false, remove = false; - InspectorRow("source.export", "Function " + (i + 1), () => - { - functionChanged = EditorImGui.InputText("##name", ref function, 256); - ImGuiApi.SameLine(0f, -1f); - remove = ImGuiApi.SmallButton("Remove"); - }); - if (functionChanged) + EditorImGui.PushId("export." + i); + try { - settings.exports[i] = function; - changed = true; - } + string function = settings.exports[i]; + bool functionChanged = false, remove = false; + InspectorRow("source.export", "Function " + (i + 1), () => + { + functionChanged = EditorImGui.InputText("##name", ref function, 256); + EditorImGui.SameLine(0f, -1f); + remove = EditorImGui.SmallButton("Remove"); + }); + if (functionChanged) + { + settings.exports[i] = function; + changed = true; + } - if (remove) + if (remove) + { + settings.exports = settings.exports.Where(( + _, + index + ) => index != i).ToArray(); + changed = true; + break; + } + } + finally { - settings.exports = settings.exports.Where(( - _, - index - ) => index != i).ToArray(); - changed = true; - ImGuiApi.PopId(); - break; + EditorImGui.PopId(); } - - ImGuiApi.PopId(); } if (CenteredAddButton("Add Export")) @@ -130,56 +133,60 @@ private void SourceSettings(Guid sourceId) } EditorUI.current.widgets.WrappedText("Every listed name is exported as an independent graph function. All other functions remain private helpers. Ports are parsed from each exported declaration; alternative implementations must expose matching interfaces."); - ImGuiApi.SeparatorText("Alternative Implementations"); + EditorImGui.SeparatorText("Alternative Implementations"); for (int i = 0; i < settings.implementations.Length; i++) { - ImGuiApi.PushId(i); - ShaderFunctionAsset? current = settings.implementations[i]; - bool remove = false; - InspectorRow("source.implementation", "Implementation " + (i + 1), () => + EditorImGui.PushId(i); + try { - if (EditorUI.current.widgets.BeginBoundedCombo("##implementation", current?.assetPath.ToString() ?? "Missing source")) + ShaderFunctionAsset? current = settings.implementations[i]; + bool remove = false; + InspectorRow("source.implementation", "Implementation " + (i + 1), () => { - try + if (EditorUI.current.widgets.BeginBoundedCombo("##implementation", current?.assetPath.ToString() ?? "Missing source")) { - foreach (AssetFileEntry source in owner.assets.GetFileSystemEntries(includeDirectories: false)) - if (source.extension == ".ishadersource" - && source.assetPath != info.assetPath - && ImGuiApi.Selectable( - source.assetPath.ToString(), - current?.assetPath == source.assetPath - ) - && owner.assets.TryLoad( - source.assetPath, - out ShaderFunctionAsset? replacement - ) - && replacement is not null) - { - settings.implementations[i] = replacement; - changed = true; - } + try + { + foreach (AssetFileEntry source in owner.assets.GetFileSystemEntries(includeDirectories: false)) + if (source.extension == ".ishadersource" + && source.assetPath != info.assetPath + && EditorImGui.Selectable( + source.assetPath.ToString(), + current?.assetPath == source.assetPath + ) + && owner.assets.TryLoad( + source.assetPath, + out ShaderFunctionAsset? replacement + ) + && replacement is not null) + { + settings.implementations[i] = replacement; + changed = true; + } + } + finally + { + EditorUI.current.widgets.EndBoundedCombo(); + } } - finally - { - EditorUI.current.widgets.EndBoundedCombo(); - } - } - ImGuiApi.SameLine(0f, -1f); - remove = ImGuiApi.SmallButton("Remove"); - }); - if (remove) + EditorImGui.SameLine(0f, -1f); + remove = EditorImGui.SmallButton("Remove"); + }); + if (remove) + { + settings.implementations = settings.implementations.Where(( + _, + index + ) => index != i).ToArray(); + changed = true; + break; + } + } + finally { - settings.implementations = settings.implementations.Where(( - _, - index - ) => index != i).ToArray(); - changed = true; - ImGuiApi.PopId(); - break; + EditorImGui.PopId(); } - - ImGuiApi.PopId(); } if (CenteredAddButton("Add Implementation")) @@ -190,8 +197,8 @@ out ShaderFunctionAsset? replacement if (changed) draft.sourceSettings = owner.serialization.Serialize(settings, owner.context); - ImGuiApi.Separator(); - if (ImGuiApi.Button("Apply Import Settings")) + EditorImGui.Separator(); + if (EditorImGui.Button("Apply Import Settings")) { bool imported = owner.importSettings.Apply(info.assetPath, settings, draft.sourceSettingsFingerprint); LoadSourceSettings(info.assetPath, sourceId); @@ -201,11 +208,11 @@ out ShaderFunctionAsset? replacement } finally { - ImGuiApi.EndDisabled(); + EditorImGui.EndDisabled(); } - ImGuiApi.SameLine(0f, -1f); - if (ImGuiApi.Button("Reload")) + EditorImGui.SameLine(0f, -1f); + if (EditorImGui.Button("Reload")) LoadSourceSettings(info.assetPath, sourceId); if (readOnly) EditorUI.current.widgets.ColoredText(Inno.Editor.ImGui.EditorPalette.textDisabled, "Installed source settings are read-only."); @@ -220,7 +227,7 @@ out ShaderFunctionAsset? replacement } finally { - ImGuiApi.EndPopup(); + EditorImGui.EndPopup(); } } diff --git a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs index ca052718f..1d3d1d50b 100644 --- a/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs +++ b/src/composition/editor/panels/Inno.Editor.Panel.ShaderEditor/Canvas/ShaderEditorCanvas.Techniques.cs @@ -3,7 +3,6 @@ using System.Linq; using Inno.Rendering; using EditorImGui = Inno.Editor.ImGui.ImGui; -using ImGuiApi = Inno.Editor.ImGui.ImGui; using Inno.Rendering.Assets; namespace Inno.Editor.Panel.ShaderEditor; @@ -11,124 +10,133 @@ internal sealed partial class ShaderEditorCanvas { private bool TechniqueControls(ShaderDefinition definition) { - if (!ImGuiApi.CollapsingHeader("Techniques & Roles")) + if (!EditorImGui.CollapsingHeader("Techniques & Roles")) return false; EditorUI.current.widgets.WrappedText("Techniques map pipeline-defined roles to this shader's passes. Contract and role identities are supplied by the rendering pipeline."); bool changed = false; for (int i = 0; i < definition.techniques.Length; i++) { - ImGuiApi.PushId("technique-" + i); - ShaderTechniqueDefinition technique = definition.techniques[i]; - string id = technique.id.value ?? "", contract = technique.contract.value ?? ""; - bool idChanged = false; - InspectorRow("technique.id", "ID", () => idChanged = EditorImGui.InputText("##id", ref id, 128)); - if (idChanged) + EditorImGui.PushId("technique-" + i); + try { - technique.id = new() + ShaderTechniqueDefinition technique = definition.techniques[i]; + string id = technique.id.value ?? "", contract = technique.contract.value ?? ""; + bool idChanged = false; + InspectorRow("technique.id", "ID", () => idChanged = EditorImGui.InputText("##id", ref id, 128)); + if (idChanged) { - value = id - }; - changed = true; - } - - Gesture(); - bool contractChanged = false; - InspectorRow("technique.contract", "Contract", () => contractChanged = EditorImGui.InputText("##contract", ref contract, 256)); - if (contractChanged) - { - technique.contract = new() - { - value = contract - }; - changed = true; - } - - Gesture(); - GraphicsCapability features = technique.requiredFeatures; - if (FeatureControls(ref features)) - { - technique.requiredFeatures = features; - changed = true; - } + technique.id = new() + { + value = id + }; + changed = true; + } - for (int j = 0; j < technique.passes.Length; j++) - { - ImGuiApi.PushId(j); - ShaderTechniquePass mapping = technique.passes[j]; - string role = mapping.role.value ?? ""; - bool roleChanged = false; - InspectorRow("technique.role", "Role", () => roleChanged = EditorImGui.InputText("##role", ref role, 128)); - if (roleChanged) + Gesture(); + bool contractChanged = false; + InspectorRow("technique.contract", "Contract", () => contractChanged = EditorImGui.InputText("##contract", ref contract, 256)); + if (contractChanged) { - mapping.role = new() + technique.contract = new() { - value = role + value = contract }; changed = true; } Gesture(); - bool passChanged = false; - InspectorRow("technique.pass", "Pass", () => + GraphicsCapability features = technique.requiredFeatures; + if (FeatureControls(ref features)) + { + technique.requiredFeatures = features; + changed = true; + } + + for (int j = 0; j < technique.passes.Length; j++) { - if (!EditorUI.current.widgets.BeginBoundedCombo("##pass", mapping.passName)) - return; + EditorImGui.PushId(j); try { - foreach (ShaderPassDefinition pass in definition.passes) - if (ImGuiApi.Selectable(pass.name, pass.name == mapping.passName)) + ShaderTechniquePass mapping = technique.passes[j]; + string role = mapping.role.value ?? ""; + bool roleChanged = false; + InspectorRow("technique.role", "Role", () => roleChanged = EditorImGui.InputText("##role", ref role, 128)); + if (roleChanged) + { + mapping.role = new() { - mapping.passName = pass.name; - passChanged = true; + value = role + }; + changed = true; + } + + Gesture(); + bool passChanged = false; + InspectorRow("technique.pass", "Pass", () => + { + if (!EditorUI.current.widgets.BeginBoundedCombo("##pass", mapping.passName)) + return; + try + { + foreach (ShaderPassDefinition pass in definition.passes) + if (EditorImGui.Selectable(pass.name, pass.name == mapping.passName)) + { + mapping.passName = pass.name; + passChanged = true; + } + } + finally + { + EditorUI.current.widgets.EndBoundedCombo(); } + }); + changed |= passChanged; + technique.passes[j] = mapping; + if (EditorImGui.SmallButton("Remove Role")) + { + technique.passes = technique.passes.Where(( + _, + index + ) => index != j).ToArray(); + changed = true; + break; + } } finally { - EditorUI.current.widgets.EndBoundedCombo(); + EditorImGui.PopId(); + } + } + + if (CenteredAddButton("Add Role")) + { + technique.passes = [..technique.passes, new() + { + role = new("role-" + technique.passes.Length), + passName = definition.passes.FirstOrDefault().name ?? "" } - }); - changed |= passChanged; - technique.passes[j] = mapping; - if (ImGuiApi.SmallButton("Remove Role")) + + ]; + changed = true; + } + + definition.techniques[i] = technique; + if (EditorImGui.SmallButton("Remove Technique")) { - technique.passes = technique.passes.Where(( + definition.techniques = definition.techniques.Where(( _, index - ) => index != j).ToArray(); + ) => index != i).ToArray(); changed = true; - ImGuiApi.PopId(); break; } - ImGuiApi.PopId(); - } - - if (CenteredAddButton("Add Role")) - { - technique.passes = [..technique.passes, new() - { - role = new("role-" + technique.passes.Length), - passName = definition.passes.FirstOrDefault().name ?? "" - } - - ]; - changed = true; + EditorImGui.Separator(); } - - definition.techniques[i] = technique; - if (ImGuiApi.SmallButton("Remove Technique")) + finally { - definition.techniques = definition.techniques.Where(( - _, - index - ) => index != i).ToArray(); - changed = true; - ImGuiApi.PopId(); - break; + EditorImGui.PopId(); } - - ImGuiApi.Separator(); - ImGuiApi.PopId(); } if (CenteredAddButton("Add Technique")) @@ -149,27 +157,33 @@ private bool TechniqueControls(ShaderDefinition definition) private static bool FeatureControls(ref GraphicsCapability features) { - if (!ImGuiApi.TreeNode("Required Capabilities", 0)) + if (!EditorImGui.TreeNode("Required Capabilities", 0)) return false; - bool changed = false; - GraphicsCapability updated = features; - foreach (GraphicsCapability feature in Enum.GetValues()) + try { - ulong bits = Convert.ToUInt64(feature); - if (bits == 0 || (bits & (bits - 1)) != 0) - continue; - bool enabled = (updated & feature) != 0; - bool featureChanged = false; - InspectorRow("feature." + feature, feature.ToString(), () => featureChanged = ImGuiApi.Checkbox("##enabled", ref enabled)); - if (featureChanged) + bool changed = false; + GraphicsCapability updated = features; + foreach (GraphicsCapability feature in Enum.GetValues()) { - updated = enabled ? updated | feature : updated & ~feature; - changed = true; + ulong bits = Convert.ToUInt64(feature); + if (bits == 0 || (bits & (bits - 1)) != 0) + continue; + bool enabled = (updated & feature) != 0; + bool featureChanged = false; + InspectorRow("feature." + feature, feature.ToString(), () => featureChanged = EditorImGui.Checkbox("##enabled", ref enabled)); + if (featureChanged) + { + updated = enabled ? updated | feature : updated & ~feature; + changed = true; + } } - } - features = updated; - ImGuiApi.TreePop(); - return changed; + features = updated; + return changed; + } + finally + { + EditorImGui.TreePop(); + } } } diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs index 4d5ccbfc0..ea96a3d75 100644 --- a/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/GameExportModal.cs @@ -1,4 +1,3 @@ -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; @@ -6,7 +5,6 @@ using Inno.Build.Managed; using Inno.Editor.Core; using Inno.Editor.ImGui; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Workbench; @@ -182,7 +180,7 @@ private void DrawButtons() { Ui.Spacing(); string primaryLabel = "Export"; - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float closeWidth = Ui.MeasureText("Close").X + style.framePadding.X * 2f; float primaryWidth = Ui.MeasureText(primaryLabel).X + style.framePadding.X * 2f; Ui.SetCursorPosX(Ui.GetCursorPosX() + MathF.Max(0f, Ui.GetContentRegionAvailable().X - closeWidth - style.itemSpacing.X - primaryWidth)); @@ -201,7 +199,7 @@ Action apply ) { BeginField(label); Ui.SetNextItemWidth(-1f); - _ = EditorImGui.InputText($"##{id}", ref value, C_TEXT_CAPACITY); + _ = Ui.InputText($"##{id}", ref value, C_TEXT_CAPACITY); apply(value); } } diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs index a426c2d01..be022e422 100644 --- a/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Exporting/PluginExportModal.cs @@ -1,11 +1,9 @@ using Inno.Editor.ImGui; -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Numerics; using Inno.Editor.Core; using Inno.Editor.ImGui.ImGuiWidget; -using EditorImGui = Inno.Editor.ImGui.ImGui; using Ui = Inno.Editor.ImGui.ImGui; namespace Inno.Editor.Workbench; @@ -77,7 +75,7 @@ private void DrawOutcome() private void DrawButtons() { Ui.Spacing(); - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float closeWidth = Ui.MeasureText("Close").X + style.framePadding.X * 2f; string primaryLabel = window.isPluginBusy ? "Cancel" : "Export"; float exportWidth = Ui.MeasureText(primaryLabel).X + style.framePadding.X * 2f; @@ -111,7 +109,7 @@ Action apply ) { Ui.Text(label); Ui.SetNextItemWidth(-1f); - if (EditorImGui.InputText($"##{id}", ref value, C_TEXT_CAPACITY)) + if (Ui.InputText($"##{id}", ref value, C_TEXT_CAPACITY)) apply(value); else apply(value); diff --git a/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs index bf981b2e1..e9d68e178 100644 --- a/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs +++ b/src/composition/editor/presentation/Inno.Editor.Workbench/Settings/IconSettings.cs @@ -1,5 +1,4 @@ using Inno.Editor.ImGui; -using UiDrawing = Inno.Editor.ImGui.ImGui; using EditorUI = Inno.Editor.ImGui.EditorUiSession; using System; using System.Collections.Generic; @@ -66,7 +65,7 @@ protected override void OnDraw(EditorSettingObject setting) Vector2 minimum = Ui.GetItemMinimum(); Vector2 maximum = Ui.GetItemMaximum(); - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float lineHeight = Ui.GetTextLineHeight(); DrawIconTextAt( Ui.GetDrawList(), @@ -118,7 +117,7 @@ private static void DrawIconTextAt( string text, float iconSlotWidth ) { - EditorUiLayout style = UiDrawing.GetLayout(); + EditorUiLayout style = Ui.GetLayout(); float fontSize = Ui.GetFontSize(); float lineHeight = Ui.GetTextLineHeight(); uint color = Ui.GetStyleColor(ImGuiCol.Text); diff --git a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs index 40a1887e5..eb3ab9956 100644 --- a/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs +++ b/tests/tooling/Inno.Tooling.Architecture.Tests/ArchitectureCommandTests.cs @@ -162,6 +162,14 @@ bool rejected [InlineData("src/content/assets/Probe.cs", "internal class Probe { void Read() { context.With(resolver); } }", "owner-complete", true)] [InlineData("src/services/audio/Probe.cs", "public class Probe { public MaEngine engine; }", "native implementation", true)] [InlineData("src/services/audio/Probe.cs", "internal class Probe { void Release() { OnCleanupFailed(phase, failure); } }", "diagnostic-only", true)] + [InlineData("src/composition/editor/panels/Probe.cs", "using Ui = Inno.Editor.ImGui.ImGui; internal class Probe { void Draw() { for (int i = 0; i < 2; i++) { Ui.PushId(i); DrawRow(); if (i == 1) break; Ui.PopId(); } } }", "presentation loop ID scopes", true)] + [InlineData("src/composition/editor/panels/Probe.cs", "using Ui = Inno.Editor.ImGui.ImGui; internal class Probe { void Draw() { for (int i = 0; i < 2; i++) { Ui.PushId(i); try { DrawRow(); if (i == 1) break; } finally { Ui.PopId(); } } } }", "presentation loop ID scopes", false)] + [InlineData("src/composition/editor/panels/Probe.cs", "using Ui = Inno.Editor.ImGui.ImGui; using Other = Inno.Editor.ImGui.ImGui; internal class Probe { }", "multiple presentation aliases", true)] + [InlineData("src/composition/editor/panels/Probe.cs", "using Ui = Inno.Editor.ImGui.ImGui; using NativeUi = Inno.Native.ImGui.ImGui; internal class Probe { }", "multiple presentation aliases", false)] + [InlineData("src/composition/editor/panels/Probe.cs", "/// \n/// Removed identity.\n/// \n/// \n/// Actual section.\n/// \ninternal class Probe { }", "multiple XML summary elements", true)] + [InlineData("src/composition/editor/panels/Probe.cs", "/// \n/// Actual section.\n/// \ninternal class Probe { }", "multiple XML summary elements", false)] + [InlineData("src/services/Probe.cs", "using Ui = Inno.Editor.ImGui.ImGui; internal class Probe { void Draw() { for (int i = 0; i < 2; i++) { Ui.PushId(i); DrawRow(); Ui.PopId(); } } }", "presentation loop ID scopes", false)] + [InlineData("src/composition/editor/panels/Probe.cs", "internal class Probe { void Draw() { for (int i = 0; i < 2; i++) { collector.PushId(i); DrawRow(); collector.PopId(); } } }", "presentation loop ID scopes", false)] [InlineData("src/services/audio/Probe.cs", "internal class Probe { void Release() { try { } catch (RetirementPendingException) { throw; } } }", "wrapped pending ownership", true)] [InlineData("src/services/audio/Probe.cs", "internal class Probe { void Release() { try { } catch (Inno.Core.Execution.RetirementTimeoutException failure) { throw; } } }", "wrapped pending ownership", true)] [InlineData("src/services/audio/Probe.cs", "internal class Probe { void Release() { try { } catch (Exception error) when (RetirementPendingException.Find(error) is not null) { throw; } } }", "wrapped pending ownership", false)] diff --git a/tools/Inno.Tooling.Architecture/CSharpStyleValidator.cs b/tools/Inno.Tooling.Architecture/CSharpStyleValidator.cs index cf47171ac..670581609 100644 --- a/tools/Inno.Tooling.Architecture/CSharpStyleValidator.cs +++ b/tools/Inno.Tooling.Architecture/CSharpStyleValidator.cs @@ -18,6 +18,7 @@ ICollection failures ) { SyntaxTree tree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.CSharp14)); SourceText text = tree.GetText(); + EditorPresentationSourceValidator.Validate(relativePath, tree, failures); foreach (ParameterListSyntax parameters in tree.GetRoot().DescendantNodes().OfType()) { if (parameters.Parameters.Count <= 1) diff --git a/tools/Inno.Tooling.Architecture/EditorPresentationSourceValidator.cs b/tools/Inno.Tooling.Architecture/EditorPresentationSourceValidator.cs new file mode 100644 index 000000000..7c92df433 --- /dev/null +++ b/tools/Inno.Tooling.Architecture/EditorPresentationSourceValidator.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Inno.Tooling.Architecture; + +internal static class EditorPresentationSourceValidator +{ + private const string C_FACADE = "Inno.Editor.ImGui.ImGui"; + + internal static void Validate( + string relativePath, + SyntaxTree tree, + ICollection failures + ) { + if (!relativePath.StartsWith("src/composition/editor/", StringComparison.Ordinal)) + return; + SyntaxNode root = tree.GetRoot(); + UsingDirectiveSyntax[] aliases = root.DescendantNodes().OfType() + .Where(static directive => directive.Alias is not null && directive.Name is not null).ToArray(); + foreach (IGrouping group in aliases.GroupBy( + static directive => directive.Name!.ToString(), StringComparer.Ordinal)) + { + if (group.Count() > 1) + Report(relativePath, tree, group.First(), "one type cannot have multiple presentation aliases in the same file", failures); + } + var facadeAliases = aliases.Where(static directive => directive.Name!.ToString() == C_FACADE) + .Select(static directive => directive.Alias!.Name.Identifier.ValueText).ToHashSet(StringComparer.Ordinal); + facadeAliases.Add(C_FACADE); + facadeAliases.Add("global::" + C_FACADE); + foreach (BlockSyntax block in root.DescendantNodes().OfType()) + { + if (block.Parent is not (ForStatementSyntax or ForEachStatementSyntax or ForEachVariableStatementSyntax or WhileStatementSyntax or DoStatementSyntax)) + continue; + ExpressionStatementSyntax[] statements = block.Statements.OfType().ToArray(); + foreach (ExpressionStatementSyntax push in statements.Where(statement => IsFacadeCall(statement, "PushId", facadeAliases))) + { + if (statements.Any(statement => statement.SpanStart > push.SpanStart && IsFacadeCall(statement, "PopId", facadeAliases))) + Report(relativePath, tree, push, "presentation loop ID scopes require local try/finally cleanup", failures); + } + } + foreach (DocumentationCommentTriviaSyntax comment in root.DescendantTrivia() + .Select(static trivia => trivia.GetStructure()).OfType()) + { + if (comment.Content.OfType().Count(static element => element.StartTag.Name.LocalName.ValueText == "summary") > 1) + Report(relativePath, tree, comment, "a declaration cannot retain multiple XML summary elements", failures); + } + } + + private static bool IsFacadeCall( + ExpressionStatementSyntax statement, + string method, + IReadOnlySet receivers + ) => statement.Expression is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax member } + && member.Name.Identifier.ValueText == method && receivers.Contains(member.Expression.ToString()); + + private static void Report( + string relativePath, + SyntaxTree tree, + SyntaxNode node, + string message, + ICollection failures + ) => failures.Add($"{relativePath}:{tree.GetLineSpan(node.Span).StartLinePosition.Line + 1}: {message}."); +} From 870a0af0be0977313054e0a1127977fa4e98317b Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Wed, 28 Oct 2026 21:44:27 +0800 Subject: [PATCH 25/54] fix(build): isolate product outputs and restore entry reference owners --- AGENTS.md | 1 + Directory.Build.props | 6 + Directory.Build.targets | 10 +- .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 2 + docs/build/README.md | 4 + docs/core/Inno.Core.Serialization.md | 6 +- docs/tooling/Inno.Tooling.Architecture.md | 6 +- .../InnoEngine.Editor.csproj | 1 + .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 10 ++ ...EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md | 19 ++- .../ManagedOutputOwnershipTests.cs | 157 ++++++++++++++++++ .../StaticGraphRestoreTests.cs | 68 ++++++++ .../editor-ui/PanelLifecycle.editor.cs | 65 +++++++- 13 files changed, 344 insertions(+), 11 deletions(-) create mode 100644 tests/build/Inno.Build.Tests/ManagedOutputOwnershipTests.cs diff --git a/AGENTS.md b/AGENTS.md index 8eeefd70f..ffd79066e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -297,6 +297,7 @@ public void AAA( - 冷 CMake producer 通过共同 executor 固定 recipe 声明的工作区输入;Browser 聚合的组件 include、bridge 与参数必须解析到同一复制树。热缓存命中不复制,外部 SDK 与输出保持自己的位置和 owner,不用字符串替换整个仓库根来误重定向输出。 - Task runtime 只在 IBuildEngine4 Build 生命周期、同一完整请求身份内复用成功不可变 publication;registry 只存 BCL 数据,不持有 SDK owner、lease 或私有 ALC 类型。每个实际载入边界仍有独立 owner;失败/取消不缓存。共享 restore 写入由 Core.IO lease 协调,等待时 Yield/Reacquire,保留锁文件。 - 独立并发托管构建必须通过 SDK 的明确 ArtifactsPath 使用各自 bin/obj owner;共同规则不得覆盖该输出选择。NuGet Runtime 资产按产品真实 Restore 图选择,Native 内容寻址缓存仍通过自身 lease 共享。普通 IDE bin/obj 仅属于一个活动构建,不能把两个独立进程同时写同一路径视为受支持的并发模式。 +- 同一 SDK 图中的中立与产品请求也必须隔离实际输出;使用 ArtifactsPath 时,在 SDK 建立默认路径前选择包含产品输入的 ArtifactsProjectName,保留 SDK configuration/TFM/RID pivots 和显式调用方设置。开发 Editor 入口通过现有 owned restore 准备所选产品的完整引用闭包后再构建,不能依赖平台子项目稍后还原来满足入口的直接或 SDK 添加的传递引用。 - Owned NuGet Restore 必须使用标准 `ExcludeRestorePackageImports=true`,编译使用包含已恢复 imports 的独立求值配置。不得让同一缓存 ProjectInstance 把旧 package content items 带入新程序集输出,也不得通过包名特例、补写 NuGet imports 或删除缓存掩盖混装。 - NuGet 依赖使用正常 `.csproj` 的 `PackageReference` 与 SDK Restore,不启用、生成或提交依赖锁文件,不维护逐工程、宿主、RID、AOT 或裁剪的锁清单与命名规则。包版本由正式声明维护,BGCS 包族继续使用统一精确版本;消费者读取 SDK 实际求值的 assets 和包内容。CI 使用普通还原并验证构建、包身份和源码卫生;不得以自制依赖解析器、清空真实 SDK 属性或 ignore 规则代替正确流程。用于并发读写、进程所有权和原子发布的 Core IO lease 不属于依赖锁定,必须保留。 diff --git a/Directory.Build.props b/Directory.Build.props index 1dad40cee..6e8cefb51 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,6 +17,12 @@ $(DefaultItemExcludes);$(MSBuildProjectDirectory)/obj/**;$(MSBuildProjectDirectory)/bin/** + + + <_InnoManagedProductPropertiesPath Condition="'$(InnoProductBuildProperties)' != ''">$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '$(InnoProductBuildProperties)')) + $(MSBuildProjectName).$([MSBuild]::StableStringHash('$(InnoNativeTarget)|$(InnoToolTarget)|$(_InnoManagedProductPropertiesPath)|$(InnoNativeBuildFingerprint)', 'Sha256')) + + obj/$(InnoNativeTarget)/ bin/$(InnoNativeTarget)/ diff --git a/Directory.Build.targets b/Directory.Build.targets index 6247bc117..12a634a91 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -225,14 +225,18 @@ + + $(MSBuildProjectFullPath) + + + Condition="'$(InnoProductRestoreProject)' != '' and '$(DesignTimeBuild)' != 'true' and '$(InnoProductRestore)' != 'false'"> diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index 9fe72571c..b33118be4 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -87,6 +87,8 @@ Missing 形成可恢复 barrier,不移动 Undo/Redo 栈,不丢原始 bytes Begin/End、Push/Pop、Child、Table、Popup、Clip 和样式遵守各自真实配对规则,并用局部 `try/finally` 释放本次所有权。扩展异常保留原始诊断,后续合法帧必须能够继续绘制。不能清空其他 owner 的状态来恢复当前 Panel。 +循环行的 ID 生命周期必须覆盖异常、提前退出和普通完成;移除条目后的 break 也通过同一 finally 释放,不在分支中手工重复 PopId。架构源码门禁检查这一已确认的回归模式,并检查重复呈现别名及失效 XML 说明;真实绘制、失效借用和跨 owner 异常仍由各自 Native 契约验收。 + Presentation 的准备、绘制与发布属于同一次 owner 操作;期间拒绝重入和退休。准备失败不创建 Native frame;绘制失败只结束所属 Context 的失败帧,不发布部分 draw data。调用方仍须平衡自己打开的窗口和栈,后端不伪造修复其他 owner 的作用域。 关闭 Panel 退休其暂态控件;关闭文档退休文档状态。generation 切换不能保留旧 Type、delegate、扩展实例或 collectible 引用。旧 ALC 退休严格执行既有 GC/finalizer/weak-monitor barrier;timeout 进入 Faulted,不能清空 monitor 继续。 diff --git a/docs/build/README.md b/docs/build/README.md index a17e3427e..3123c40bb 100644 --- a/docs/build/README.md +++ b/docs/build/README.md @@ -42,6 +42,10 @@ Windows、MacOS、Browser 的 target/source/validator/template 属于各平台 B 需要验证全新托管输出或并行验收时,可对 `Invoke-ManagedChecks.ps1` 指定独立的 `-ArtifactsPath`;构建、普通测试、隔离发现和隔离执行共用该 SDK 输出 owner。 +共同 props 在 SDK 默认输出路径建立前,为具有产品输入的工程选择独立 `ArtifactsProjectName`:身份覆盖 Native target、工具 target、产品 props 和 Native build fingerprint。中立工程与 Roslyn 工具保持中立,SDK 自身的 configuration、TFM 和 RID pivots 不变;还原和编译消费同一 owner。调用者显式设置的 SDK 输出属性仍由调用者负责,构建工具必须读取求值后的实际路径,不能拼接 `ArtifactsPath/bin/`。这避免两个正式开发入口在同一个 SDK 构建图中争写共享工程的 DLL;不靠串行构建或清空真实 RID 规避。 + +Editor 开发入口只声明需要还原其宿主路由选中的产品工程。共享 `RestoreProductTargetGraph` 使用已有 `RestoreOwnedProjectTask`、Core IO lease 和 Restore/compilation 隔离,先完成该闭包,再开始入口的直接及传递引用构建。平台产品独立构建仍按同一协议准备自身;入口不保存平台名单或另建依赖解析器。 + Reload fixture 使用普通 `ProjectReference` 声明构建依赖,通过 SDK 的 `OutputItemType` 接收实际产物,再部署到测试宿主的 Modules 目录;不拼接 `bin//`,也不从另一输出 owner 借用 fixture。 Wiki 脚本 fixture 从测试工程实际求值的 framework `ReferencePath` 编译,并在所属输出目录保存构建清单;运行测试的 runtime 补丁与 SDK reference pack 可以不同,不按 runtime 补丁号推导 reference pack 路径。 diff --git a/docs/core/Inno.Core.Serialization.md b/docs/core/Inno.Core.Serialization.md index f784413e6..193835375 100644 --- a/docs/core/Inno.Core.Serialization.md +++ b/docs/core/Inno.Core.Serialization.md @@ -154,7 +154,7 @@ public sealed class OrderedState : ISerializable ### 跨 generation 的独立成员恢复 -逐成员快照用于热重载和其他需要容忍 schema 演进的流程,不会改变普通 `Serialize` / `Deserialize` / `Restore` 的严格行为: +逐成员快照用于当前会话中的扩展重载、成员暂时 Missing 及恢复;普通 `Serialize` / `Deserialize` / `Restore` 仍保持严格的当前格式: ```csharp using System.Collections.Generic; @@ -175,7 +175,7 @@ static SerializationPropertyRestoreResult RestoreMatchingProperties( `SerializationPropertyRestoreMode.Strict` 在第一个匹配但不可解码的成员处抛异常。`Compatible` 使用独立的 operation checkpoint 跳过该成员,撤销它注册的 completion callback,保留目标对象构造后的默认值,并在 `failures` 中记录旧类型、新类型和错误信息。 -Editor History 等需要跨 assembly generation 保留数据的系统应使用 `CapturePropertyData` / `RestorePropertiesData`。该格式包含 property key 与原声明类型信息,但不包含 owning object 引用;调用方必须用 persistent ID 在当前 generation 解析目标。格式严格检查当前 magic、长度与 trailing bytes,损坏数据不会被部分接受。 +Editor History 等需要跨 assembly generation 保留数据的系统应使用 `CapturePropertyData` / `RestorePropertiesData`。该格式保存 property key 与独立编码的值节点,不保存 CLR Type 或 owning object;恢复时由当前目标的 metadata 决定声明类型。内存中的 `SerializationPropertySnapshot.propertyType` 只属于其创建代际,不能据此长期持有 collectible 类型。调用方必须用 persistent ID 在当前 generation 解析目标。格式严格检查当前 magic、长度与 trailing bytes,损坏数据不会被部分接受。 已删除成员计入 `ignoredCount`,新增成员自然保持默认值。所有兼容成员完成后,目标的 `[OnSerializableRestored]` 仍只执行一次;对象级 restore hook 失败属于严重错误,不会被 compatible 模式吞掉。 @@ -301,6 +301,8 @@ static (float x, float y)[] RoundTripPoints( 单属性快照 CapturePropertyData 只读取指定的持久属性;其他 getter 不会因捕获该属性而执行。二进制节点编码先计算确定大小,再用标准库 BinaryWriter 写入独立的结果数组,避免增长缓冲区及最终复制。当前 magic、字段顺序、UTF-8 长度编码和调用方拥有的 bytes 语义保持一致;这是非公开实现,不增加格式或缓存协议。 +已解析 Converter 的查找复用当前 generation 自有的并发缓存;首次解析及实例创建仍受同一个 owner 协调,最后一个 lease 释放后才清空缓存并退休实例。冻结 generation 的并行调用仍要求 Converter 和被读写数据自身满足相应线程约束,调用方应在所有操作完成后释放 lease。这些内部查找改良不改变 Registry、Context 或业务对象的所有权。 + 内部序列化操作仅在需要时创建回调、恢复去重与循环检测集合;不使用跨操作对象池。已有上下文只有在 TypeCatalog、Registry 和 metadata source 均属于当前 owner 时才复用,否则重新绑定这些依赖并保留调用方服务。`SerializationContext.With` 仍返回新的不可变上下文,确定性键排序和当前二进制格式保持不变。 ## 当前源码公开 API 清单 diff --git a/docs/tooling/Inno.Tooling.Architecture.md b/docs/tooling/Inno.Tooling.Architecture.md index 1f9f7d6d1..7066e4c08 100644 --- a/docs/tooling/Inno.Tooling.Architecture.md +++ b/docs/tooling/Inno.Tooling.Architecture.md @@ -1,5 +1,7 @@ # Inno.Tooling.Architecture +[Tooling 索引](README.md) · [Wiki 首页](../README.md) + ## 独立输出目录的检查 使用 `--dotnet ` 和 `--artifacts-path ` 检查独立 SDK 输出。求值图的 `TargetPath` 决定公开程序集的 owner,Wiki 的 `ReferencePath`、`Compile`、语言和符号也使用同一输出选择。不要把独立输出复制回默认 bin 来满足检查。 @@ -7,8 +9,6 @@ `--configuration` 支持 `Debug`、`SourceDebug` 和 `Release`。`--artifacts-path` 要求显式 `--dotnet`;SDK 失败、缺失引用或取消均不能当作通过。 -[Tooling 索引](README.md) - 退休 catch 的 AST 规则拒绝直接捕获 `RetirementPendingException` / `RetirementTimeoutException`(包括全限定名)。 应捕获 Exception,并用 Core `RetirementPendingException.Find` 检查整棵异常树后原样传播。 规则包含两个反例和一个合法 filter 正例;它是源码约束,不声称静态证明任意动态异常、类型别名或 native callback 行为。 @@ -22,6 +22,8 @@ Registry 派生实现不得直接调用 `OnCleanupFailed` 代替退休失败传 默认检查手写源码的多参数声明:逐参数换行、右括号对齐,以及紧随参数列表的 `) {`。 检查基于 C# 语法节点,覆盖方法、构造函数、委托、主构造函数、局部函数和 Lambda,不改写源码。 +Editor 源码还检查同类型重复别名、重复 XML summary,以及循环中依赖手动 PopId 收尾的 ImGui ID 作用域。循环行必须在局部 finally 中释放 ID,确保异常、break 和正常完成均保持配对。该 AST 门禁识别已确认的源码模式,不替代其他 Begin/End、Push/Pop 的所有权审查或真实 Native 异常测试;分组取得与包装方法仍需沿实际调用方核对。 + ## 检查范围 SDK 工程图检查读取实际程序集身份、产品目标、ProjectReference、Compile 和 using 项,验证依赖方向、源码 owner 与可执行入口。`--project-graph` 的非公开诊断模型只描述真实求值结果,不维护 NuGet 解析规则;[依赖还原](../build/DEPENDENCY_SUBMISSION.md)直接使用标准 SDK 流程。 diff --git a/entry/InnoEngine.Editor/InnoEngine.Editor.csproj b/entry/InnoEngine.Editor/InnoEngine.Editor.csproj index 2d7afb91b..9a56e1e3f 100644 --- a/entry/InnoEngine.Editor/InnoEngine.Editor.csproj +++ b/entry/InnoEngine.Editor/InnoEngine.Editor.csproj @@ -8,6 +8,7 @@ $(InnoDevelopmentTarget) $(InnoDevelopmentTarget) $(InnoDevelopmentEditorBuildProperties) + $(InnoDevelopmentEditorProject) Exe InnoEngine.Editor InnoEngine.Editor diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index 9b2b0fb69..601d57a92 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -213,3 +213,13 @@ Final working-tree verification: the twelve-project compilation, Core 49 / Scene - Absolute hierarchy performance remains poor: 345.14 ms median, 402.69 ms P95 and 139.06 MB allocated per frame. New issue #46 owns the remaining complete-scene dirty-check bottleneck and the required correctness/invalidation design; no timer relaxation or partial cache is used. - Exact-commit managed Windows/macOS and NuGet dependency CI pass. Full native Windows/macOS CI is still running; no completion or Ready claim is made. - Full E08/E09/E10/E11 and the parent retain the limitations in the report. Relative performance improvement and focused green suites do not establish whole-plan completion. + +## Closure follow-up evidence + +- `6df06585` bounds binary encoding buffers and generation-owned converter lookup contention; Core 59 / Scene 53 and independent Release Core 59 pass. It does not close the full-scene dirty-scan issue #46. +- `e2e6ca32` balances six Shader settings row scopes and one tree scope, removes duplicate aliases/XML summaries, and adds limited AST checks; source-contract tests pass 16 cases. +- `closure-final`: Contracts 721 passes plus two platform-inapplicable link cases; Workflows 305, Visual 136, Lifecycle 11. Profiles overlap. +- The extra two-entry SDK build reproduced CS2012, then missing assets exposed the entry/product restore ordering. Existing owned restore and early SDK output-owner selection address the actual chain; no BGCS or platform workaround. Initial red/green evidence is preserved; expanded real SDK tests pass 9 cases in `e40/output-entry-final.trx`. +- The actual FlappyBird source extension completed 100 close/open cycles for all nine panels during 15,000 D3D12 frames. Eleven full-GC resource samples preserve Selection/Scene/History/pending-event invariants and show stable managed memory. This is not a proof of every private owner. +- Workflow 40653896090 for f7dce89c completed all four native/managed jobs successfully; NuGet passed separately. Later source changes require their own CI. +- The full original-product baseline incompatibility and unclosed E08/E09/E10/E11 matrix remain explicitly tracked. The PR stays Draft until all applicable gates are satisfied. diff --git a/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md index 00e095587..a6ba2b55d 100644 --- a/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md +++ b/records/architecture/acceptance/EDITOR_UI_CLOSURE_ACCEPTANCE_2026_10_28.md @@ -17,21 +17,34 @@ | 范围 | 状态与证据 | | --- | --- | | 完整十二工程测试图 | 通过;`final-candidate-suites.log/binlog`,0 编译错误;保留 NU1900 漏洞服务网络警告。 | -| Core / Scene | 通过;49 + 53 项,`final-candidate/core-scene-final.trx`。 | +| Core / Scene | 当前编码及并发改良后 59 + 53 项通过,`e46/core-scene-current.trx`;独立 Release Core 59 项通过。 | | Workflows | 通过;305 项,`final-candidate/9409749fa03d4badb6f3b34c99a3dcdc`。 | | Visual / Lifecycle | 通过;分别 136、11 项,`final-candidate` 中对应 profile 的 summary / TRX。套件有交叠,不能直接相加。 | | Editor Debug / Release、Build 入口 | 通过;`final-candidate-editor-*.log/binlog`、`final-candidate-build-entry.log/binlog`,0 编译错误。 | | FlappyBird 产品 | 通过;独立的当前 Samples 源码副本,Debug/Release × D3D11/D3D12/Vulkan 六组,每组 300 帧、正常退出;`products/final-candidate-*.log`。该 smoke 不替代完整人工旅程。 | | Canvas / Rendering2D / FlappyBird | 正式脚本编译及 IDE 投影通过;`companion-projections-final/*/generation.log`。未编辑派生代码或插件包。 | -| 完整 Contracts | 前一修正候选 713 项通过,`unified-correction-final/cf4d562d8bf1413f945d8f23e5d1d712`。后续 Core / modifier 修正后的最终完整 CI 另行记录。 | +| 完整 Contracts | `e2e6ca32` 普通及隔离执行 721 项通过,另有 2 个 Windows 不适用的 Unix link 用例跳过;`closure-final/5c4cfef780fe4107bf0822dbff8a8432`。本轮后续 SDK 输出修复另有 9 项真实 SDK 专项通过。 | | 最终 Wiki / 架构 | 通过;`wiki-final-candidate`,241/241 工程实际归属、API 清单与可编译示例门禁。网络 NU1900 警告原样保留。 | | 最终成对性能 | 三类负载各五组交替原版/当前进程、共 30 次,以及菜单/统计五组对照均完成;输入与依赖 12,778 文件、583,137,380 bytes 前后完全一致。详见下方性能结果。完整 E11 产品/交互矩阵仍未闭合。 | -| CI | `f7dce89c` 的 Windows/macOS Managed 与 NuGet job 通过;完整 Windows/macOS ARM64 job 已通过 Debug 及真实 renderer smoke,正在执行 Release/后续测试。旧 `660962a5` 的七个 Mac 输入失败已修正,仍须等待新提交的完整原生套件确认。 | +| CI | `f7dce89c` 的完整 Windows、macOS ARM64、两端 Managed 和 NuGet 检查全部通过,完整 job 含 tests、Support Pack 和 Wiki;[workflow 40653896090](https://github.com/Wolfiora/InnoEngine/actions/runs/40653896090)。后续本轮提交尚需其自身 CI,不能借用旧 head 的绿灯。 | Core 内部现在只在实际需要时创建回调、循环检测和恢复去重集合,复用 BCL reference equality;上下文仅在全部 owner 身份一致时复用。正式输出的同一属性 bytes 保持一致,分配测量为 fresh 1,888 / prepared 1,168 bytes,原 prepared 为 2,096。新公开边界回归先失败再通过;没有修改 `SerializationContext.With` 的新实例语义。该观察不是整帧或全产品 CPU 收益证明。 原生 command-key 模式会在 ImGui 内交换 Control / Super。`EditorUiInput` 现在还原物理 Core 修饰键含义,列表选择复用该边界;原生文本控件保留自身配置。四个真实输入组合验证两种 primary modifier 与两种原生模式。共享 Editor 不增加 OS 检测或平台引用,fixtures 显式选择键盘配置。 + +## 本轮收尾复验与深层自查 + +- `6df06585`:二进制编码先计算精确长度,直接写入最终 caller-owned buffer;wire bytes、字符串边界、特殊数值与错误语义由独立 BinaryWriter oracle 验证。1 MB 值的旧编码分配测试先失败,改良后通过。Converter lookup 继续属于同一个 generation,热命中使用 ConcurrentDictionary,首次构造由原 owner 锁保证唯一,最后一个 lease 才能退休类型和实例;没有全局 converter cache。 +- `e2e6ca32`:Shader settings 六处行 ID 和一处 capability tree 采用局部 finally;合并 30 个重复 alias、176 个语义符号引用,删除 3 段重复 XML summary。AST 防线仅覆盖明确模式,不伪称证明任意动态 scope。相关 source-contract 16 项通过。 +- `closure-final`:Contracts 721、Workflows 305、Visual 136、Lifecycle 11 项通过;各 profile 有交叠。Core/Scene 的后续专项为 59/53 项,不与 profile 计数相加。 +- 新增的真实 SDK 输出回归先以两项 ArtifactsPath 失败复现,普通 Debug/Release 两项通过;修复后初始四项全部通过。扩展后的 `e40/output-entry-final.trx` 共 9 项通过,覆盖 Debug/Release、普通/独立输出、真实 RID、工具中立、工具/产品/fingerprint 输入、等价 props 路径、IDE execution 属性、并行编译、编译器工具引用和入口 owned restore。 +- 全双入口构建暴露了两个关联问题:中立/产品实例争写 DLL,以及入口直接引用先于产品还原。前者使用 SDK ArtifactsProjectName 的早期选择,后者复用 RestoreOwnedProjectTask 和 Core IO lease;没有串行构建、手拷 DLL、借用默认 obj、删除 RID 或增加平台名单。`e40/two-entry-restore-fixed.log/binlog` 已通过,0 编译错误,NU1900 原样保留。规范化路径修正后的全新输出复验与最终 CI 另行记录。 +- 实际 FlappyBird D3D12 运行 15,000 帧,9 个实际 Panel 各关闭/打开 100 次,完整退出与 sRGB 检查通过。两份完成 TSV 必须存在,不能只依赖进程退出。`products/closure-panel-lifecycle-resources.tsv` 在 0/10/.../100 次 closed-state 进行 Full GC → finalizers → Full GC:Selection 始终 0、Scene 1、History resident/disk 0、Core pending events 0;managed 从 26,168,896 到 26,179,976 bytes,private 从 880,267,264 降至 864,219,136 bytes。该结果是公开状态和内存观察,不证明每一个私有 native owner。 +- 最初短 smoke 的期望字符串写错,未产生 fixture 完成标记,因此没有计入通过。增强 fixture 通过公开 Workspace API 打开实际 FlappyBird 源场景,不让引擎为验收自动创建 Untitled。测试辅助方法曾有一次编译错误,已修正,后续构建 0 错误、专项 9 项通过;失败日志保留。 +- 并发序列化对照 `e46/parallel/summary.json`:同输入、五组交替进程,8 workers / 80,000 次 decode 的中位 wall 55.46 → 42.63 ms,CPU 375 → 265.625 ms,分配基本相同;1 worker 的 10,000 次为 25.11 → 23.36 ms。Monitor contention 计数不代表所有锁种类,因此收益以独立 wall/CPU 结果说明。此负载不等同完整 UI 帧性能,完整 Scene dirty-scan 的 #46 仍 Open。 + +自查已深入执行链:平台策略仍由平台贡献;入口只声明选中的产品;Restore 与 compilation 保留隔离;共享层使用 SDK 求值路径;History、Identity、Core Events、Serialization 与 IO 没有平行实现;新的 converter 缓存仍由 generation/lease 拥有。BGCS、NuGet 包、第三方及生成绑定未修改。没有因本轮通过项而宣布 E08–E12 或整个引擎已经完美;下方未完成矩阵仍然有效。 ## 最终受控性能结果 源码候选为 `f7dce89c0d91ecb0f2333b8ca3117fefc71db96a`;原生产基线为 `b303d9f10cbaf5a905f7cb529e52cf8293b0d8bd`,原版只移植验收 harness,不修改生产实现。两侧使用同一 .NET 10.0.402、Debug 配置、机器、冻结数据和 Native command-validation 方法;各进程预热五帧,再采样十帧,五组进程交替顺序运行,没有构建/测试/产品并发。原始各组数据保留在 [性能证据目录](editor-ui-performance-2026-10-28/README.md)。 diff --git a/tests/build/Inno.Build.Tests/ManagedOutputOwnershipTests.cs b/tests/build/Inno.Build.Tests/ManagedOutputOwnershipTests.cs new file mode 100644 index 000000000..b6468234d --- /dev/null +++ b/tests/build/Inno.Build.Tests/ManagedOutputOwnershipTests.cs @@ -0,0 +1,157 @@ +using Inno.Build.Toolchains; +using Inno.Core.IO; +using Inno.Testing.Source; +using System; +using System.Collections.Generic; +using System.IO; +using System.Security; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Inno.Build.Tests; + +public sealed class ManagedOutputOwnershipTests +{ + [Theory] + [InlineData("Debug", false)] + [InlineData("Release", false)] + [InlineData("Debug", true)] + [InlineData("Release", true)] + public async Task SdkSeparatesNeutralAndProductOutputsBeforeRestore( + string configuration, + bool artifacts + ) { + string root = TestRepository.GetRoot(typeof(ManagedOutputOwnershipTests).Assembly); + string owner = Path.Combine(Path.GetTempPath(), "InnoManagedOutputTests"); + string fixture = PathBoundary.Resolve(owner, Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(fixture); + try + { + File.Copy(Path.Combine(root, "global.json"), Path.Combine(fixture, "global.json")); + await File.WriteAllTextAsync(Path.Combine(fixture, "Directory.Build.props"), $""" + + + + """); + string project = Path.Combine(fixture, "Shared.csproj"); + await File.WriteAllTextAsync(project, """ + + net10.0 + + """); + await File.WriteAllTextAsync(Path.Combine(fixture, "Shared.cs"), "public sealed class Shared { }"); + string host = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") + ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + var common = new List { $"-p:Configuration={configuration}" }; + if (artifacts) + common.Add($"-p:ArtifactsPath={Path.Combine(fixture, "outputs")}"); + Dictionary neutral = await EvaluateAsync(host, project, common); + var productArguments = new List(common) { "-p:InnoNativeTarget=test-target" }; + Dictionary product = await EvaluateAsync(host, project, productArguments); + foreach (string property in new[] { "ProjectAssetsFile", "IntermediateOutputPath", "TargetPath" }) + Assert.NotEqual(neutral[property], product[property]); + + var compileArguments = new List(productArguments) + { + "-p:BuildProjectReferences=true", "-p:BuildingInsideVisualStudio=false" + }; + Dictionary compile = await EvaluateAsync(host, project, compileArguments); + Assert.Equal(product["ProjectAssetsFile"], compile["ProjectAssetsFile"]); + Assert.Equal(product["TargetPath"], compile["TargetPath"]); + + if (artifacts) + { + foreach (string input in new[] { "InnoToolTarget", "InnoNativeBuildFingerprint" }) + { + var changedArguments = new List(productArguments) { $"-p:{input}=different-input" }; + Dictionary changed = await EvaluateAsync(host, project, changedArguments); + Assert.NotEqual(product["ProjectAssetsFile"], changed["ProjectAssetsFile"]); + Assert.NotEqual(product["TargetPath"], changed["TargetPath"]); + } + string productProperties = Path.Combine(fixture, "Product.props"); + await File.WriteAllTextAsync(productProperties, ""); + var changedProductArguments = new List(productArguments) + { + $"-p:InnoProductBuildProperties={productProperties}" + }; + Dictionary changedProduct = await EvaluateAsync(host, project, changedProductArguments); + Assert.NotEqual(product["ProjectAssetsFile"], changedProduct["ProjectAssetsFile"]); + Assert.NotEqual(product["TargetPath"], changedProduct["TargetPath"]); + var equivalentProductArguments = new List(productArguments) + { + $"-p:InnoProductBuildProperties={Path.Combine(fixture, ".", "Product.props")}" + }; + Dictionary equivalentProduct = await EvaluateAsync(host, project, equivalentProductArguments); + Assert.Equal(changedProduct["ProjectAssetsFile"], equivalentProduct["ProjectAssetsFile"]); + Assert.Equal(changedProduct["TargetPath"], equivalentProduct["TargetPath"]); + } + + var toolArguments = new List(productArguments) { "-p:IsRoslynComponent=true" }; + Dictionary compilerTool = await EvaluateAsync(host, project, toolArguments); + Assert.Equal(neutral["ProjectAssetsFile"], compilerTool["ProjectAssetsFile"]); + Assert.Equal(neutral["TargetPath"], compilerTool["TargetPath"]); + + var ridArguments = new List(productArguments) { "-p:RuntimeIdentifier=win-x64" }; + Dictionary rid = await EvaluateAsync(host, project, ridArguments); + Assert.Equal("win-x64", rid["RuntimeIdentifier"]); + Assert.NotEqual(product["TargetPath"], rid["TargetPath"]); + + foreach (List request in new[] { common, productArguments }) + await RunAsync(host, fixture, ["restore", project, .. request, "-v:quiet"]); + string driver = Path.Combine(fixture, "Build.proj"); + await File.WriteAllTextAsync(driver, $""" + + + + + InnoNativeTarget=test-target;BuildProjectReferences=true;BuildingInsideVisualStudio=false + + + + + + + """); + await RunAsync(host, fixture, ["msbuild", driver, "-t:Build", "-m:2", "-nodeReuse:false", "-v:quiet", .. common]); + Assert.True(File.Exists(neutral["TargetPath"])); + Assert.True(File.Exists(product["TargetPath"])); + } + finally + { + Directory.Delete(PathBoundary.RequireUnlinkedPath(owner, fixture), recursive: true); + } + } + + private static async Task> EvaluateAsync( + string host, + string project, + List properties + ) { + using var output = new StringWriter(); + await RunAsync(host, Path.GetDirectoryName(project)!, [ + "msbuild", project, "-nologo", .. properties, + "-getProperty:ProjectAssetsFile,IntermediateOutputPath,TargetPath,RuntimeIdentifier" + ], output); + using JsonDocument document = JsonDocument.Parse(output.ToString()); + var values = new Dictionary(StringComparer.Ordinal); + foreach (JsonProperty property in document.RootElement.GetProperty("Properties").EnumerateObject()) + values.Add(property.Name, property.Value.GetString()!); + return values; + } + + private static async Task RunAsync( + string host, + string directory, + IReadOnlyList arguments, + StringWriter? output = null + ) { + using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + using var error = new StringWriter(); + using var captured = new StringWriter(); + Exception? failure = await Record.ExceptionAsync(() => ToolchainEnvironment.RunAsync( + host, arguments, directory, cancellation.Token, DotNetSdkEnvironment.Create(host), output ?? captured, error)); + Assert.True(failure is null, $"{failure}{Environment.NewLine}{output ?? captured}{Environment.NewLine}{error}"); + } +} diff --git a/tests/build/Inno.Build.Tests/StaticGraphRestoreTests.cs b/tests/build/Inno.Build.Tests/StaticGraphRestoreTests.cs index 49df390fe..69875663c 100644 --- a/tests/build/Inno.Build.Tests/StaticGraphRestoreTests.cs +++ b/tests/build/Inno.Build.Tests/StaticGraphRestoreTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Security; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Inno.Build.Toolchains; @@ -13,6 +14,57 @@ namespace Inno.Build.Tests; public sealed class StaticGraphRestoreTests { + [Fact] + public async Task DevelopmentEntryRestoresItsDirectProductReferenceOwners() + { + string root = TestRepository.GetRoot(typeof(StaticGraphRestoreTests).Assembly); + string owner = Path.Combine(Path.GetTempPath(), "InnoEntryRestoreTests"); + string fixture = PathBoundary.Resolve(owner, Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(fixture); + try + { + string host = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") + ?? ToolchainEnvironment.ResolveExecutable("dotnet"); + string entry = Path.Combine(root, "entry", "InnoEngine.Editor", "InnoEngine.Editor.csproj"); + string artifacts = Path.Combine(fixture, "managed"); + string[] common = ["-p:Configuration=Debug", "-p:ArtifactsPath=" + artifacts, "-nodeReuse:false"]; + using JsonDocument route = await EvaluateAsync(host, entry, common); + JsonElement properties = route.RootElement.GetProperty("Properties"); + string product = properties.GetProperty("InnoProductRestoreProject").GetString()!; + Assert.True(File.Exists(product)); + string[] productProperties = [.. common, + "-p:InnoNativeTarget=" + properties.GetProperty("InnoNativeTarget").GetString(), + "-p:InnoToolTarget=" + properties.GetProperty("InnoToolTarget").GetString(), + "-p:InnoProductBuildProperties=" + properties.GetProperty("InnoProductBuildProperties").GetString()]; + using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(3)); + await ToolchainEnvironment.RunAsync(host, + ["msbuild", entry, "-t:RestoreProductTargetGraph", "-v:quiet", .. common], + root, cancellation.Token, DotNetSdkEnvironment.Create(host)); + + var references = new HashSet(StringComparer.Ordinal); + foreach (JsonElement reference in route.RootElement.GetProperty("Items").GetProperty("ProjectReference").EnumerateArray()) + references.Add(reference.GetProperty("FullPath").GetString()!); + using JsonDocument selectedProduct = await EvaluateAsync(host, product, productProperties); + foreach (JsonElement reference in selectedProduct.RootElement.GetProperty("Items").GetProperty("ProjectReference").EnumerateArray()) + { + if (!reference.TryGetProperty("PrivateAssets", out JsonElement privateAssets) || string.IsNullOrEmpty(privateAssets.GetString())) + references.Add(reference.GetProperty("FullPath").GetString()!); + } + Assert.NotEmpty(references); + foreach (string reference in references) + { + using JsonDocument evaluated = await EvaluateAsync(host, reference, productProperties); + string assets = evaluated.RootElement.GetProperty("Properties").GetProperty("ProjectAssetsFile").GetString()!; + Assert.Equal(Path.GetFullPath(assets), PathBoundary.RequireContained(artifacts, assets)); + Assert.True(File.Exists(assets), $"The entry did not restore its selected reference owner: {assets}"); + } + } + finally + { + Directory.Delete(PathBoundary.RequireUnlinkedPath(owner, fixture), recursive: true); + } + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -50,4 +102,20 @@ await ToolchainEnvironment.RunAsync(host, Directory.Delete(PathBoundary.RequireUnlinkedPath(owner, fixture), recursive: true); } } + + private static async Task EvaluateAsync( + string host, + string project, + IReadOnlyList properties + ) { + using var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + using var output = new StringWriter(); + using var error = new StringWriter(); + await ToolchainEnvironment.RunAsync(host, + ["msbuild", project, "-nologo", .. properties, + "-getProperty:ProjectAssetsFile,InnoProductRestoreProject,InnoNativeTarget,InnoToolTarget,InnoProductBuildProperties", + "-getItem:ProjectReference"], + Path.GetDirectoryName(project)!, cancellation.Token, DotNetSdkEnvironment.Create(host), output, error); + return JsonDocument.Parse(output.ToString()); + } } diff --git a/tests/fixtures/editor-ui/PanelLifecycle.editor.cs b/tests/fixtures/editor-ui/PanelLifecycle.editor.cs index 920b20a06..1af47e6ed 100644 --- a/tests/fixtures/editor-ui/PanelLifecycle.editor.cs +++ b/tests/fixtures/editor-ui/PanelLifecycle.editor.cs @@ -1,21 +1,42 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using InnoEditor.Core; using InnoEditor.Interactions; +using InnoEditor.Scene; namespace EditorUiAcceptance; /// /// Exercises the normal product panel protocol from an ordinary source Editor extension. /// +/// +/// The current Host's public commands, selection, panel and History entry points. +/// +/// +/// The current Edit workspace whose loaded scenes must survive window-only operations. +/// [EditorModule("tests.editor-ui.panel-lifecycle", order: 10_000)] -public sealed class PanelLifecycle(EditorInteractions interactions) : EditorModule +public sealed class PanelLifecycle( + EditorInteractions interactions, + IEditorSceneWorkspace workspace +) : EditorModule { + private readonly List m_resources = ["completed-cycles\tmanaged-bytes-after-full-gc\tprivate-bytes\tselection-count\tscene-count\thistory-resident-bytes\thistory-disk-bytes\tpending-events"]; private string[] m_panels = []; private bool[] m_originalOpenStates = []; + private string? m_undoName; + private string? m_redoName; + private long m_historyResidentBytes; + private long m_historyDiskBytes; + private int m_selectionCount; + private int m_sceneCount; + private int m_warmup; private int m_frame; private int m_cycles; + private bool m_baselineCaptured; private bool m_completed; /// @@ -23,8 +44,14 @@ protected override void OnUpdate(EditorContext context) { if (m_completed) return; + if (m_panels.Length == 0 && m_warmup++ < 20) + { + return; + } if (m_panels.Length == 0) { + if (workspace.scenes.Count == 0) + workspace.Open("FlappyBird/FlappyBird.iscene"); m_panels = interactions.GetPanelDeclarations().Select(static panel => panel.id).Order(StringComparer.Ordinal).ToArray(); if (m_panels.Length < 8) throw new InvalidOperationException("The complete Editor product must expose its eight standard panels."); @@ -45,6 +72,12 @@ protected override void OnUpdate(EditorContext context) } if (expectedOpen) m_cycles++; + else + { + VerifyDocumentState(); + if (m_cycles % 10 == 0) + CaptureResources(); + } if (m_cycles == 100 && !expectedOpen) { for (int index = 0; index < m_panels.Length; index++) @@ -58,6 +91,7 @@ protected override void OnUpdate(EditorContext context) File.WriteAllLines(Path.Combine(directory, "panel-lifecycle.tsv"), new[] { "panel\tcompleted-open-close-cycles", "protocol\tpublic EditorInteractions / actual product drawing" } .Concat(m_panels.Select(static panel => panel + "\t100"))); + File.WriteAllLines(Path.Combine(directory, "panel-lifecycle-resources.tsv"), m_resources); m_completed = true; return; } @@ -68,4 +102,33 @@ protected override void OnUpdate(EditorContext context) throw new InvalidOperationException("The panel protocol rejected '" + panel + "'."); } } + + private void VerifyDocumentState() + { + IEditorHistory history = interactions.history; + if (!m_baselineCaptured) + { + m_undoName = history.undoName; + m_redoName = history.redoName; + m_historyResidentBytes = history.residentBytes; + m_historyDiskBytes = history.diskBytes; + m_selectionCount = interactions.selection.count; + m_sceneCount = workspace.scenes.Count; + m_baselineCaptured = true; + } + if (history.isFaulted || history.undoName != m_undoName || history.redoName != m_redoName || + history.residentBytes != m_historyResidentBytes || history.diskBytes != m_historyDiskBytes || + interactions.selection.count != m_selectionCount || workspace.scenes.Count != m_sceneCount) + throw new InvalidOperationException("Window-only lifecycle operations changed Edit selection, scenes or committed History state."); + } + + private void CaptureResources() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + long managed = GC.GetTotalMemory(forceFullCollection: false); + using Process process = Process.GetCurrentProcess(); + m_resources.Add(FormattableString.Invariant($"{m_cycles}\t{managed}\t{process.PrivateMemorySize64}\t{interactions.selection.count}\t{workspace.scenes.Count}\t{interactions.history.residentBytes}\t{interactions.history.diskBytes}\t{interactions.events.pendingCount}")); + } } From bb5b4bb9aff7d926cf585040c2feb4566f7ad74e Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Thu, 29 Oct 2026 00:29:25 +0800 Subject: [PATCH 26/54] feat(editor): initialize new projects with the standard dock layout --- AGENTS.md | 1 + docs/architecture/CURRENT_ACCEPTANCE.md | 2 + .../architecture/EDITOR_UI_DESIGN_STANDARD.md | 4 + docs/editor/Inno.Editor.Core.md | 6 +- docs/editor/Inno.Editor.Hosting.md | 8 ++ .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 1 + ...OR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md | 65 ++++++++++ .../Inno.Editor.Core/Inno.Editor.Core.csproj | 3 + .../Inno.Editor.Core/Runtime/EditorContext.cs | 15 ++- .../Runtime/EditorLayoutSettings.cs | 45 ++++--- .../Inno.Editor.Hosting/Hosting/EditorHost.cs | 2 +- .../Inno.Editor.Hosting.csproj | 3 + .../Layout/DefaultEditorLayout.ini | 75 ++++++++++++ .../Layout/EditorDefaultLayout.cs | 19 +++ .../DefaultLayoutNativeTests.cs | 101 +++++++++++++++ .../EditorLayoutInitializationTests.cs | 115 ++++++++++++++++++ .../Inno.Editor.PlayMode.Tests.csproj | 5 + 17 files changed, 447 insertions(+), 23 deletions(-) create mode 100644 records/architecture/acceptance/EDITOR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md create mode 100644 src/composition/editor/hosting/Inno.Editor.Hosting/Layout/DefaultEditorLayout.ini create mode 100644 src/composition/editor/hosting/Inno.Editor.Hosting/Layout/EditorDefaultLayout.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/DefaultLayoutNativeTests.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/EditorLayoutInitializationTests.cs diff --git a/AGENTS.md b/AGENTS.md index ffd79066e..d3e5001c2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,6 +115,7 @@ - 保存、打开、选择等纯工作流操作默认不进入数据 Undo;它们只有在确实修改项目数据时才记录对应的数据部分。 - 跨启动的项目语义状态直接属于 Module/Panel;它们使用 Attribute 中必填、稳定且全局唯一的 ID,并通过 protected `Capture(EditorState)` / `Restore(EditorState)` hooks 参与持久化。扩展只调用参数对象的 `Get` / `Set`,不得在公开或 protected API 中暴露 JSON 实现。未 override Capture 的类型不进入状态 IO;不得恢复独立 Workspace interface、reader/writer 或第二个状态 ID。持久值只允许可重新解析的中立数据。 - `editor.ini` 是统一且可读的项目级 Editor settings 文档:标准 ImGui section 保存 layout;每个 Module/Panel 分别使用 `[InnoEditor][Module.]` / `[InnoEditor][Panel.]`;Panel 开关使用 `[InnoEditor][Panels]`。禁止用 Base64 或单一 opaque payload 包装全部 Workspace。Undo 栈、dirty Scene 内容、runtime 引用和编译中间态不得持久化。 +- 新项目的默认 layout 由标准 Host 的嵌入资源提供,Core 只接收中立初始文档并复用 Core IO 原子写入。仅文件不存在时初始化,不能覆盖已有或刻意留空的 `editor.ini`;模板不得带入示例场景、Asset ID、插件状态、Selection、History 或临时弹窗。 - Editor Selection 是当前 session 的瞬时交互状态,不得写入 `editor.ini`。Workspace 可以保存可独立解释的导航位置、已打开文档和 active document,但启动后不得自动恢复 Asset、Scene、GameObject、Component 或 System selection。 - Editor 正常退出时必须先捕获全部有状态的 Module/Panel,再捕获最新 ImGui layout,最后在 Module 停止和 Scene 卸载前强制原子写入一次完整 `editor.ini`。运行期间仍可节流保存,但不能把它当作退出保存的替代品。 - Editor Scene 修改统一进入 `Inno.Editor.Scene.SceneEdits`。普通属性只保存单 property bytes;Component/System 保存 element identity/type/index/state;GameObject 删除保存最小 subtree;层级只保存受影响 placement;禁止为小修改序列化或恢复完整 Scene。 diff --git a/docs/architecture/CURRENT_ACCEPTANCE.md b/docs/architecture/CURRENT_ACCEPTANCE.md index 0c4ed8b11..82abc421f 100644 --- a/docs/architecture/CURRENT_ACCEPTANCE.md +++ b/docs/architecture/CURRENT_ACCEPTANCE.md @@ -8,6 +8,8 @@ 当前依赖通过标准 `.csproj` / `PackageReference` 和 SDK Restore 解析,源码不保存独立依赖锁清单;本轮范围与证据见[标准 NuGet 还原验收](../../records/architecture/acceptance/STANDARD_NUGET_RESTORE_ACCEPTANCE_2026_10_26.md)。基线保持 .NET SDK 10.0.402、C# 14、BGCS 4.1.2。 +新项目的默认 `editor.ini` 已完成本地专项验收,见[默认布局报告](../../records/architecture/acceptance/EDITOR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md)。标准 Host 提供布局,Core 复用原子文档写入;已有文件不被默认值重置。该结果不替代完整 UI 原计划或最终提交的 CI。 + 此前 BGCS 消费与三项优先修复的独立证据见[BGCS 4.1.2 消费验收](../../records/architecture/acceptance/BGCS412_CONSUMER_ACCEPTANCE_2026_10_25.md)及[专项报告](../../records/architecture/acceptance/PRIORITY_FIX_ACCEPTANCE_2026_10_25.md)。历史通过数量不能当作当前源码已重跑。 以下表格保留此前 BGCS 消费验收的独立范围和数量;不与本轮标准还原检查重复累计,也不代表本轮重跑完整产品矩阵。 diff --git a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md index b33118be4..8c324cc63 100644 --- a/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md +++ b/docs/architecture/EDITOR_UI_DESIGN_STANDARD.md @@ -4,6 +4,10 @@ 本页定义当前 Editor 的交互和实现契约。测试结果及未完成项属于验收报告;本页不以规范代替验收证据。 +## 首次打开项目 + +标准 Host 为缺少 `editor.ini` 的项目提供通用停靠布局,并在加载 presentation 前原子创建文档。布局资源由 Host 组合层拥有;Core 只处理传入的中立文档与现有状态协议。已有布局(包括刻意留空的文件)不能被默认值覆盖。默认资源不得带入示例场景、Asset ID、插件状态、Selection、History 或临时弹窗;后续保存复用统一文档生命周期。 + ## 职责与依赖 | Owner | 职责 | 禁止承担的职责 | diff --git a/docs/editor/Inno.Editor.Core.md b/docs/editor/Inno.Editor.Core.md index 910ec39a1..cbedc027f 100644 --- a/docs/editor/Inno.Editor.Core.md +++ b/docs/editor/Inno.Editor.Core.md @@ -49,6 +49,10 @@ Commands、Menus、DragDrop、Selection 和 Rename 状态属于 `Inno.Editor.Int `EditorContext` 是由 Application host 创建并注入扩展的中立数据,不承担路由: +Host 构造入口为 `EditorContext(string projectDirectory, EditorKeyboardPolicy keyboard, string? initialLayoutDocument = null)`;该构造函数不导出给脚本。可选初始文档由组合层提供,Core 不内置标准面板、停靠比例或任何项目内容。仅当 `editor.ini` 不存在时通过 Core IO `AtomicFile` 创建,已有文件(包括空文件)保持原始内容及时间戳;并发创建不覆盖先完成者。读取、权限或创建失败明确传播。未提供初始文档时,Context 保持被动,不在构造时创建文件。 + +普通保存继续使用同一份可读文档和原子替换;没有第二套 Writer、模板 Reader 或格式兼容分支。 + ```csharp using System; using Inno.Editor.Core; @@ -342,7 +346,7 @@ Editor 模块更新与 Reload 事务使用 Edit Session;Play Tick 和运行对 - + diff --git a/docs/editor/Inno.Editor.Hosting.md b/docs/editor/Inno.Editor.Hosting.md index ef25d2083..9b962e3d0 100644 --- a/docs/editor/Inno.Editor.Hosting.md +++ b/docs/editor/Inno.Editor.Hosting.md @@ -8,6 +8,14 @@ `src/composition/editor/EditorProduct.props` 是内置 Editor 功能、Panel 和呈现程序集的唯一产品依赖声明。Windows、macOS 产品共同导入它,保证仅由 attribute 发现、没有 CLR 调用引用的扩展仍进入部署目录和产品依赖清单。Hosting 的项目引用仅承担自身实现或公开契约所需依赖,不通过类型锚点、运行时目录扫描或重复平台列表补齐产品。 +## 新项目默认布局 + +`Layout/DefaultEditorLayout.ini` 是标准 Host 拥有的嵌入资源,初始化 `EditorContext` 时传入,随后才由 presentation 加载。新项目首次打开会立即获得可读的 `editor.ini`;已有文件始终优先,不因升级或打开项目自动覆盖。 + +默认排列使用标准停靠结构:左上 Scene/Game 并排、左下 File,中间 Hierarchy,右上 Inspector/Shader Editor 标签,右下 Stats/Console 标签。初始比例由正常 ImGui dock tree 表达,现有呈现逻辑适应实际窗口尺寸。 + +模板仅包含八个标准窗口、停靠数据和标准面板开关;不包含示例场景、Shader ID、导航状态、Plugin 窗口、Selection、History、编译或导出弹窗。默认文件不是另一个 Workspace 系统,后续保存仍归现有统一生命周期。 + ## 组合、生命周期与扩展 公开入口为 `EditorApplication.RunAsync(EditorLaunchOptions, CancellationToken)`。产品传入项目、catalog、selection、窗口、帧驱动、键盘约定、distribution、明确初始导出目标、SDK上下文、模块/序列化来源与日志工厂。 diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index 601d57a92..01ceecba5 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -10,6 +10,7 @@ Every work item below is a real GitHub issue created before implementation. Issu | Work | Issue | Owner | State | | --- | --- | --- | --- | +| New-project default layout | [#47](https://github.com/Wolfiora/InnoEngine/issues/47) | Standard Hosting resource and Core document owner | Local 9 focused / 150 related / 131 Interactions / 10 Hosting cases pass; hidden first-run Host and 241-project Wiki pass; final committed CI pending | | E01 scopes and failures | [#28](https://github.com/Wolfiora/InnoEngine/issues/28) | ImGui adapter and concrete presentation | Focused native scopes passed; final graph/lifecycle coverage pending | | E02 command indexes | [#29](https://github.com/Wolfiora/InnoEngine/issues/29) | Interactions generation catalog | Implemented; focused contracts passed; final retirement/performance pending | | E03 styles | [#30](https://github.com/Wolfiora/InnoEngine/issues/30) | Shared and domain presentation | Domain styles moved to their actual owners; same-frame style deferral and foreign-context contracts passed; complete physical visual matrix pending | diff --git a/records/architecture/acceptance/EDITOR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md b/records/architecture/acceptance/EDITOR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md new file mode 100644 index 000000000..7746d18bd --- /dev/null +++ b/records/architecture/acceptance/EDITOR_DEFAULT_LAYOUT_ACCEPTANCE_2026_10_29.md @@ -0,0 +1,65 @@ +# 新项目默认 Editor 布局验收 + +[Issue #47](https://github.com/Wolfiora/InnoEngine/issues/47) · [PR #26](https://github.com/Wolfiora/InnoEngine/pull/26) · [执行台账](../EDITOR_UI_CLOSURE_PLAN.md) · [Host Wiki](../../../docs/editor/Inno.Editor.Hosting.md) + +## 结果与范围 + +本地专项验收通过。新项目首次打开时,标准 Host 为不存在的 `editor.ini` 原子安装默认文档,然后再加载 presentation。布局采用当前 FlappyBird 的通用停靠比例;原始样例文件 SHA-256 为 `a62d29ad965db98e7ba1ebab357b5444b58633530922daa9a82db0c992758cca`,未修改该文件。 + +左上 Scene/Game 并排、左下 File、中间 Hierarchy、右上 Inspector/Shader Editor 标签、右下 Stats/Console 标签。模板不包含场景、Asset ID、Plugin 窗口、导航状态、Selection、History、编译或导出弹窗;屏幕绝对位置不从样例复制。现有布局(包括刻意留空的文件)在初始化时保持原样;正常使用后的统一状态保存仍会记录最新布局。 + +本项不代表完整 UI 原计划已经闭合。PR 保持 Draft;最终提交的远端 CI 独立报告,issue 合并时才关闭。 + +## 实际职责与文件树 + +```text +src/composition/editor/ +├── framework/Inno.Editor.Core/ +│ ├── Runtime/EditorContext.cs +│ ├── Runtime/EditorLayoutSettings.cs +│ └── Inno.Editor.Core.csproj +└── hosting/Inno.Editor.Hosting/ + ├── Layout/ + │ ├── DefaultEditorLayout.ini + │ └── EditorDefaultLayout.cs + ├── Hosting/EditorHost.cs + └── Inno.Editor.Hosting.csproj + +tests/editor/Inno.Editor.PlayMode.Tests/ +├── EditorLayoutInitializationTests.cs +└── DefaultLayoutNativeTests.cs +``` + +Host 拥有默认窗口与布局数据,Core 不知道标准面板或示例项目。唯一新增公开边界是现有 Host-only `EditorContext` 构造入口的可选 `initialLayoutDocument` 参数;它传递中立文档,不进入 Scripting API,也不要求项目作者填写配置。保留一个构造实现,没有旧 overload 或转发层。 + +首次创建和普通保存都复用 `Inno.Core.IO.AtomicFile`,删除了原先私有固定 `.tmp` 写入协议。首次安装使用 `overwrite: false`,并发创建后读取实际完成者的文档;真实 IO 失败传播,不发布半成品。Core IO 引用属于实现依赖,没有泄漏到公开签名。 + +## 本地验收 + +证据根为 `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27/e47`。本节记录基于 `870a0af0` 的工作区候选,源码/资源哈希保存在 `source-inputs.json`;提交后以 PR 的实际 head 与 CI 为准。 + +| 检查 | 结果与证据 | +| --- | --- | +| 新增专项 | 9 项通过,`initial-layout.trx`:缺失创建、已有/空文件保护、16 路并发、失败清理、保存重开、无默认值时的被动 Context,以及两组真实 Native docking/resize。 | +| PlayMode 相关套件 | 150 项通过,`layout-related-suite.trx`;包含新增 9 项,不能再次相加。 | +| Interactions | 131 项通过,`Inno.Editor.Interactions.Tests.trx`;覆盖现有状态、History 与设置工作流。 | +| Hosting | 10 项通过,`Inno.Editor.Hosting.Tests.trx`;预取消和配置拒绝继续不创建项目。 | +| 标准 Editor Debug | 构建通过,0 编译错误;`layout-product-build.log/binlog`。保留 4 个 NU1900 网络/漏洞服务警告。 | +| 完整 SourceDebug Solution | 构建通过,0 编译错误;`layout-source-solution.log/binlog`,同样保留网络警告。 | +| 当前 Player 引用闭包 | 正式项目构建通过;`wiki-player-reference-build.log/binlog`。不借用 Editor 或默认目录的引用。 | +| 架构/XML/Wiki/示例 | 最终通过,241/241 SDK 工程归属;`wiki-final-gate.log`、`wiki-final/api.json`、`wiki-final/examples.json`。 | +| 实际空项目首开 | 隐藏窗口 D3D12 Host 完成 300 帧并有序退出;`first-run-hidden-result.json` 与 stdout/stderr。自动生成的 INI 含八个实际 docked 窗口,没有示例或 Untitled 场景。 | + +Native 几何测试通过真实 `ImGuiEditorRuntime` 加载标准 dock tree,再从 1600×900 调整到 1280×720、800×600,检查窗口边界、列比例、上下关系和两个标签组。它没有创建桌面操作或冒充物理显示器视觉验证。实际隐藏 Host 进一步证明嵌入资源进入了正式产品初始化流程;隐藏运行不证明人工视觉体验。 + +首轮 Wiki 门禁发现新构造签名映射、API 页中的外部样例引用,以及独立输出 owner 缺少选定 Player 闭包,共五项诊断。签名按 SDK 清单更新,样例出处留在本验收记录,Player 按正式工程构建。最终门禁全部通过,没有忽略或降低校验规则。 + +## 深层自查 + +- 默认值归组合 owner,Core 只接收不可变字符串并管理同一可读文档;没有平台名单、渲染 API 分支或领域类型反向依赖。 +- 原子创建、失败清理、保存和竞态复用 Core IO;没有平行 Writer、事件系统、身份表、Workspace 或格式兼容机制。 +- 首次初始化发生在 presentation 读取之前;已有文档的读取权威、模块恢复与退出原子保存顺序不变。没有隐式创建 Scene。 +- 未修改 BGCS、NuGet 包、第三方或生成绑定,没有新增 global/implicit using、IVT、私有反射或生产测试入口。 +- 用户停止电脑操作后未再使用 computer use;本专项通过原生 Context 测试与隐藏 Host 验收,不操作桌面焦点。 + +指定的 `/System/Library/Sounds/Glass.aiff` 在本 Windows 环境不可用,未播放。 diff --git a/src/composition/editor/framework/Inno.Editor.Core/Inno.Editor.Core.csproj b/src/composition/editor/framework/Inno.Editor.Core/Inno.Editor.Core.csproj index d8b053d66..0a98bf14d 100644 --- a/src/composition/editor/framework/Inno.Editor.Core/Inno.Editor.Core.csproj +++ b/src/composition/editor/framework/Inno.Editor.Core/Inno.Editor.Core.csproj @@ -10,6 +10,9 @@ + diff --git a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs index 65fe34898..c52027aa5 100644 --- a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs +++ b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorContext.cs @@ -21,23 +21,34 @@ public sealed class EditorContext /// /// The explicit immutable conventions selected by product composition. /// + /// + /// An optional host-owned initial settings document, installed atomically only when + /// editor.ini does not exist. Existing documents, including empty files, are preserved. + /// /// /// The keyboard policy is null. /// /// /// Thrown when is empty. /// + /// + /// The current document cannot be read or its initial document cannot be installed. + /// + /// + /// The caller cannot access or initialize the project settings document. + /// [ScriptingApiIgnore] public EditorContext( string projectDirectory, - EditorKeyboardPolicy keyboard + EditorKeyboardPolicy keyboard, + string? initialLayoutDocument = null ) { if (string.IsNullOrWhiteSpace(projectDirectory)) throw new ArgumentException("A project directory is required.", nameof(projectDirectory)); ArgumentNullException.ThrowIfNull(keyboard); this.keyboard = keyboard; this.projectDirectory = Path.GetFullPath(projectDirectory); - layout = new EditorLayoutSettings(this.projectDirectory); + layout = new EditorLayoutSettings(this.projectDirectory, initialLayoutDocument); statistics = new EditorStatistics(); } diff --git a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorLayoutSettings.cs b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorLayoutSettings.cs index 04169f06a..03d5bf18f 100644 --- a/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorLayoutSettings.cs +++ b/src/composition/editor/framework/Inno.Editor.Core/Runtime/EditorLayoutSettings.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Text; +using Inno.Core.IO; namespace Inno.Editor.Core; @@ -26,14 +27,19 @@ internal sealed class EditorLayoutSettings /// /// The project root that owns editor.ini. /// + /// + /// The optional initial document supplied by the composition owner, never a replacement for an existing file. + /// /// /// Thrown when is empty. /// - internal EditorLayoutSettings(string projectDirectory) - { + internal EditorLayoutSettings( + string projectDirectory, + string? initialDocument + ) { ArgumentException.ThrowIfNullOrWhiteSpace(projectDirectory); m_path = Path.Combine(Path.GetFullPath(projectDirectory), "editor.ini"); - string document = File.Exists(m_path) ? File.ReadAllText(m_path) : string.Empty; + string document = ReadDocument(initialDocument); Parse(document, out m_imguiLayout, out m_sections); m_lastSavedDocument = Compose(m_imguiLayout, m_sections); } @@ -200,24 +206,25 @@ internal void Save() private void WriteDocument(string document) { - string? directory = Path.GetDirectoryName(m_path); - if (!string.IsNullOrEmpty(directory)) - Directory.CreateDirectory(directory); - string temporaryPath = m_path + ".tmp"; - using (var stream = new FileStream( - temporaryPath, - FileMode.Create, - FileAccess.Write, - FileShare.None, - 4096, - FileOptions.WriteThrough)) + AtomicFile.WriteAllBytes(m_path, Encoding.UTF8.GetBytes(document)); + m_lastSavedDocument = document; + } + + private string ReadDocument(string? initialDocument) + { + if (File.Exists(m_path)) + return File.ReadAllText(m_path); + if (initialDocument is null) + return string.Empty; + try { - byte[] bytes = Encoding.UTF8.GetBytes(document); - stream.Write(bytes); - stream.Flush(flushToDisk: true); + AtomicFile.WriteAllBytes(m_path, Encoding.UTF8.GetBytes(initialDocument), overwrite: false); } - File.Move(temporaryPath, m_path, overwrite: true); - m_lastSavedDocument = document; + catch (IOException) when (File.Exists(m_path)) + { + // A concurrent creator owns the completed document; never overwrite its settings. + } + return File.ReadAllText(m_path); } private static void Parse( diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs index c5ebc26bf..b0cb47e6b 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Hosting/EditorHost.cs @@ -435,7 +435,7 @@ private void Initialize() presentation, reloadCoordinator), static service => service.Dispose()); - var editorContext = new EditorContext(projectDirectory, m_launch.keyboard); + var editorContext = new EditorContext(projectDirectory, m_launch.keyboard, EditorDefaultLayout.document); presentation.SetLayoutFile(null); presentation.LoadLayout(editorContext.imguiLayout); var playOptions = new RuntimeSessionOptions diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj b/src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj index f0289ea69..2edc0305c 100644 --- a/src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Inno.Editor.Hosting.csproj @@ -61,4 +61,7 @@ + + + diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/DefaultEditorLayout.ini b/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/DefaultEditorLayout.ini new file mode 100644 index 000000000..6dd4d9f52 --- /dev/null +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/DefaultEditorLayout.ini @@ -0,0 +1,75 @@ +[Window][WindowOverViewport_11111111] +Pos=0,31 +Size=1600,868 +Collapsed=0 + +[Window][Hierarchy] +Pos=731,31 +Size=430,868 +Collapsed=0 +DockId=0x0000000B,0 + +[Window][Inspector] +Pos=1164,31 +Size=435,527 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Scene] +Pos=0,31 +Size=401,371 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Game] +Pos=403,31 +Size=326,371 +Collapsed=0 +DockId=0x00000004,0 + +[Window][Shader Editor] +Pos=1164,31 +Size=435,527 +Collapsed=0 +DockId=0x00000005,1 + +[Window][File] +Pos=0,404 +Size=729,495 +Collapsed=0 +DockId=0x00000008,0 + +[Window][Console] +Pos=1164,560 +Size=435,338 +Collapsed=0 +DockId=0x00000006,1 + +[Window][Stats] +Pos=1164,560 +Size=435,338 +Collapsed=0 +DockId=0x00000006,0 + +[Docking][Data] +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,31 Size=1600,868 Split=X + DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=729,868 Split=Y Selected=0x39CACFEF + DockNode ID=0x00000007 Parent=0x00000001 SizeRef=661,371 Split=X Selected=0xE601B12F + DockNode ID=0x00000003 Parent=0x00000007 SizeRef=401,371 Selected=0xE601B12F + DockNode ID=0x00000004 Parent=0x00000007 SizeRef=326,371 Selected=0xD1EB2482 + DockNode ID=0x00000008 Parent=0x00000001 SizeRef=661,495 Selected=0x39CACFEF + DockNode ID=0x00000002 Parent=0x08BD597D SizeRef=868,868 Split=X Selected=0x36DC96AB + DockNode ID=0x0000000B Parent=0x00000002 SizeRef=430,868 Selected=0xBABDAE5E + DockNode ID=0x0000000C Parent=0x00000002 SizeRef=435,868 Split=Y Selected=0x36DC96AB + DockNode ID=0x00000005 Parent=0x0000000C SizeRef=562,527 CentralNode=1 Selected=0x36DC96AB + DockNode ID=0x00000006 Parent=0x0000000C SizeRef=562,338 Selected=0x590DF097 + +[InnoEditor][Panels] +asset.file-browser=true +diagnostics.console=true +diagnostics.stats=true +rendering.game-view=true +rendering.scene-view=true +rendering.shader-editor=true +scene.hierarchy=true +scene.inspector=true diff --git a/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/EditorDefaultLayout.cs b/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/EditorDefaultLayout.cs new file mode 100644 index 000000000..dfd2e0adf --- /dev/null +++ b/src/composition/editor/hosting/Inno.Editor.Hosting/Layout/EditorDefaultLayout.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Inno.Editor.Hosting; + +internal static class EditorDefaultLayout +{ + private const string C_RESOURCE_NAME = "Inno.Editor.Hosting.DefaultEditorLayout.ini"; + + internal static string document { get; } = ReadDocument(); + + private static string ReadDocument() + { + using Stream stream = typeof(EditorDefaultLayout).Assembly.GetManifestResourceStream(C_RESOURCE_NAME) + ?? throw new InvalidOperationException("The standard Editor layout resource is missing."); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/DefaultLayoutNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/DefaultLayoutNativeTests.cs new file mode 100644 index 000000000..be061f641 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/DefaultLayoutNativeTests.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Inno.Adapter.Modules.DotNet; +using Inno.Core.Input; +using Inno.Core.Logging; +using Inno.Editor.Core; +using Inno.Editor.ImGui; +using Inno.Extensibility.Catalogs; +using Inno.Extensibility.Modules; +using Inno.Extensibility.Types; +using Inno.Native.ImGui; +using Xunit; +using NativeGui = Inno.Native.ImGui.ImGui; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed unsafe class DefaultLayoutNativeTests +{ + [Theory] + [InlineData(1280f, 720f)] + [InlineData(800f, 600f)] + [Trait("Acceptance", "Visual")] + public void StandardDockTreeLoadsAndResizesThroughThePresentationRuntime( + float width, + float height + ) { + using var host = new NativePanelHost(); + using var modules = new ModuleHost(new() { catalogSource = new DotNetAssemblyCatalogSource(typeof(DefaultLayoutNativeTests).Assembly) }); + using var types = new TypeCatalog(modules, new EmptyTypeSource()); + using var logs = new LogRouter(); + var context = new EditorContext(host.runtime.context.projectDirectory, new EditorKeyboardPolicy(KeyModifier.Control, "Ctrl")); + using var runtime = new ImGuiEditorRuntime(context, types, logs, []); + ImGuiIO* io = NativeGui.Raw.GetIO(); + io->ConfigFlags |= (int)ImGuiConfigFlags.DockingEnable; + io->DisplaySize = Unsafe.BitCast(new(1600f, 900f)); + byte[] layout = Encoding.UTF8.GetBytes(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "DefaultEditorLayout.ini")) + '\0'); + fixed (byte* pointer = layout) + NativeGui.Raw.LoadIniSettingsFromMemory((sbyte*)pointer, (ulong)(layout.Length - 1)); + runtime.Start(); + string[] titles = ["Scene", "Game", "File", "Hierarchy", "Inspector", "Shader Editor", "Console", "Stats"]; + var rectangles = new Dictionary(); + for (int frame = 0; frame < 12; frame++) + { + if (frame == 6) + io->DisplaySize = Unsafe.BitCast(new(width, height)); + host.Frame(() => + { + runtime.Draw(); + foreach (string title in titles) + { + bool open = true; + _ = NativeGui.Begin(title, ref open, 0); + try + { + rectangles[title] = (NativeGui.GetWindowPos(), NativeGui.GetWindowSize(), NativeGui.GetWindowDockID()); + } + finally + { + NativeGui.End(); + } + } + }); + } + foreach (var rectangle in rectangles.Values) + { + Assert.NotEqual(0u, rectangle.dock); + Assert.True(rectangle.size.X > 0f && rectangle.size.Y > 0f); + Assert.InRange(rectangle.position.X, -1f, width); + Assert.InRange(rectangle.position.Y, -1f, height); + Assert.InRange(rectangle.position.X + rectangle.size.X, 0f, width + 1f); + Assert.InRange(rectangle.position.Y + rectangle.size.Y, 0f, height + 1f); + } + Assert.Equal(rectangles["Inspector"].dock, rectangles["Shader Editor"].dock); + Assert.Equal(rectangles["Console"].dock, rectangles["Stats"].dock); + Assert.True(rectangles["Scene"].position.X < rectangles["Game"].position.X); + Assert.True(rectangles["File"].position.Y > rectangles["Scene"].position.Y); + Assert.True(rectangles["Hierarchy"].position.X > rectangles["File"].position.X); + Assert.True(rectangles["Inspector"].position.X > rectangles["Hierarchy"].position.X); + Assert.True(rectangles["Console"].position.Y > rectangles["Inspector"].position.Y); + Assert.InRange(rectangles["File"].size.X / width, 0.43f, 0.49f); + Assert.InRange(rectangles["Hierarchy"].size.X / width, 0.25f, 0.30f); + Assert.InRange(rectangles["Inspector"].size.X / width, 0.25f, 0.30f); + } + + private sealed class EmptyTypeSource : ITypeCatalogSource + { + public IReadOnlyList GetTypes(Assembly assembly) => []; + public TypeCatalogMetadata GetMetadata(Type type) => throw new NotSupportedException(); + public Type? ConstructGenericType( + Type definition, + IReadOnlyList arguments + ) => throw new NotSupportedException(); + public bool CanCreateInstance(Type type) => false; + public object CreateInstance(Type type) => throw new NotSupportedException(); + } +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/EditorLayoutInitializationTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/EditorLayoutInitializationTests.cs new file mode 100644 index 000000000..6ba5a874a --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/EditorLayoutInitializationTests.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Inno.Core.IO; +using Inno.Core.Input; +using Inno.Editor.Core; +using Xunit; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed class EditorLayoutInitializationTests : IDisposable +{ + private readonly string m_owner = Path.Combine(Path.GetTempPath(), "InnoInitialLayoutTests"); + private readonly string m_root; + + public EditorLayoutInitializationTests() + { + m_root = PathBoundary.Resolve(m_owner, Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(m_root); + } + + [Fact] + public void MissingProjectDocumentReceivesOnlyTheStandardLayout() + { + string template = ReadTemplate(); + EditorContext context = CreateContext(template); + Assert.Equal(template, File.ReadAllText(context.layoutPath)); + Assert.False(context.SaveLayoutIfChanged()); + Assert.Equal(["Panels"], context.GetLayoutSectionNames()); + Assert.True(context.TryGetLayoutSection("Panels", out var panels)); + Assert.Equal(8, panels.Count); + Assert.All(panels.Values, value => Assert.Equal("true", value)); + Assert.DoesNotContain("Module.", template, StringComparison.Ordinal); + Assert.DoesNotContain("FlappyBird", template, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain("Sprite Animation", template, StringComparison.Ordinal); + Assert.DoesNotContain("Compiling Scripts", template, StringComparison.Ordinal); + Assert.DoesNotContain("Export as", template, StringComparison.Ordinal); + Assert.DoesNotContain("Debug##", template, StringComparison.Ordinal); + } + + [Theory] + [InlineData("")] + [InlineData("[Window][User]\nSize=123,456\n\n[InnoEditor][Panels]\nscene.hierarchy=false\n")] + public void ExistingDocumentIsAuthoritativeIncludingAnIntentionallyEmptyFile(string document) + { + string path = Path.Combine(m_root, "editor.ini"); + File.WriteAllText(path, document); + DateTime timestamp = new(2020, 2, 3, 4, 5, 6, DateTimeKind.Utc); + File.SetLastWriteTimeUtc(path, timestamp); + byte[] bytes = File.ReadAllBytes(path); + EditorContext context = CreateContext(ReadTemplate()); + Assert.Equal(bytes, File.ReadAllBytes(path)); + Assert.Equal(timestamp, File.GetLastWriteTimeUtc(path)); + Assert.False(context.SaveLayoutIfChanged()); + Assert.Equal(bytes, File.ReadAllBytes(path)); + } + + [Fact] + public void ConcurrentInitializersNeverReplaceTheWinningDocument() + { + var layouts = new ConcurrentBag(); + Parallel.For(0, 16, index => + { + string document = $"[Window][Creator {index}]\nSize=400,300\n"; + layouts.Add(CreateContext(document).imguiLayout); + }); + string winner = CreateContext(null).imguiLayout; + Assert.All(layouts, value => Assert.Equal(winner, value)); + Assert.Single(Directory.GetFiles(m_root)); + } + + [Fact] + public void FailedCreationDoesNotLeaveAPartialFileOrStagingPayload() + { + Directory.CreateDirectory(Path.Combine(m_root, "editor.ini")); + Assert.ThrowsAny(() => CreateContext(ReadTemplate())); + Assert.Empty(Directory.GetFiles(m_root)); + } + + [Fact] + public void OrdinarySaveAndReopenRetainLayoutAndIndependentStateSections() + { + EditorContext context = CreateContext(ReadTemplate()); + context.SetLayoutSection("Module.fixture", new Dictionary { ["value"] = "42" }); + context.SetImGuiLayout("[Window][User layout]\nPos=10,20\nSize=600,400\n"); + context.SaveLayout(); + byte[] saved = File.ReadAllBytes(context.layoutPath); + EditorContext reopened = CreateContext(ReadTemplate()); + Assert.Contains("[Window][User layout]", reopened.imguiLayout); + Assert.True(reopened.TryGetLayoutSection("Module.fixture", out var values)); + Assert.Equal("42", values["value"]); + Assert.True(reopened.TryGetLayoutSection("Panels", out var panels)); + Assert.Equal(8, panels.Count); + Assert.Equal(saved, File.ReadAllBytes(reopened.layoutPath)); + Assert.Single(Directory.GetFiles(m_root)); + } + + [Fact] + public void AContextWithoutACompositionDefaultRemainsPassive() + { + EditorContext context = CreateContext(null); + Assert.False(File.Exists(context.layoutPath)); + Assert.Empty(context.imguiLayout); + Assert.Empty(context.GetLayoutSectionNames()); + } + + public void Dispose() => Directory.Delete(PathBoundary.RequireUnlinkedPath(m_owner, m_root), recursive: true); + + private EditorContext CreateContext(string? initialDocument) => new( + m_root, new EditorKeyboardPolicy(KeyModifier.Control, "Ctrl"), initialDocument); + + private static string ReadTemplate() => File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "DefaultEditorLayout.ini")); +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj index 5acb9930f..cf91e7d99 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj +++ b/tests/editor/Inno.Editor.PlayMode.Tests/Inno.Editor.PlayMode.Tests.csproj @@ -32,6 +32,11 @@ $(InnoNativeTarget) + + + + Date: Thu, 29 Oct 2026 05:31:04 +0800 Subject: [PATCH 27/54] fix(io): publish complete files without replacing concurrent creators --- docs/core/Inno.Core.IO.md | 4 + ..._FILE_PUBLICATION_ACCEPTANCE_2026_10_29.md | 44 ++++ .../core/Inno.Core.IO/AtomicFile.cs | 20 ++ .../core/Inno.Core.IO/FileSystemRename.cs | 11 +- .../core/Inno.Core.IO/Inno.Core.IO.csproj | 1 + .../core/Inno.Core.IO/PosixFilePublication.cs | 57 +++++ .../AtomicFilePublicationTests.cs | 197 ++++++++++++++++++ 7 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 records/architecture/acceptance/ATOMIC_FILE_PUBLICATION_ACCEPTANCE_2026_10_29.md create mode 100644 src/foundation/core/Inno.Core.IO/PosixFilePublication.cs create mode 100644 tests/core/Inno.Core.IO.Tests/AtomicFilePublicationTests.cs diff --git a/docs/core/Inno.Core.IO.md b/docs/core/Inno.Core.IO.md index b9ee0693a..475d72b51 100644 --- a/docs/core/Inno.Core.IO.md +++ b/docs/core/Inno.Core.IO.md @@ -14,6 +14,10 @@ `IByteDocumentStore` 仅用于小型文档。`FileByteDocumentStore` 复用 AtomicFile;`ReadOnlyByteDocumentStore` 捕获 owned bytes,写入明确失败。它不是 Asset catalog 或事件协议。 +`AtomicFile` 只发布已经完整写入的同目录候选。`overwrite: false` 的冲突检查和发布必须是同一个文件系统操作,不能先检查不存在再 rename。Windows 使用系统的不可覆盖移动;Unix 使用公开 POSIX `link` 原语原子创建目标,再退休候选,不调用 SDK 私有导出。此处只选择宿主文件系统原语,不选择引擎 target、backend 或产品平台。Unix 文件系统必须支持同目录 hard link;不支持时明确抛出 `IOException`,不回退成可能暴露部分内容的复制。不能提供该宿主原语的执行环境明确抛出 `PlatformNotSupportedException`,其 inner exception 保留能力缺失原因;不维护引擎平台名单。普通原子替换不受影响。 + +`Install` 在发布失败时保留调用方候选;发布成功后移除候选。若目标已经发布而候选删除失败,异常明确说明目标完整存在,调用方不能重试覆盖他人的目标。`WriteAllBytes` 自己拥有 staging 并负责在退出时清理。空的现有文档也属于已有目标,不能因为长度为零而覆盖。父目录和候选仍需要调用方的所有权保护;原子发布不抵御非合作进程替换整棵父目录。 + `AtomicDirectory.Publish` 只向不存在的目标发布完整 staging。需要替换目录时使用 `Install`,由调用方先协调全部读写 owner;这两个原子目录入口不会自行取得读取 lease。提交后的旧目录清理失败与提交前失败具有不同结果,不能把清理异常解释为新产物未安装。 `DirectoryCleanupException` 只表示提交后的 backup 删除失败;`installedDirectory` 指向已经完整安装的新目录,`backupDirectory` 指向保留的旧目录。消费方记录独立清理诊断并继续使用已提交输出,不重试安装、不回滚新目录。其他 IO 异常仍表示安装阶段失败,遵循原来的恢复语义。 diff --git a/records/architecture/acceptance/ATOMIC_FILE_PUBLICATION_ACCEPTANCE_2026_10_29.md b/records/architecture/acceptance/ATOMIC_FILE_PUBLICATION_ACCEPTANCE_2026_10_29.md new file mode 100644 index 000000000..b44c18842 --- /dev/null +++ b/records/architecture/acceptance/ATOMIC_FILE_PUBLICATION_ACCEPTANCE_2026_10_29.md @@ -0,0 +1,44 @@ +# Atomic create-only file publication + +Tracking: [#48](https://github.com/Wolfiora/InnoEngine/issues/48), [#47](https://github.com/Wolfiora/InnoEngine/issues/47), [#40](https://github.com/Wolfiora/InnoEngine/issues/40), [parent #27](https://github.com/Wolfiora/InnoEngine/issues/27). + +## Confirmed failures + +At `bb5b4bb9`, the complete Windows and macOS CI suites rejected the output-owner fixture's hard-coded `bin/Fixture/debug` path. The SDK builds succeeded. Product-aware `ArtifactsProjectName` now participates in the actual output owner, so the fixture must inspect SDK outputs rather than restore an obsolete path convention. + +The same macOS ARM64 run reproduced a separate production defect: competing new-project layout initializers observed different winning documents. Core `AtomicFile` delegated create-only installation to `File.Move(..., overwrite: false)`. The exact [.NET 10.0.13 Unix implementation](https://github.com/dotnet/runtime/blob/v10.0.13/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs#L193-L214) can check destination absence and then perform a replacing rename. That sequence cannot satisfy Core's stronger atomic no-clobber promise. + +This is not a BGCS failure. No SDK, package, BGCS, generated binding or vendor file was modified. + +## Correct ownership and implementation + +- Core IO retains its existing portable atomic publication responsibility. Editor, asset creation and document consumers all receive the correction through the same API. +- Windows retains no-replace file movement. Other hosts must provide the public POSIX `link` filesystem primitive: a complete same-directory candidate gains its destination name atomically, then the candidate name retires. +- Native host details remain private to Core IO. There is no engine platform ID, backend, target, product routing table, new provider registry or Editor-specific lock manager. The existing platform ownership rule already distinguishes portable Core IO primitives from product policy. +- An existing file, empty file, directory or competing creator cannot be replaced in create-only mode. Losing `Install` candidates remain caller-owned. `WriteAllBytes` owns its staging file and cleans it on exit. +- Missing host primitives fail with `PlatformNotSupportedException` and their original cause. Unsupported filesystem operations fail with `IOException`; there is no copy fallback exposing partial destination content. +- Failure to retire a candidate after successful linking explicitly reports that the complete target has already been published. Parent and candidate ownership remain the caller's responsibility. +- The SDK fixture now reads `TargetPath`, `ProjectAssetsFile` and the actual `IntermediateAssembly` item through the SDK's structured result output. It checks existence, containment and disjoint owners while retaining two concurrent real SDK builds. + +## Verification + +Evidence owner: `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`. + +| Gate | Result | Evidence and limits | +| --- | --- | --- | +| Core IO, Windows Release | 31 passed | `e48/io-publication-capability.trx`; final capability-based correction, no analyzer suppression | +| Simultaneous candidates | Passed within Core IO suite | 12 rounds, 16 independent publishing threads per round, exactly one complete winner, losing candidates preserved | +| Independent processes and reader | Passed within Core IO suite | Eight actual .NET publishing processes, public atomic API, 1 MiB distinct payloads, complete repeated reads, exactly one success, no staging leftovers | +| Existing owners and replacement | Passed within Core IO suite | Empty/custom destination, directory collision, normal replacement, parent boundary and existing filesystem safety contracts | +| Actual SDK output-owner fixture | Passed, Windows Release | `e40/sdk-owner-executed.trx`; two concurrent real SDK builds, actual output/assets/intermediate files, containment and disjoint owners | +| Real new-project layout concurrency | Pending final candidate | Earlier Windows passes do not substitute for the final source or actual ARM64 CI | +| macOS ARM64 | Pending exact-head CI | No local Mac; the observed red CI result is retained and will only be superseded by a real successful remote job | +| Architecture / Wiki | Pending final candidate | An intermediate explicit Browser check was rejected by the existing shared-layer policy and removed; capability absence now comes from the actual native boundary without a product-platform branch or validator exception | + +An intermediate Task runtime build correctly rejected changed Core IO source during compilation. That failed preparation was not cached. The source is frozen for the replacement build; no input check or publication guarantee was relaxed. + +Two SDK fixture preparation failures are retained: the first omitted the structured-result parent directory; the second omitted the explicit Build target, so SDK get* switches performed evaluation only. The final request explicitly executes Build and reads its resulting values. Neither attempt is counted as a successful build or a production defect. + +## Deep self-review + +The fix acts at the authoritative filesystem operation, not at the Editor symptom. It does not introduce a second IO/lease protocol, weaken atomic visibility, guess package/output locations or expand the public API. Independent process contention exercises a real public boundary. The Unix implementation still requires actual ARM64 CI; Windows success cannot certify it. Issues remain Open until complete verification and merge through PR #26. diff --git a/src/foundation/core/Inno.Core.IO/AtomicFile.cs b/src/foundation/core/Inno.Core.IO/AtomicFile.cs index 0f842acc9..77810c182 100644 --- a/src/foundation/core/Inno.Core.IO/AtomicFile.cs +++ b/src/foundation/core/Inno.Core.IO/AtomicFile.cs @@ -20,6 +20,13 @@ public static class AtomicFile /// /// Whether an existing destination may be replaced; false atomically rejects name collisions. /// + /// + /// Publication or cleanup fails, the destination already exists in create-only mode, or the + /// filesystem cannot atomically publish a complete file. No copy fallback exposes partial content. + /// + /// + /// The host does not provide an atomic create-only filesystem primitive. + /// public static void WriteAllBytes( string path, ReadOnlySpan data, @@ -61,6 +68,19 @@ public static void WriteAllBytes( /// /// Whether an existing destination may be replaced; false never overwrites an external creator. /// + /// + /// A failed create-only publication preserves the candidate. After successful publication the + /// candidate is removed; a subsequent cleanup failure reports that the complete target exists. + /// Unix create-only publication requires same-directory hard-link support. The caller must own + /// the candidate and parent directory until the operation completes. + /// + /// + /// Publication fails, create-only mode finds an existing destination, or candidate retirement + /// fails after the complete destination has been published. + /// + /// + /// The host does not provide an atomic create-only filesystem primitive. + /// public static void Install( string source, string destination, diff --git a/src/foundation/core/Inno.Core.IO/FileSystemRename.cs b/src/foundation/core/Inno.Core.IO/FileSystemRename.cs index a61c8adcf..0f9f6d20f 100644 --- a/src/foundation/core/Inno.Core.IO/FileSystemRename.cs +++ b/src/foundation/core/Inno.Core.IO/FileSystemRename.cs @@ -20,7 +20,16 @@ internal static void MoveFile( string source, string destination, bool overwrite - ) => Execute(() => File.Move(source, destination, overwrite), default); + ) { + if (overwrite || OperatingSystem.IsWindows()) + { + Execute(() => File.Move(source, destination, overwrite), default); + return; + } + // The SDK's Unix Move(false) can check absence and then replace a competing creator. + // POSIX link creates the complete destination in one no-clobber filesystem operation. + PosixFilePublication.Install(source, destination); + } private static void Execute( Action rename, diff --git a/src/foundation/core/Inno.Core.IO/Inno.Core.IO.csproj b/src/foundation/core/Inno.Core.IO/Inno.Core.IO.csproj index cb3f5bcc1..c6e2f6885 100644 --- a/src/foundation/core/Inno.Core.IO/Inno.Core.IO.csproj +++ b/src/foundation/core/Inno.Core.IO/Inno.Core.IO.csproj @@ -4,6 +4,7 @@ 14.0 disable enable + true true $(WarningsAsErrors);CS1572;CS1573;CS1591 diff --git a/src/foundation/core/Inno.Core.IO/PosixFilePublication.cs b/src/foundation/core/Inno.Core.IO/PosixFilePublication.cs new file mode 100644 index 000000000..517daad74 --- /dev/null +++ b/src/foundation/core/Inno.Core.IO/PosixFilePublication.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; + +namespace Inno.Core.IO; + +internal static partial class PosixFilePublication +{ + internal static void Install( + string source, + string destination + ) { + int result; + try + { + result = Link(source, destination); + } + catch (Exception exception) when (exception is DllNotFoundException or EntryPointNotFoundException or PlatformNotSupportedException) + { + throw new PlatformNotSupportedException("The host does not provide atomic create-only file publication through POSIX link.", exception); + } + if (result != 0) + { + int error = Marshal.GetLastPInvokeError(); + throw new IOException( + $"Atomic create-only publication of '{source}' at '{destination}' failed (errno {error}). " + + "The destination was not replaced; the filesystem must support same-directory hard links.", + new Win32Exception(error)); + } + + try + { + File.Delete(source); + } + catch (IOException exception) + { + throw CleanupFailure(source, destination, exception); + } + catch (UnauthorizedAccessException exception) + { + throw CleanupFailure(source, destination, exception); + } + } + + private static IOException CleanupFailure( + string source, + string destination, + Exception exception + ) => new($"The complete file was published at '{destination}', but its candidate '{source}' could not be retired.", exception); + + [LibraryImport("libc", EntryPoint = "link", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] + private static partial int Link( + string source, + string destination + ); +} diff --git a/tests/core/Inno.Core.IO.Tests/AtomicFilePublicationTests.cs b/tests/core/Inno.Core.IO.Tests/AtomicFilePublicationTests.cs new file mode 100644 index 000000000..0c5b425a4 --- /dev/null +++ b/tests/core/Inno.Core.IO.Tests/AtomicFilePublicationTests.cs @@ -0,0 +1,197 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; + +using Xunit; + +namespace Inno.Core.IO.Tests; + +public sealed class AtomicFilePublicationTests : IDisposable +{ + private readonly string m_root = Path.Combine(Path.GetTempPath(), "InnoAtomicPublication", Guid.NewGuid().ToString("N")); + + public AtomicFilePublicationTests() => Directory.CreateDirectory(m_root); + + [Theory] + [InlineData("")] + [InlineData("external document")] + public void CreateOnlyPreservesAnExistingFileAndTheLosingCandidate(string document) + { + string destination = Path.Combine(m_root, "document.ini"); + string candidate = Path.Combine(m_root, "candidate"); + File.WriteAllText(destination, document); + File.WriteAllText(candidate, "candidate"); + byte[] original = File.ReadAllBytes(destination); + + Assert.ThrowsAny(() => AtomicFile.Install(candidate, destination, overwrite: false)); + Assert.Equal(original, File.ReadAllBytes(destination)); + Assert.Equal("candidate", File.ReadAllText(candidate)); + Assert.ThrowsAny(() => AtomicFile.WriteAllBytes(destination, "replacement"u8, overwrite: false)); + Assert.Equal(original, File.ReadAllBytes(destination)); + Assert.Equal(2, Directory.GetFiles(m_root).Length); + } + + [Fact] + public void CreateOnlyRejectsAnExistingDirectoryWithoutConsumingTheCandidate() + { + string destination = Path.Combine(m_root, "directory"); + string candidate = Path.Combine(m_root, "candidate"); + Directory.CreateDirectory(destination); + File.WriteAllText(candidate, "complete"); + Exception? failure = Record.Exception(() => AtomicFile.Install(candidate, destination, overwrite: false)); + Assert.True(failure is IOException or UnauthorizedAccessException, failure?.ToString()); + Assert.Equal("complete", File.ReadAllText(candidate)); + Assert.Empty(Directory.GetFileSystemEntries(destination)); + } + + [Fact] + public async Task ConcurrentCandidatesPublishExactlyOneCompleteFile() + { + const int count = 16; + for (int round = 0; round < 12; round++) + { + string destination = Path.Combine(m_root, "document-" + round); + string[] candidates = Enumerable.Range(0, count).Select(index => destination + ".candidate-" + index).ToArray(); + foreach (string candidate in candidates) + File.WriteAllBytes(candidate, Enumerable.Repeat((byte)Array.IndexOf(candidates, candidate), 64 * 1024).ToArray()); + int winner = -1; + int successes = 0; + using var ready = new CountdownEvent(count); + using var start = new ManualResetEventSlim(); + Task[] writers = Enumerable.Range(0, count).Select(index => Task.Factory.StartNew(() => + { + ready.Signal(); + start.Wait(); + try + { + AtomicFile.Install(candidates[index], destination, overwrite: false); + Interlocked.Exchange(ref winner, index); + Interlocked.Increment(ref successes); + } + catch (IOException) when (File.Exists(destination)) + { + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray(); + bool prepared = ready.Wait(TimeSpan.FromSeconds(20)); + start.Set(); + await Task.WhenAll(writers).WaitAsync(TimeSpan.FromSeconds(20)); + Assert.True(prepared, "All candidate publishers must reach the start barrier."); + Assert.Equal(1, successes); + byte[] bytes = File.ReadAllBytes(destination); + Assert.Equal(64 * 1024, bytes.Length); + Assert.All(bytes, value => Assert.Equal((byte)winner, value)); + for (int index = 0; index < count; index++) + Assert.Equal(index != winner, File.Exists(candidates[index])); + } + } + + [Fact] + public async Task IndependentCreatorsAndAReaderObserveOnlyTheCompleteWinningPayload() + { + string project = Path.Combine(m_root, "Publisher.csproj"); + new XDocument(new XElement("Project", new XAttribute("Sdk", "Microsoft.NET.Sdk"), + new XElement("PropertyGroup", new XElement("TargetFramework", "net10.0"), new XElement("OutputType", "Exe"), + new XElement("ImplicitUsings", "disable")), + new XElement("ItemGroup", new XElement("Reference", new XAttribute("Include", "Inno.Core.IO"), + new XElement("HintPath", typeof(AtomicFile).Assembly.Location))))).Save(project); + File.WriteAllText(Path.Combine(m_root, "Program.cs"), """ + using System; + using System.IO; + using System.Threading; + using Inno.Core.IO; + byte[] payload = new byte[1024 * 1024]; + Array.Fill(payload, byte.Parse(args[2])); + File.WriteAllText(args[1] + ".ready-" + args[2], "ready"); + long deadline = Environment.TickCount64 + 20000; + while (!File.Exists(args[1])) + { + if (Environment.TickCount64 > deadline) return 2; + Thread.Sleep(1); + } + try { AtomicFile.WriteAllBytes(args[0], payload, overwrite: false); return 0; } + catch (IOException) when (File.Exists(args[0])) { return 17; } + """); + using (Process build = Start("build", project, "--disable-build-servers", "-m:1", "-nodeReuse:false", "-v:quiet")) + { + Task output = build.StandardOutput.ReadToEndAsync(); + Task error = build.StandardError.ReadToEndAsync(); + try + { + await build.WaitForExitAsync().WaitAsync(TimeSpan.FromMinutes(2)); + } + finally + { + if (!build.HasExited) + { + build.Kill(entireProcessTree: true); + await build.WaitForExitAsync(); + } + } + Assert.True(build.ExitCode == 0, await output + await error); + } + string destination = Path.Combine(m_root, "concurrent.ini"); + string gate = Path.Combine(m_root, "start"); + string assembly = Path.Combine(m_root, "bin", "Debug", "net10.0", "Publisher.dll"); + Process[] creators = Enumerable.Range(1, 8).Select(index => Start(assembly, destination, gate, index.ToString())).ToArray(); + try + { + await WaitUntil(() => Directory.GetFiles(m_root, "start.ready-*").Length == creators.Length); + File.WriteAllText(gate, "start"); + await WaitUntil(() => File.Exists(destination)); + byte[] winner = File.ReadAllBytes(destination); + Assert.Equal(1024 * 1024, winner.Length); + Assert.InRange(winner[0], (byte)1, (byte)8); + Assert.All(winner, value => Assert.Equal(winner[0], value)); + for (int read = 0; read < 16; read++) + Assert.Equal(winner, File.ReadAllBytes(destination)); + await Task.WhenAll(creators.Select(process => process.WaitForExitAsync())).WaitAsync(TimeSpan.FromSeconds(30)); + Assert.Single(creators.Where(process => process.ExitCode == 0)); + Assert.All(creators, process => Assert.True(process.ExitCode is 0 or 17)); + Assert.Empty(Directory.GetFiles(m_root, "*.staging-*")); + } + finally + { + foreach (Process process in creators) + { + if (!process.HasExited) + process.Kill(entireProcessTree: true); + await process.WaitForExitAsync(); + process.Dispose(); + } + } + } + + public void Dispose() => Directory.Delete(m_root, recursive: true); + + private Process Start(params string[] arguments) + { + string sdk = Path.Combine(Path.GetFullPath("../../..", RuntimeEnvironment.GetRuntimeDirectory()), + OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); + var start = new ProcessStartInfo(sdk) + { + WorkingDirectory = m_root, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + foreach (string argument in arguments) + start.ArgumentList.Add(argument); + return Process.Start(start) ?? throw new InvalidOperationException("The SDK fixture did not start."); + } + + private static async Task WaitUntil(Func condition) + { + long deadline = Environment.TickCount64 + 20000; + while (!condition()) + { + Assert.True(Environment.TickCount64 < deadline, "The independent publishers did not reach their owned barrier."); + await Task.Delay(5); + } + } +} From a38437f7d27307aadcf964f76e9228256d26185f Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Thu, 29 Oct 2026 05:31:05 +0800 Subject: [PATCH 28/54] test(build): verify output ownership through executed SDK results --- .../BuildOutputOwnershipTests.cs | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/tests/build/Inno.Build.Tests/BuildOutputOwnershipTests.cs b/tests/build/Inno.Build.Tests/BuildOutputOwnershipTests.cs index d2273a094..3e5f9c5ff 100644 --- a/tests/build/Inno.Build.Tests/BuildOutputOwnershipTests.cs +++ b/tests/build/Inno.Build.Tests/BuildOutputOwnershipTests.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text.Json; using System.Threading.Tasks; using System.Xml.Linq; @@ -34,13 +35,18 @@ public async Task ConcurrentSdkRequestsKeepExplicitOutputOwnersForTheSameTarget( .Save(project); string first = Path.Combine(root, "requests", "first"); string second = Path.Combine(root, "requests", "second"); - await Task.WhenAll(Build(project, first), Build(project, second)); - foreach (string owner in new[] { first, second }) + string[][] outputs = await Task.WhenAll(Build(project, first), Build(project, second)); + for (int index = 0; index < outputs.Length; index++) { - Assert.True(File.Exists(Path.Combine(owner, "bin", "Fixture", "debug", "Fixture.dll"))); - Assert.True(File.Exists(Path.Combine(owner, "obj", "Fixture", "project.assets.json"))); - Assert.True(File.Exists(Path.Combine(owner, "obj", "Fixture", "debug", "Fixture.dll"))); + string owner = Path.GetFullPath(index == 0 ? first : second) + Path.DirectorySeparatorChar; + Assert.Equal(3, outputs[index].Length); + foreach (string path in outputs[index]) + { + Assert.StartsWith(owner, path, StringComparison.Ordinal); + Assert.True(File.Exists(path), $"The SDK-selected output does not exist: {path}"); + } } + Assert.All(outputs[0], path => Assert.DoesNotContain(path, outputs[1])); Assert.False(Directory.Exists(Path.Combine(root, "bin"))); Assert.False(Directory.Exists(Path.Combine(root, "obj"))); } @@ -50,7 +56,7 @@ public async Task ConcurrentSdkRequestsKeepExplicitOutputOwnersForTheSameTarget( } } - private static async Task Build( + private static async Task Build( string project, string outputOwner ) { @@ -61,14 +67,37 @@ string outputOwner WorkingDirectory = Path.GetDirectoryName(project)!, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; + string evaluated = Path.Combine(Path.GetDirectoryName(outputOwner)!, Path.GetFileName(outputOwner) + ".json"); + Directory.CreateDirectory(Path.GetDirectoryName(evaluated)!); foreach (string argument in new[] { "build", project, "--artifacts-path", outputOwner, "-p:InnoNativeTarget=fixture-target", "-p:InnoNativeBuildFingerprint=fixture-fingerprint", + "-target:Build", "-getProperty:TargetPath,ProjectAssetsFile", "-getItem:IntermediateAssembly", "-getResultOutputFile:" + evaluated, "--disable-build-servers", "-m:1", "-nodeReuse:false", "-v:minimal" }) start.ArgumentList.Add(argument); using Process process = Process.Start(start) ?? throw new InvalidOperationException("The SDK did not start."); Task output = process.StandardOutput.ReadToEndAsync(); Task error = process.StandardError.ReadToEndAsync(); - await process.WaitForExitAsync(); + try + { + await process.WaitForExitAsync().WaitAsync(TimeSpan.FromMinutes(2)); + } + finally + { + if (!process.HasExited) + { + process.Kill(entireProcessTree: true); + await process.WaitForExitAsync(); + } + } Assert.True(process.ExitCode == 0, await output + await error); + using JsonDocument result = JsonDocument.Parse(File.ReadAllText(evaluated)); + JsonElement properties = result.RootElement.GetProperty("Properties"); + JsonElement intermediate = Assert.Single(result.RootElement.GetProperty("Items").GetProperty("IntermediateAssembly").EnumerateArray()); + return + [ + Path.GetFullPath(properties.GetProperty("TargetPath").GetString()!), + Path.GetFullPath(properties.GetProperty("ProjectAssetsFile").GetString()!), + Path.GetFullPath(intermediate.GetProperty("FullPath").GetString()!) + ]; } } From 0b0a821c728a592ff18cde432c22e8d85b258d16 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Thu, 29 Oct 2026 06:28:38 +0800 Subject: [PATCH 29/54] perf(serialization): reuse exact neutral captures without retaining source generations --- AGENTS.md | 1 + docs/core/Inno.Core.Serialization.md | 28 ++ .../architecture/EDITOR_UI_CLOSURE_PLAN.md | 35 +- ...DITOR_UI_SNAPSHOT_ACCEPTANCE_2026_10_29.md | 71 +++++ .../EditorSceneWorkspace.Persistence.cs | 27 +- .../AssetDependencyCollection.cs | 10 +- .../Serialization/AssetObjectConverter.cs | 11 +- .../Converters/GameObjectConverter.cs | 3 +- .../Converters/GameSceneConverter.cs | 25 +- .../Serialization/PrefabOverrideProcessor.cs | 2 +- .../SceneGraph/SceneGraphReferenceMap.cs | 22 +- .../SceneGraph/SceneGraphSerialization.cs | 130 ++++---- .../ScenePropertySerialization.cs | 5 +- .../Serialization/SceneReloadStateTransfer.cs | 2 +- .../SceneSubtreeStateConverter.cs | 3 +- .../scene/Inno.Scene/Storage/SceneStore.cs | 7 +- .../Context/SerializationContext.cs | 33 +- .../IO/SerializationWriter.cs | 219 +++++++++++-- .../Internal/BinarySerializationFormat.cs | 10 + .../Internal/ConverterRegistry.cs | 16 +- .../Internal/PropertyDataCapture.cs | 9 + .../Internal/PropertySnapshotBinaryFormat.cs | 17 + .../Internal/PropertySnapshotPipeline.cs | 49 +++ .../Internal/SerializationArrayCapture.cs | 44 +++ .../Internal/SerializationNode.cs | 27 +- .../Internal/SerializationNodeReuse.cs | 65 ++++ .../Internal/SerializationOperation.cs | 28 +- .../Internal/ValuePipeline.cs | 133 +++++--- .../SerializationRegistry.cs | 63 +++- .../SerializationSnapshot.cs | 50 +++ tests/Invoke-EditorUiAcceptance.ps1 | 5 +- tests/Invoke-EditorUiProductJourney.ps1 | 94 ++++++ .../SerializationBehaviorTests.Efficiency.cs | 35 +- .../SerializationBehaviorTests.Encoding.cs | 8 +- .../SerializationBehaviorTests.Snapshots.cs | 299 ++++++++++++++++++ .../LargePanelPerformanceTests.cs | 112 ++++++- .../ListPanelKeyboardNativeTests.cs | 36 ++- .../NativePanelHost.cs | 15 +- .../NativeProjectPanelHost.cs | 2 + .../SceneDirtyCaptureTests.cs | 167 ++++++++++ .../editor-ui/WorkflowJourney.editor.cs.txt | 289 +++++++++++++++++ tests/fixtures/editor-ui/WorkflowState.cs.txt | 15 + .../ScriptingPipelineTests.UiLifecycle.cs | 58 ++++ .../ShaderEditorWorkflowTests.Performance.cs | 6 +- 44 files changed, 2059 insertions(+), 227 deletions(-) create mode 100644 records/architecture/acceptance/EDITOR_UI_SNAPSHOT_ACCEPTANCE_2026_10_29.md create mode 100644 src/foundation/core/Inno.Core.Serialization/Internal/PropertyDataCapture.cs create mode 100644 src/foundation/core/Inno.Core.Serialization/Internal/SerializationArrayCapture.cs create mode 100644 src/foundation/core/Inno.Core.Serialization/Internal/SerializationNodeReuse.cs create mode 100644 src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs create mode 100644 tests/Invoke-EditorUiProductJourney.ps1 create mode 100644 tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Snapshots.cs create mode 100644 tests/editor/Inno.Editor.PlayMode.Tests/SceneDirtyCaptureTests.cs create mode 100644 tests/fixtures/editor-ui/WorkflowJourney.editor.cs.txt create mode 100644 tests/fixtures/editor-ui/WorkflowState.cs.txt diff --git a/AGENTS.md b/AGENTS.md index d3e5001c2..509cd4a82 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -311,6 +311,7 @@ public void AAA( - 检查新增或修改的基础能力是否能够直接复用 Core IO、Events、Identity、Serialization 等既有契约;在不破坏领域边界的前提下消除重复协议、平行管理器和重复实现。 - 通过真实调用方、可替换边界及适用验证确认扩展能力。新增 backend 或平台时需要修改哪些 owner,必须能够从实际代码说明;不得用未执行的测试或抽象声明代替证据。 - 每次最终交付必须简要说明上述自查结果、实际验证证据及尚未解决或无法执行的事项。没有完成验证时明确说明,不得宣称已经达到完美或全部通过。 +- 每次准备结束执行前,逐项复核本轮 issue 的实际解决条件、获批计划的功能与验收、架构清晰度、基础能力复用及新增回归。仍有可推进的工作时必须更新剩余执行计划并自动继续,不重复请求批准;仅在全部适用工作完成或明确记录无法独立解除的外部阻塞后交付,不能以阶段性进展或 CI 通过代替整个计划完成。 ## 31. Issue 与 Pull Request 交付规范 diff --git a/docs/core/Inno.Core.Serialization.md b/docs/core/Inno.Core.Serialization.md index 193835375..f42b75799 100644 --- a/docs/core/Inno.Core.Serialization.md +++ b/docs/core/Inno.Core.Serialization.md @@ -4,6 +4,14 @@ `Inno.Core.Serialization` 提供确定性的二进制对象图格式、基于 attribute 的属性持久化,以及通过 TypeRegistry 自动发现的 Converter。它既支持“序列化完整 `ISerializable` 根对象”,也支持用 Writer/Reader 显式定义 schema。 +### 完整状态捕获与复用 + +Host 可以使用 `CaptureSnapshot(value, context, previous)` 捕获不可变 `SerializationSnapshot`。每次仍完整执行当前 generation 的 getter 和 Converter;未变化的中立节点以 copy-on-write 复用,不依赖编辑通知或调用方上报 revision。`ToArray()` 使用同一二进制编码器返回独立 bytes,修改返回数组不会修改捕获结果。快照只持有中立标量、字符串和复制的二进制数据,不持有源对象、`Type`、Converter、delegate 或 generation lease,可在原 Registry 退休后编码。 + +该入口不导出到脚本。快照是 owner 所属的瞬时观察值,不是第二种文件格式,也不进入 History、Workspace 或资源元数据。比较失败时不发布新快照,旧快照保持有效。快照捕获期间,Converter 借用的 Writer 只属于其同步回调;不要在回调返回后继续写入。 + +`SerializationWriter.WritePropertiesData(name, value, context)` 将同一 generation 的独立属性快照写为现有 binary member,供 Scene 等需要保留独立恢复边界的 Converter 使用。它与 `CapturePropertiesData` 的 bytes 一致,并在重复捕获时复用未变化的属性节点和编码结果;传入的 context 必须保留 owner 的引用解析能力。普通序列化、保存与 Missing 恢复继续使用同一 pipeline,不建立领域专用 serializer。 + ## 初始化 Shader 等 Editor 创作回调可以借用宿主的 `SerializationRegistry`,脚本侧只导出数据编解码及属性读取。 @@ -609,6 +617,8 @@ static (float x, float y)[] RoundTripPoints( + + | 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | | --- | --- | --- | --- | | [`public Inno.Core.Serialization.SerializationRegistry.SerializationRegistry(Inno.Extensibility.Types.TypeCatalog types, Inno.Core.Serialization.ISerializationMetadataSource metadata)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L32) | Creates a serialization registry derived from one isolated type catalog.
`types`:The type catalog that owns converter extension generations.
`metadata`:The declaration access provider selected by the same composition root. | — | `System.ArgumentNullException`:Thrown when types is null. | @@ -628,6 +638,19 @@ static (float x, float y)[] RoundTripPoints( | [`public Inno.Core.Serialization.SerializationPropertyRestoreResult Inno.Core.Serialization.SerializationRegistry.RestorePropertiesData(Inno.Core.Serialization.ISerializable target, System.ReadOnlySpan data, Inno.Core.Serialization.SerializationPropertyRestoreMode mode = Inno.Core.Serialization.SerializationPropertyRestoreMode.Strict, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L324) | Restores one or more independently encoded persistent properties from neutral bytes.
返回:A summary containing restored, ignored, and skipped properties.
`target`:The existing object receiving the captured values.
`data`:Strict property snapshot bytes created by Inno.Core.Serialization.SerializationRegistry.CapturePropertyData(Inno.Core.Serialization.ISerializable,System.String,Inno.Core.Serialization.SerializationContext) or Inno.Core.Serialization.SerializationRegistry.CapturePropertiesData(Inno.Core.Serialization.ISerializable,Inno.Core.Serialization.SerializationContext).
`mode`:The failure policy for matching but incompatible current properties.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when target is null.
`System.ArgumentOutOfRangeException`:Thrown when mode is unknown.
`System.IO.InvalidDataException`:Thrown when data is malformed or unsupported.
`System.InvalidOperationException`:Thrown when strict restoration or an object-level callback fails. | | [`public byte[] Inno.Core.Serialization.SerializationRegistry.Serialize(T value, Inno.Core.Serialization.SerializationContext? context = null) where T : class, Inno.Core.Serialization.ISerializable`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L364) | Serializes a complete serializable root object into deterministic binary data.
返回:The encoded bytes.
`value`:The root object to serialize.
`context`:Optional immutable converter context. | — | `System.ArgumentNullException`:Thrown when value is null.
`System.InvalidOperationException`:Thrown when the manager is not initialized. | | [`public sealed class Inno.Core.Serialization.SerializationRegistry : System.IDisposable`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L12) | Owns one generation-aware converter registry and provides deterministic root serialization operations. | — | — | +| [`public Inno.Core.Serialization.SerializationSnapshot Inno.Core.Serialization.SerializationRegistry.CaptureSnapshot(T value, Inno.Core.Serialization.SerializationContext? context = null, Inno.Core.Serialization.SerializationSnapshot? previous = null) where T : class, Inno.Core.Serialization.ISerializable`](../../src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs#L395) | Captures complete serialized state while sharing unchanged neutral values with a previous capture.
The exact concrete root type, interpreted by the current converter generation.
`value`:The non-null root whose getters and converters are evaluated in full.
`context`:The complete owner context, including required reference resolvers.
`previous`:An optional immutable capture to reuse; it is never changed or retained by this registry.
返回:The previous instance when all captured values can be reused, otherwise a new immutable snapshot. Reference equality proves unchanged bytes; different instances may still encode identically. Ordinary serialization and snapshot encoding produce identical bytes. Capture failure publishes no snapshot.
Writers borrowed by a snapshot converter belong to its synchronous callback and must not be used after it returns. Every capture still evaluates getters and converters; this is not a mutation-notification cache.
System.ArgumentNullException:The source value is null.
System.ObjectDisposedException:The registry has been disposed.
System.InvalidOperationException:A converter, unsupported value or reference context prevents capture. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | + +### `Inno.Core.Serialization.SerializationSnapshot` + + + + + +| 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | +| --- | --- | --- | --- | +| [`public sealed class Inno.Core.Serialization.SerializationSnapshot`](../../src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs#L14) | Holds an immutable, neutral capture of serialized values without retaining the source object or its generation.
Capture again through to reuse unchanged values. Readers may share a snapshot across threads. It owns only scalar values, strings and copied binary data, never source instances, metadata, converters or delegates. It is transient and is not a persistence format. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | +| [`public byte[] Inno.Core.Serialization.SerializationSnapshot.ToArray()`](../../src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs#L24) | Encodes this capture using the same deterministic binary format as ordinary serialization.
返回:A new caller-owned byte array; changing it cannot modify this snapshot. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | +| [`public void Inno.Core.Serialization.SerializationSnapshot.WriteTo(System.IO.Stream destination)`](../../src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs#L33) | Streams this capture in the ordinary deterministic binary format without allocating the complete output payload.
`destination`:The writable caller-owned stream, which remains open.
System.ArgumentNullException:The destination is null.
System.ArgumentException:The destination cannot be written.
System.IO.IOException:The destination rejects a write; its partial bytes remain the caller's responsibility. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | ### `Inno.Core.Serialization.SerializationTypeMetadata` @@ -663,6 +686,9 @@ static (float x, float y)[] RoundTripPoints( + + + | 声明 | 行为与结果 | 所有权 / 调用约束 | 失败或限制 | | --- | --- | --- | --- | | [`public void Inno.Core.Serialization.SerializationWriter.Write(string name, object? value, System.Type declaredType)`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L85) | Writes a value using its declared metadata type without constructing a generic method at runtime.
`name`:The unique non-empty member name.
`value`:The value to encode, including null when permitted by the declared type.
`declaredType`:The exact declaration whose converter and value semantics apply. | — | `System.ArgumentNullException`:The declared type is null.
`System.ArgumentException`:The member name is empty.
`System.InvalidOperationException`:The operation has ended, the name is duplicated, or the value is unsupported. | @@ -674,6 +700,8 @@ static (float x, float y)[] RoundTripPoints( | [`public string Inno.Core.Serialization.SerializationWriter.path { get; }`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L34) | Gets the current diagnostic path. | — | — | | [`public System.Type Inno.Core.Serialization.SerializationWriter.valueType { get; }`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L39) | Gets the declared value type represented by this writer. | — | — | | [`public sealed class Inno.Core.Serialization.SerializationWriter`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L9) | Writes a structured object during the active serialization operation. | — | — | +| [`public Inno.Extensibility.Types.TypeCacheSnapshot Inno.Core.Serialization.SerializationWriter.typeSnapshot { get; }`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L53) | Borrows the immutable type snapshot pinned by this operation's converter generation.
Use this snapshot for consistent type identities during a complete capture. It must not be retained beyond the writer's operation; keeping it would retain its collectible types after the converter lease is released.
System.InvalidOperationException:The writer no longer belongs to an active capture. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | +| [`public void Inno.Core.Serialization.SerializationWriter.WritePropertiesData(string name, Inno.Core.Serialization.ISerializable value, Inno.Core.Serialization.SerializationContext? context = null)`](../../src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs#L241) | Writes independently restorable property bytes through the current converter generation.
`name`:The unique member receiving the binary property snapshot.
`value`:The object whose persistent properties are captured once.
`context`:The complete owner context, or null to use this writer's context.
System.ArgumentNullException:The source object is null.
System.InvalidOperationException:The writer has ended, a key is duplicated, or capture fails.
System.ArgumentException:The member name is empty. | 操作内借用;快照仅含中立值。 | 失败不发布部分捕获。 | ### `Inno.Core.Serialization.SerializedIdentityRemapper` diff --git a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md index 01ceecba5..73605fea1 100644 --- a/records/architecture/EDITOR_UI_CLOSURE_PLAN.md +++ b/records/architecture/EDITOR_UI_CLOSURE_PLAN.md @@ -10,7 +10,8 @@ Every work item below is a real GitHub issue created before implementation. Issu | Work | Issue | Owner | State | | --- | --- | --- | --- | -| New-project default layout | [#47](https://github.com/Wolfiora/InnoEngine/issues/47) | Standard Hosting resource and Core document owner | Local 9 focused / 150 related / 131 Interactions / 10 Hosting cases pass; hidden first-run Host and 241-project Wiki pass; final committed CI pending | +| New-project default layout | [#47](https://github.com/Wolfiora/InnoEngine/issues/47) | Standard Hosting resource and Core document owner | Local focused checks passed; exact-head ARM64 concurrency exposed #48; keep pending until the Core correction and remote CI pass | +| Atomic create-only publication | [#48](https://github.com/Wolfiora/InnoEngine/issues/48) | Existing Core IO primitive | Windows Core IO passes 31 cases, including threads/processes and complete reads; final SDK/layout/architecture and ARM64 CI pending | | E01 scopes and failures | [#28](https://github.com/Wolfiora/InnoEngine/issues/28) | ImGui adapter and concrete presentation | Focused native scopes passed; final graph/lifecycle coverage pending | | E02 command indexes | [#29](https://github.com/Wolfiora/InnoEngine/issues/29) | Interactions generation catalog | Implemented; focused contracts passed; final retirement/performance pending | | E03 styles | [#30](https://github.com/Wolfiora/InnoEngine/issues/30) | Shared and domain presentation | Domain styles moved to their actual owners; same-frame style deferral and foreign-context contracts passed; complete physical visual matrix pending | @@ -23,7 +24,7 @@ Every work item below is a real GitHub issue created before implementation. Issu | Frozen framework inputs | [#19](https://github.com/Wolfiora/InnoEngine/issues/19) | Host/Build capture and Compiler | SDK-owned provider replaces reference-pack selection; 11 frozen SDK/C# 14 contracts passed; final companion projections and CI pending | | E09 scale/input | [#36](https://github.com/Wolfiora/InnoEngine/issues/36) | Native public-input fixtures | 56 native simulated-metric combinations and selected real FlappyBird input/screenshots executed; final comprehensive matrix pending | | E10 retirement/reload | [#37](https://github.com/Wolfiora/InnoEngine/issues/37) | Host and generation owners | Actual nine-panel 100-cycle product fixture, saved Scene and Shader cycles, and focused generation tests executed; complete convergence matrix pending | -| E11 performance | [#38](https://github.com/Wolfiora/InnoEngine/issues/38) | Deterministic public-host workloads | Original/current deterministic harnesses prepared; identical current FlappyBird plugin input cannot compile against the original API; full comparison blocked | +| E11 performance | [#38](https://github.com/Wolfiora/InnoEngine/issues/38) | Deterministic public-host workloads | Historical producer combination recovered with identical runtime/assets and explicit Editor package differences; controlled final pairs and #46 absolute gate remain pending | | E12 documentation/review | [#39](https://github.com/Wolfiora/InnoEngine/issues/39) | Each changed API and Wiki owner | Design standard added; SDK-based architecture/Wiki gate passed for 241 projects before the final style cleanup; final synchronization pending | | Current SDK-owned validation | [#41](https://github.com/Wolfiora/InnoEngine/issues/41) | Tooling SDK capture and current public source models | SDK output owner and framework references implemented; 92 focused regressions passed; full policy/documentation audit pending | | Existing History rejection fix | [#13](https://github.com/Wolfiora/InnoEngine/issues/13) | History | Previously verified; final rerun required | @@ -83,6 +84,17 @@ Each row must ultimately record a final commit, executable command and evidence .NET SDK 10.0.402, C# 14 and BGCS 4.1.2 remain fixed. No BGCS/vendor/generated modifications, global/implicit imports, IVT, private reflection, test backdoors, compatibility readers or parallel infrastructure. Author and committer are Aaron Liao , with no co-author. Preserve exact source ownership and clean only proven inactive owned outputs; retain current products and evidence. +## Automatic continuation sequence (2026-10-29) + +The user requires a complete issue/feature/architecture review before ending work. Update the remaining sequence and continue automatically while independent work remains. No computer use or approval questions. A checkpoint, partial speedup or older green CI cannot satisfy final closure. + +1. **#46 exact dirty inspection:** finish neutral copy-on-write capture within the existing serialization pipeline, operation-pinned type lookup, bounded streaming hash and structural cache invalidation. Verify direct writes, converter state, references, component ordering, topology, Missing/reload, failures, save/History and owner retirement. Keep the 100 ms policy and complete authoritative traversal. Current prototypes reduce allocation but do not meet the announced absolute performance budget; they are not a resolution. +2. **#35/#28–#34 workflow closure:** map each original normal/failure/cancel/reopen journey to actual public/native tests. Add missing journeys through the existing feature/controller/presentation owners, then rerun the complete candidate. Keep handler tests distinct from drawn/input workflows. +3. **#36/#37 visual and lifetime closure:** replay all required native scale/input combinations, floating/docked and narrow layouts; collect matched captures where the actual public renderer permits it. Repeat actual panel/document and strict generation cycles with public ownership/weak-reference evidence. Physical display/manual Mac checks remain explicitly unexecuted, not inferred from simulation. +4. **#38 complete controlled performance:** recover the consistent pre-UI producer set: engine `b303d9f1`, Rendering2D `3e8fafe`, Samples `6eb52f3`, Canvas `eed5fd9`. Git confirms the later Samples change contains only the Rendering2D package; package inspection confirms only 15 editor implementation files plus its manifest differ. All other package entries and FlappyBird project files are identical. Treat those editor sources as measured implementation changes and freeze identical gameplay/assets/settings inputs. Report this multi-repository before/after experiment separately from the previously failed replay of one current package on the old API. Do not edit historical production, add compatibility shims or substitute different gameplay data. A test-owned public composition may select the existing hidden-window option. +5. **#39 documentation and final source:** synchronize new APIs and borrowing/failure semantics, exact SDK Wiki signatures, scripting exports, current file ownership and the eleven-dimension assessment. Refresh stale issue/ledger checkpoints with exact commits and evidence. +6. **Final review:** verify the final committed candidate's full applicable suites, product matrix and CI; check every issue's entire acceptance list before adding `Fixes`. Keep issues open until merge, keep partial work as `Refs`, do not merge automatically, and retire only proven inactive owned outputs. Repeat this review and continue whenever a correctable gap remains. + ## Current evidence, not final acceptance - Baseline inventory is in `baseline.json` and `device.json` under the evidence owner. All five repositories were initially clean, including BGCS. @@ -224,3 +236,22 @@ Final working-tree verification: the twelve-project compilation, Core 49 / Scene - The actual FlappyBird source extension completed 100 close/open cycles for all nine panels during 15,000 D3D12 frames. Eleven full-GC resource samples preserve Selection/Scene/History/pending-event invariants and show stable managed memory. This is not a proof of every private owner. - Workflow 40653896090 for f7dce89c completed all four native/managed jobs successfully; NuGet passed separately. Later source changes require their own CI. - The full original-product baseline incompatibility and unclosed E08/E09/E10/E11 matrix remain explicitly tracked. The PR stays Draft until all applicable gates are satisfied. + +## Automatic continuation checkpoint (2026-10-29) + +- #46 now captures complete state through the existing Core serialization pipeline, sharing immutable neutral values and hashing ordinary binary output only when necessary. It does not skip getters/converters, change the 100 ms policy or retain source objects/types/delegates. The current Core suite passes 74 cases, Scene passes 53, and a retained snapshot passes the real collectible-script retirement barrier. +- Five deliberately due Release inspection groups remain above the absolute #46 budget: medians 49.01–51.04 ms, P95 51.32–58.93 ms, approximately 10.45 MB per inspection. This is progress, not closure; further measured work is required. +- The new independent FlappyBird product journey verifies eight real drawn workflows, including mixed selection, gestures, separate Undo/Redo, reference state, component ordering, batch recovery, Save/reopen and actual Play behavior advancement with Edit isolation. The first invalid script-API fixture and an incorrect selection-count premise remain failed attempts; corrected public-boundary evidence is `e08/product-journey-d3d12-complete/result.json`. Later Core changes require a final rerun. +- A standard historical producer combination restores the original FlappyBird baseline without a compatibility layer. Runtime/assets are identical; only Rendering2D Editor source/package identity changes between producers. The linked diagnostic host uses the unchanged original entry source and complete officially built platform output. Historical entry-graph cancellation is recorded separately. Full paired product measurements remain pending. +- The first final Contracts operation was aborted after known XML-style violations were found. Its trace identifies the existing full-repository Wiki test, not a serialization deadlock. It is not a successful test result. Final verification will reuse that full gate instead of running duplicate whole-repository audits concurrently. +- Automatic approval review rejected deletion of four completed intermediate diagnostic runtime copies (`blocked by policy`, without a more specific reason). The directories remain intact. Production cache maintenance continues through its owner protocol; SDK/NuGet/user content was not deleted. + +Remaining execution order: finish the #46 quantitative gate; rerun all current source/contract/workflow/visual/lifecycle gates and the product journey on one frozen candidate; complete paired performance and the remaining per-view evidence; review every issue against its checklist; commit with the required identity, update PR/issue evidence, and verify exact-head CI. No checkpoint or focused pass closes the parent plan. + + +## Continuation checkpoint: complete input checks without a second authority + +- Core neutral captures now retain one BCL OrderedDictionary per structured node, replacing the dictionary plus ordered-entry cache. Exact bytes, full converter/getter execution, duplicate-key rejection, copied mutable binary data and callback retirement remain required. The 74-case Core suite passes on this candidate. +- The immutable context replacement no longer retains overwritten services. The independently measured due-inspection allocation is below 8 MiB, but 41–43 ms median still fails #46. Do not close it, alter the 100 ms policy or weaken complete-state checks. +- The 236-case native input/layout run adds real hierarchy/Browser keyboard coverage to all 28 scale combinations at two logical sizes. Physical displays and human visual conclusions remain separate. +- Follow-on order: commit the current reviewed capture/fixture implementation; execute the unified applicable profiles on that frozen candidate; correct every failure; run actual FlappyBird journeys and controlled original/current product pairs; refresh per-issue evidence, then repeat the deep completion audit. PR #26 remains Draft while quantitative or complete-matrix requirements remain open. diff --git a/records/architecture/acceptance/EDITOR_UI_SNAPSHOT_ACCEPTANCE_2026_10_29.md b/records/architecture/acceptance/EDITOR_UI_SNAPSHOT_ACCEPTANCE_2026_10_29.md new file mode 100644 index 000000000..abe61f2c7 --- /dev/null +++ b/records/architecture/acceptance/EDITOR_UI_SNAPSHOT_ACCEPTANCE_2026_10_29.md @@ -0,0 +1,71 @@ +# Editor UI snapshot and product workflow verification + +This is an intermediate acceptance record for #27, #35, #38 and #46. It does not close the complete approved UI plan or make PR #26 Ready. + +## Implementation and ownership + +- Core Serialization owns immutable, neutral `SerializationSnapshot` values. Repeated capture still executes all getters and converters. Copy-on-write only reuses already captured neutral nodes; it is not a change notification system. +- Snapshot values contain ordinary scalar/string/copied binary data and neutral structural metadata. They do not retain source instances, plugin types, converters, delegates, context services or generation leases. +- Scene documents own their latest observed snapshot/hash. The saved hash remains independent. Encoding and SHA256 use the ordinary binary format; failed capture/hash does not replace the previous valid observation. +- The writer borrows the converter generation's actual type snapshot for consistent Stable Type IDs. Nested state capture uses the same converter lease and complete owner context. +- Context storage uses a private immutable array of exact contract/value pairs. Replacement copies that small owned array without retaining the replaced service. Object nodes use the BCL generic OrderedDictionary for one ordered storage authority rather than a dictionary plus a duplicated entry cache. Array enumeration and component callbacks reuse operation-local mechanics. Graph membership comes from the actual capture owner; subtree boundaries remain distinct from the complete reference map. +- No additional serializer format, History stack, event bus, identity registry, mutation tracker, platform branch or public test hook was introduced. BGCS, vendor sources and generated bindings were not modified. + +## Required public surface + +`CaptureSnapshot` and `SerializationSnapshot` allow another assembly to retain the exact neutral capture without retaining its source. `ToArray` and `WriteTo` provide owned bytes and caller-owned streaming respectively. `SerializationWriter.WritePropertiesData` captures nested independently restorable state with the current converter lease. Its borrowed `typeSnapshot` avoids an independent live type-generation decision. Node representation and reuse remain internal; snapshot/type-generation APIs are not added to the script surface. + +## Recorded evidence + +Evidence owner: `D:/InnoEngine-Verification/editor-ui-closure-2026-10-27`. + +| Check | Result | Evidence / scope | +| --- | --- | --- | +| Core serialization | Passed: 74 | `e46/snapshot/core-bcl-ordered.trx`; includes ordinary byte-array copy semantics, structural changes, floating point bits, wide objects, failure, parallel readers, source retirement and context replacement lifetime | +| Scene contracts | Passed: 53 | `e46/snapshot/scene-stack-final.trx`; capture references, subtree, Prefab and current serialization contracts | +| Real workspace changes | Passed: 3 | `stack-controlled-final.trx`; independent ordinary serialization/hash oracle, direct mutation, converter state, failure recovery and component ordering | +| Snapshot across collectible retirement | Passed: 1 | `snapshot-generation.trx`; retained neutral bytes survive the existing strict unload barrier while the old script type becomes unreachable; later final-source rerun required | +| Independent Editor output | Passed | `product-independent-final.log/binlog`; official SDK graph, zero warnings/errors; native `processes=0`, `bindingGenerations=0`; later comment/context changes require final rebuild | +| FlappyBird drawn workflows | Passed: 8 stages | `e08/product-journey-d3d12-complete/result.json`; actual hidden D3D12 product, gestures, independent Undo/Redo, references, mixed axes, duplicate components, batch recovery, failed mutation rollback, Save/reopen and advancing Play behavior with Edit isolation | +| Whole source build | Passed on successive implementation candidates | `source-wide-final.log`, `source-documented-final.log`; zero errors; final-source gate remains separate | +| Full Contracts / Wiki | Pending rerun | The first operation was aborted after confirmed XML formatting violations. A diagnostic trace identifies its existing whole-repository Wiki test. The direct diagnostic Wiki invocation also lacked logical example references; neither invocation is counted as passed. New XML is expanded and API markers are synchronized. | + +The product journey uses a fresh owned project copy and ordinary source extensions. It never edits the original FlappyBird project. It drives public feature commands while real panels draw intermediate state; native pointer/keyboard tests and physical-device experience are separate evidence. Short smoke runs that exit before script activation do not prove these workflows. + +The initial journey used host-only Scene lookup APIs that are deliberately absent from script references. The corrected fixture uses the public current scene/component collections and Core persistent identities; no API was exposed for testing. Another initial assertion incorrectly required Selection count zero after closure. The actual contract retains temporarily unavailable identities, so the corrected assertion rejects a resolved stale target while preserving Missing semantics. These attempts remain failed records. + +## Quantitative gate still open + +`e46/snapshot/absolute-array-processes/measurements.json` records five independent Release VSTest processes, each with five warmups and ten deliberately due complete inspections. The 110 ms preparation delay is excluded from frame timing; it ensures every sample runs the unchanged 100 ms policy. These measurements precede the OrderedDictionary storage cleanup and are not final-candidate measurements. + +| Process | Median ms | P95 ms | Allocated bytes / inspection | +| --- | ---: | ---: | ---: | +| 0 | 40.98200 | 42.3213 | 8,370,499.2 | +| 1 | 42.83055 | 44.2989 | 8,370,499.2 | +| 2 | 41.33805 | 61.8373 | 8,370,524.8 | +| 3 | 43.39640 | 50.5440 | 8,370,499.2 | +| 4 | 42.02650 | 43.2985 | 8,370,499.2 | + +Allocation is below the announced 8 MiB limit; latency does **not** meet the 16 ms median / 25 ms P95 target. #46 remains Open and must not appear as `Fixes` yet. Earlier stack-context results remain in `stack-controlled-final.json` (49–51 ms, approximately 10.45 MB per inspection). Do not compare due inspections with ordinary frames as though they were identical operations. + +Diagnostic decomposition measures the public canonical Asset load and current source freshness separately from complete neutral capture. The public metadata-getter workload costs approximately 9 ms for 10,000 objects. These diagnostic runs locate work; they do not authorize skipping liveness or source verification and are not controlled final comparisons. An experimental scalar converter cache was rejected rather than added to production without sufficient benefit. + +## Additional applicable checks + +- `e09/native-current-matrix.trx`: 236 real-context checks passed, including the hierarchy and Browser keyboard workflows at both logical sizes and all 28 zoom/framebuffer-scale combinations. These are native input/layout simulations, not physical-monitor or human visual approval. +- `core-bcl-ordered.trx`: 74 Core serialization cases pass after replacing duplicated ordered-entry storage with the existing .NET collection. +- CI at `a38437f7d27307aadcf964f76e9228256d26185f`: Windows and macOS managed jobs passed. The macOS log explicitly reports all 31 Core IO tests passed, including the atomic publication regressions. Native job results are tracked separately; this does not certify subsequent uncommitted snapshot work. + +## Original product baseline + +The original engine `b303d9f1`, Rendering2D `3e8fafe`, Samples `6eb52f3` and unchanged Canvas restore a consistent historical producer combination. The Samples source differs only in the Rendering2D package; package inspection shows identical runtime/assets and changed Editor code/manifest identity. Both producers share the explicit neutral layout and test source. This is a multi-repository before/after comparison, not an assertion that the original engine can load the current package unchanged. + +The unchanged original platform builds and runs 300 D3D12 frames. A diagnostic SDK host links its unmodified entry program and complete official platform output. The cancelled historical entry-graph operation is preserved separately. Complete paired product measurements are still pending. + +## Self-review and continuation + +The correction preserves domain direction and reuses Core serialization, identity and IO protocols. Snapshot publication owns only neutral data; contexts and converter leases remain operation-local. Failure does not replace valid data, and replacement-context tests verify that discarded services are collectible. These concrete checks support the ownership design; they do not certify the whole engine as flawless. + +Still required: #46 quantitative closure, final frozen-source profiles and Wiki, final product/paired performance, remaining per-view and lifetime evidence, exact-head CI, issue-by-issue review and the eleven-dimension report. Mac physical checks remain unexecuted. PR #26 stays Draft until its applicable completion conditions are met. + +Automatic approval review rejected deletion of four completed diagnostic runtime copies with `blocked by policy` and no more specific reason. They remain intact. No user, SDK or NuGet global content was removed. The Windows environment has no specified `Glass.aiff`; no completion sound has been played. diff --git a/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.Persistence.cs b/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.Persistence.cs index 29d9c7476..60630c2c2 100644 --- a/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.Persistence.cs +++ b/src/composition/editor/features/Inno.Editor.Scene/Documents/EditorSceneWorkspace.Persistence.cs @@ -302,19 +302,33 @@ private void EnsureCanPersist() } } - private byte[] ComputeSceneHash(GameScene scene) - { + private byte[] ComputeSceneHash( + GameScene scene, + SceneDocument? document = null + ) { var dependencies = new AssetDependencyCollection(includeLastKnownPaths: false); SerializationContext context = AssetSerializationContext.Create(m_assets, dependencies); - byte[] payload = m_serialization.Serialize(scene, context); - return SHA256.HashData(payload); + SerializationSnapshot captured = m_serialization.CaptureSnapshot(scene, context, document?.observedSnapshot); + if (document is not null && ReferenceEquals(captured, document.observedSnapshot)) + return document.observedHash!; + using var algorithm = SHA256.Create(); + using (var digest = new CryptoStream(Stream.Null, algorithm, CryptoStreamMode.Write, leaveOpen: true)) + using (var output = new BufferedStream(digest, 64 * 1024)) + captured.WriteTo(output); + byte[] hash = algorithm.Hash ?? throw new InvalidOperationException("Scene content hashing did not complete."); + if (document is not null) + { + document.observedSnapshot = captured; + document.observedHash = hash; + } + return hash; } private bool HasSerializedChanges( GameScene scene, SceneDocument document ) - => !ComputeSceneHash(scene).AsSpan().SequenceEqual(document.savedHash); + => !ComputeSceneHash(scene, document).AsSpan().SequenceEqual(document.savedHash); private string CreateUniquePath( string directory, @@ -376,6 +390,9 @@ private sealed class SceneDocument( Guid sourceAssetId, byte[] savedHash ) { + internal SerializationSnapshot? observedSnapshot; + internal byte[]? observedHash; + /// /// The scene value used as part of this type's public representation. /// diff --git a/src/content/assets/Inno.Assets/Serialization/AssetDependencyCollection.cs b/src/content/assets/Inno.Assets/Serialization/AssetDependencyCollection.cs index 793587dbd..5931938c6 100644 --- a/src/content/assets/Inno.Assets/Serialization/AssetDependencyCollection.cs +++ b/src/content/assets/Inno.Assets/Serialization/AssetDependencyCollection.cs @@ -16,7 +16,7 @@ namespace Inno.Assets; /// public sealed class AssetDependencyCollection { - private readonly Dictionary m_dependencies = []; + private Dictionary? m_dependencies; /// /// Creates a dependency collector that preserves last-known source paths in serialized references. @@ -47,7 +47,9 @@ public AssetDependencyCollection(bool includeLastKnownPaths) /// Gets the collected dependencies in deterministic persistent-identity order. /// public IReadOnlyList dependencies - => m_dependencies.Values.OrderBy(static dependency => dependency.persistentId).ToArray(); + => m_dependencies is null || m_dependencies.Count == 0 + ? Array.Empty() + : m_dependencies.Values.OrderBy(static dependency => dependency.persistentId).ToArray(); internal void Add( AssetObject asset, @@ -64,7 +66,7 @@ TypeCatalog types $"Asset type '{asset.GetType().FullName}' requires a StableTypeId before it can be referenced persistently."); } - m_dependencies[persistentId] = new AssetDependency( + (m_dependencies ??= [])[persistentId] = new AssetDependency( persistentId, typeRef, includeLastKnownPaths ? asset.assetPath.ToString() : string.Empty); @@ -80,7 +82,7 @@ public void Add(AssetDependency dependency) { if (dependency.persistentId == Guid.Empty) throw new ArgumentException("A dependency requires a persistent asset identity.", nameof(dependency)); - m_dependencies[dependency.persistentId] = includeLastKnownPaths ? dependency + (m_dependencies ??= [])[dependency.persistentId] = includeLastKnownPaths ? dependency : new AssetDependency(dependency.persistentId, dependency.type, string.Empty); } } diff --git a/src/content/assets/Inno.Assets/Serialization/AssetObjectConverter.cs b/src/content/assets/Inno.Assets/Serialization/AssetObjectConverter.cs index f7b1615a8..d77cc4b77 100644 --- a/src/content/assets/Inno.Assets/Serialization/AssetObjectConverter.cs +++ b/src/content/assets/Inno.Assets/Serialization/AssetObjectConverter.cs @@ -31,7 +31,7 @@ AssetObject value Guid persistentId = value.identity.persistentId; if (persistentId == Guid.Empty) throw new InvalidOperationException($"Asset '{value.GetType().FullName}' has no persistent identity at '{writer.path}'."); - TypeCatalog types = writer.context.GetRequired(); + TypeCacheSnapshot types = writer.typeSnapshot; if (!types.TryGetTypeRef(value.GetType(), out TypeRef typeRef)) { throw new InvalidOperationException( @@ -40,13 +40,14 @@ AssetObject value bool hasDependencies = writer.context.TryGet(out AssetDependencyCollection? dependencies) && dependencies is not null; + string lastKnownPath = !hasDependencies || dependencies!.includeLastKnownPaths + ? value.assetPath.ToString() + : string.Empty; writer.Write(C_PERSISTENT_ID, persistentId); writer.Write(C_STABLE_TYPE_ID, typeRef.stableId); - writer.Write( - C_LAST_KNOWN_PATH, - !hasDependencies || dependencies!.includeLastKnownPaths ? value.assetPath.ToString() : string.Empty); + writer.Write(C_LAST_KNOWN_PATH, lastKnownPath); if (hasDependencies) - dependencies!.Add(value, types); + dependencies!.Add(new AssetDependency(persistentId, typeRef, lastKnownPath)); } /// diff --git a/src/content/scene/Inno.Scene/Serialization/Converters/GameObjectConverter.cs b/src/content/scene/Inno.Scene/Serialization/Converters/GameObjectConverter.cs index a7d3cd8a0..abef5e5e0 100644 --- a/src/content/scene/Inno.Scene/Serialization/Converters/GameObjectConverter.cs +++ b/src/content/scene/Inno.Scene/Serialization/Converters/GameObjectConverter.cs @@ -68,7 +68,7 @@ GameObject value : Guid.NewGuid()); } - var references = new SceneGraphReferenceMap(value.scene, engineObjects, sourceIds, value); + var references = new SceneGraphReferenceMap(value.scene, sourceIds, value); writer.Write(C_SOURCE_ROOT_ID_KEY, sourceIds[value]); using (references.Enter()) { @@ -76,6 +76,7 @@ GameObject value writer, entries, sourceIds, + included.Contains, preserveRootSiblingOrder: false); } } diff --git a/src/content/scene/Inno.Scene/Serialization/Converters/GameSceneConverter.cs b/src/content/scene/Inno.Scene/Serialization/Converters/GameSceneConverter.cs index 6b0981faa..4906064c7 100644 --- a/src/content/scene/Inno.Scene/Serialization/Converters/GameSceneConverter.cs +++ b/src/content/scene/Inno.Scene/Serialization/Converters/GameSceneConverter.cs @@ -31,14 +31,22 @@ GameScene value SceneStructureSnapshot snapshot = value.CaptureStructure(); IReadOnlyList systems = value.GetSystems(); - EngineObject[] engineObjects = snapshot.objects - .SelectMany(static entry => entry.components.Cast().Prepend(entry.gameObject)) - .Concat(systems) - .ToArray(); - var sourceIds = new Dictionary(ReferenceEqualityComparer.Instance); - for (int i = 0; i < engineObjects.Length; i++) - sourceIds.Add(engineObjects[i], engineObjects[i].identity.persistentId); - var references = new SceneGraphReferenceMap(value, engineObjects, sourceIds); + int count = systems.Count; + foreach (SceneObjectStructureSnapshot entry in snapshot.objects) + count = checked(count + entry.components.Count + 1); + var sourceIds = new Dictionary(count, ReferenceEqualityComparer.Instance); + foreach (SceneObjectStructureSnapshot entry in snapshot.objects) + { + sourceIds.Add(entry.gameObject, entry.gameObject.identity.persistentId); + for (int index = 0; index < entry.components.Count; index++) + { + GameComponent component = entry.components[index]; + sourceIds.Add(component, component.identity.persistentId); + } + } + foreach (GameSystem system in systems) + sourceIds.Add(system, system.identity.persistentId); + var references = new SceneGraphReferenceMap(value, sourceIds); writer.Write(SceneGraphSerialization.C_SCENE_ID_KEY, value.identity.persistentId); writer.Write(SceneGraphSerialization.C_NAME_KEY, value.name); @@ -48,6 +56,7 @@ GameScene value writer, snapshot.objects, sourceIds, + gameObject => sourceIds.ContainsKey(gameObject), preserveRootSiblingOrder: true); SceneGraphSerialization.WriteSystems(writer, systems, sourceIds); } diff --git a/src/content/scene/Inno.Scene/Serialization/PrefabOverrideProcessor.cs b/src/content/scene/Inno.Scene/Serialization/PrefabOverrideProcessor.cs index d65ac10a8..ab53679ae 100644 --- a/src/content/scene/Inno.Scene/Serialization/PrefabOverrideProcessor.cs +++ b/src/content/scene/Inno.Scene/Serialization/PrefabOverrideProcessor.cs @@ -559,7 +559,7 @@ PrefabConnectionRecord connection if (component is not null) ids[component] = sourceId; } - return new SceneGraphReferenceMap(scene, allObjects, ids); + return new SceneGraphReferenceMap(scene, ids); } private static SceneGraphReferenceMap CreateTargetReferences( diff --git a/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphReferenceMap.cs b/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphReferenceMap.cs index a4584d884..42a5910e9 100644 --- a/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphReferenceMap.cs +++ b/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphReferenceMap.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Inno.Core.Execution; using Inno.Scene; @@ -10,32 +11,20 @@ internal sealed class SceneGraphReferenceMap { private static readonly ExecutionSlot C_CURRENT = new("scene graph references"); - private readonly Dictionary m_sourceIdByObject = new(ReferenceEqualityComparer.Instance); + private readonly IReadOnlyDictionary? m_sourceIdByObject; private readonly Dictionary m_objectBySourceId = new(); - private readonly HashSet m_allowedObjects = new(ReferenceEqualityComparer.Instance); private readonly GameScene m_scene; private readonly GameObject? m_captureRoot; internal SceneGraphReferenceMap( GameScene scene, - IEnumerable? allowedObjects = null, IReadOnlyDictionary? sourceIds = null, GameObject? captureRoot = null ) { m_scene = scene ?? throw new ArgumentNullException(nameof(scene)); m_captureRoot = captureRoot; - if (allowedObjects is null) - return; - - foreach (EngineObject engineObject in allowedObjects) - { - m_allowedObjects.Add(engineObject); - m_sourceIdByObject.Add( - engineObject, - sourceIds is not null && sourceIds.TryGetValue(engineObject, out Guid sourceId) - ? sourceId - : engineObject.identity.persistentId); - } + // Capture callers own the complete map and keep it fixed for this synchronous scope. + m_sourceIdByObject = sourceIds; } internal static SceneGraphReferenceMap current => C_CURRENT.current; @@ -60,8 +49,7 @@ internal EngineReferenceToken Capture( string path ) { ArgumentNullException.ThrowIfNull(engineObject); - if (engineObject.isDestroyed || - !m_allowedObjects.Contains(engineObject) || + if (engineObject.isDestroyed || m_sourceIdByObject is null || !m_sourceIdByObject.TryGetValue(engineObject, out Guid sourceId)) { throw new InvalidOperationException( diff --git a/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphSerialization.cs b/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphSerialization.cs index 541a022c2..493bd468a 100644 --- a/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphSerialization.cs +++ b/src/content/scene/Inno.Scene/Serialization/SceneGraph/SceneGraphSerialization.cs @@ -61,15 +61,17 @@ internal static void WriteObjects( SerializationWriter writer, IReadOnlyCollection entries, IReadOnlyDictionary sourceIds, + Predicate containsObject, bool preserveRootSiblingOrder ) { ArgumentNullException.ThrowIfNull(writer); ArgumentNullException.ThrowIfNull(entries); ArgumentNullException.ThrowIfNull(sourceIds); - - var included = new HashSet( - entries.Select(static entry => entry.gameObject), - ReferenceEqualityComparer.Instance); + ArgumentNullException.ThrowIfNull(containsObject); + Action writeComponent = ( + componentWriter, + component + ) => WriteComponent(componentWriter, component, sourceIds); WriteMissingReferenceAliases(writer, sourceIds); writer.WriteObjectArray(C_OBJECTS_KEY, entries, ( objectWriter, @@ -78,7 +80,7 @@ bool preserveRootSiblingOrder { GameObject gameObject = entry.gameObject; Transform? parent = gameObject.transform.parent; - bool parentIsIncluded = parent is not null && included.Contains(parent.gameObject); + bool parentIsIncluded = parent is not null && containsObject(parent.gameObject); objectWriter.Write(C_OBJECT_ID_KEY, GetSourceId(sourceIds, gameObject)); objectWriter.Write(C_NAME_KEY, gameObject.name); objectWriter.Write(C_TAG_KEY, gameObject.tag); @@ -91,7 +93,7 @@ bool preserveRootSiblingOrder C_SIBLING_INDEX_KEY, parentIsIncluded || preserveRootSiblingOrder ? gameObject.transform.siblingIndex : 0); PrefabInstanceInfo? prefab = gameObject.prefabInstance; - bool hasPrefabConnection = prefab is not null && included.Contains(prefab.instanceRoot); + bool hasPrefabConnection = prefab is not null && containsObject(prefab.instanceRoot); objectWriter.Write(C_HAS_PREFAB_CONNECTION_KEY, hasPrefabConnection); if (hasPrefabConnection) { @@ -127,36 +129,7 @@ bool preserveRootSiblingOrder C_PREFAB_ORPHANED_OVERRIDE_COUNT_KEY, capturedOverrides?.orphanedCount ?? prefab.orphanedOverrideCount); } - objectWriter.WriteObjectArray(C_COMPONENTS_KEY, entry.components, ( - componentWriter, - component - ) => - { - componentWriter.Write(C_COMPONENT_ID_KEY, GetSourceId(sourceIds, component)); - if (component is MissingGameComponent missing) - { - componentWriter.Write(C_STABLE_TYPE_ID_KEY, missing.missingType.stableId); - componentWriter.Write(C_TYPE_NAME_KEY, missing.missingTypeName); - componentWriter.Write(C_STATE_KEY, missing.CaptureSerializedState()); - WriteStateDependencies(componentWriter, missing.dependencies); - return; - } - componentWriter.Write(C_STABLE_TYPE_ID_KEY, GetStableComponentTypeId(component.GetType(), componentWriter.context)); - componentWriter.Write(C_TYPE_NAME_KEY, component.GetType().FullName ?? component.GetType().Name); - try - { - CapturedSceneState state = CaptureState(component, componentWriter.context); - componentWriter.Write(C_STATE_KEY, state.data); - WriteStateDependencies(componentWriter, state.dependencies); - } - catch (Exception exception) - { - throw new InvalidOperationException( - $"Failed to serialize GameComponent '{component.GetType().FullName}' on " + - $"GameObject '{gameObject.name}' at '{componentWriter.path}'. {exception.Message}", - exception); - } - }); + objectWriter.WriteObjectArray(C_COMPONENTS_KEY, entry.components, writeComponent); }); } @@ -413,11 +386,10 @@ IReadOnlyDictionary sourceIds WriteStateDependencies(systemWriter, missing.dependencies); return; } - systemWriter.Write(C_STABLE_TYPE_ID_KEY, GetStableSystemTypeId(system.GetType(), systemWriter.context)); + systemWriter.Write(C_STABLE_TYPE_ID_KEY, systemWriter.typeSnapshot.GetTypeRef(system.GetType()).stableId); systemWriter.Write(C_TYPE_NAME_KEY, system.GetType().FullName ?? system.GetType().Name); - CapturedSceneState state = CaptureState(system, systemWriter.context); - systemWriter.Write(C_STATE_KEY, state.data); - WriteStateDependencies(systemWriter, state.dependencies); + AssetDependency[] dependencies = WriteState(systemWriter, system); + WriteStateDependencies(systemWriter, dependencies); }); } @@ -806,13 +778,6 @@ private static bool IsTransform(SerializationReader reader) out Type? type) && type == typeof(Transform); - private static Guid GetStableComponentTypeId( - Type componentType, - SerializationContext context - ) { - return context.GetRequired().GetTypeRef(componentType).stableId; - } - private static bool TryResolveComponentType( Guid stableTypeId, SerializationContext context, @@ -829,13 +794,6 @@ out Type? componentType return true; } - private static Guid GetStableSystemTypeId( - Type systemType, - SerializationContext context - ) { - return context.GetRequired().GetTypeRef(systemType).stableId; - } - private static bool TryResolveSystemType( Guid stableTypeId, SerializationContext context, @@ -857,18 +815,48 @@ out Type? type return context.GetRequired().TryResolve(typeRef, out type); } - private static CapturedSceneState CaptureState( - ISerializable value, - SerializationContext outerContext + private static void WriteComponent( + SerializationWriter writer, + GameComponent component, + IReadOnlyDictionary sourceIds ) { + writer.Write(C_COMPONENT_ID_KEY, GetSourceId(sourceIds, component)); + if (component is MissingGameComponent missing) + { + writer.Write(C_STABLE_TYPE_ID_KEY, missing.missingType.stableId); + writer.Write(C_TYPE_NAME_KEY, missing.missingTypeName); + writer.Write(C_STATE_KEY, missing.CaptureSerializedState()); + WriteStateDependencies(writer, missing.dependencies); + return; + } + writer.Write(C_STABLE_TYPE_ID_KEY, writer.typeSnapshot.GetTypeRef(component.GetType()).stableId); + writer.Write(C_TYPE_NAME_KEY, component.GetType().FullName ?? component.GetType().Name); + GameObject gameObject = component.gameObject; + try + { + AssetDependency[] dependencies = WriteState(writer, component); + WriteStateDependencies(writer, dependencies); + } + catch (Exception exception) + { + throw new InvalidOperationException( + $"Failed to serialize GameComponent '{component.GetType().FullName}' on " + + $"GameObject '{gameObject.name}' at '{writer.path}'. {exception.Message}", + exception); + } + } + + private static AssetDependency[] WriteState( + SerializationWriter writer, + ISerializable value + ) { + SerializationContext outerContext = writer.context; bool includeLastKnownPaths = !outerContext.TryGet(out AssetDependencyCollection? outerDependencies) || outerDependencies is null || outerDependencies.includeLastKnownPaths; var dependencies = new AssetDependencyCollection(includeLastKnownPaths); - byte[] data = outerContext.GetRequired().CapturePropertiesData( - value, - outerContext.With(dependencies)); - return new CapturedSceneState(data, dependencies.dependencies.ToArray()); + writer.WritePropertiesData(C_STATE_KEY, value, outerContext.With(dependencies)); + return dependencies.dependencies.ToArray(); } private static void WriteStateDependencies( @@ -877,14 +865,22 @@ IReadOnlyList dependencies ) { writer.Write(C_STATE_DEPENDENCIES_KEY, dependencies.ToArray()); IAssetReferenceResolver resolver = writer.context.GetRequired(); - AssetObject[] assets = dependencies - .Select(dependency => resolver.Resolve( + if (dependencies.Count == 0) + { + writer.Write(C_STATE_DEPENDENCY_ASSETS_KEY, Array.Empty()); + return; + } + var assets = new AssetObject[dependencies.Count]; + for (int index = 0; index < dependencies.Count; index++) + { + AssetDependency dependency = dependencies[index]; + assets[index] = resolver.Resolve( dependency.persistentId, dependency.type.stableId, dependency.lastKnownPath, typeof(AssetObject), - "scene.state.dependencies")) - .ToArray(); + "scene.state.dependencies"); + } writer.Write(C_STATE_DEPENDENCY_ASSETS_KEY, assets); } @@ -924,8 +920,4 @@ string path throw new InvalidDataException($"Scene element type name at '{path}' cannot be empty."); } - private readonly record struct CapturedSceneState( - byte[] data, - AssetDependency[] dependencies - ); } diff --git a/src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs b/src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs index 7459fbf92..a44cdbee4 100644 --- a/src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs +++ b/src/content/scene/Inno.Scene/Serialization/ScenePropertySerialization.cs @@ -189,7 +189,10 @@ internal static SceneGraphReferenceMap CreateReferences( for (int i = 0; i < systems.Count; i++) objects.Add(systems[i]); - var references = new SceneGraphReferenceMap(scene, objects); + var sourceIds = new Dictionary(objects.Count, ReferenceEqualityComparer.Instance); + foreach (EngineObject value in objects) + sourceIds.Add(value, value.identity.persistentId); + var references = new SceneGraphReferenceMap(scene, sourceIds); for (int i = 0; i < objects.Count; i++) references.Register(objects[i].identity.persistentId, objects[i]); if (aliases is not null) diff --git a/src/content/scene/Inno.Scene/Serialization/SceneReloadStateTransfer.cs b/src/content/scene/Inno.Scene/Serialization/SceneReloadStateTransfer.cs index faed5aac5..098123d90 100644 --- a/src/content/scene/Inno.Scene/Serialization/SceneReloadStateTransfer.cs +++ b/src/content/scene/Inno.Scene/Serialization/SceneReloadStateTransfer.cs @@ -64,7 +64,7 @@ IAssetReferenceResolver assets var sourceIds = new Dictionary(ReferenceEqualityComparer.Instance); foreach (EngineObject target in engineObjects) sourceIds.Add(target, target.identity.persistentId); - var references = new SceneGraphReferenceMap(scene, engineObjects, sourceIds); + var references = new SceneGraphReferenceMap(scene, sourceIds); var states = new List(); using (references.Enter()) { diff --git a/src/content/scene/Inno.Scene/Serialization/SceneSubtreeStateConverter.cs b/src/content/scene/Inno.Scene/Serialization/SceneSubtreeStateConverter.cs index 54e9df78c..9faa25916 100644 --- a/src/content/scene/Inno.Scene/Serialization/SceneSubtreeStateConverter.cs +++ b/src/content/scene/Inno.Scene/Serialization/SceneSubtreeStateConverter.cs @@ -41,7 +41,7 @@ SceneSubtreeState value var sourceIds = new Dictionary(ReferenceEqualityComparer.Instance); for (int i = 0; i < allObjects.Length; i++) sourceIds.Add(allObjects[i], allObjects[i].identity.persistentId); - var references = new SceneGraphReferenceMap(scene, allObjects, sourceIds, root); + var references = new SceneGraphReferenceMap(scene, sourceIds, root); writer.Write(C_ROOT_ID_KEY, root.identity.persistentId); using (references.Enter()) @@ -50,6 +50,7 @@ SceneSubtreeState value writer, entries, sourceIds, + included.Contains, preserveRootSiblingOrder: true); } } diff --git a/src/content/scene/Inno.Scene/Storage/SceneStore.cs b/src/content/scene/Inno.Scene/Storage/SceneStore.cs index 03a6c8887..ac9aedee4 100644 --- a/src/content/scene/Inno.Scene/Storage/SceneStore.cs +++ b/src/content/scene/Inno.Scene/Storage/SceneStore.cs @@ -64,6 +64,7 @@ internal sealed class SceneStore private readonly Dictionary m_typedComponentQueryCache = []; private readonly Dictionary> m_objectQueryCache = []; private GameObject[]? m_objectSnapshotCache; + private SceneStructureSnapshot? m_structureSnapshotCache; private long m_nextObjectOrder; private long m_structureRevision; private int m_executionDepth; @@ -449,6 +450,7 @@ int componentIndex return; record.components.RemoveAt(currentIndex); record.components.Insert(targetIndex, component); + m_structureSnapshotCache = null; } internal IReadOnlyList GetComponents( @@ -572,10 +574,12 @@ internal SceneStructureSnapshot CaptureStructure() "A scene cannot be captured during an execution phase with uncommitted structural changes."); } + if (m_structureSnapshotCache is not null) + return m_structureSnapshotCache; SceneObjectStructureSnapshot[] objects = GetObjects() .Select(gameObject => new SceneObjectStructureSnapshot(gameObject, GetComponents(gameObject))) .ToArray(); - return new SceneStructureSnapshot(objects); + return m_structureSnapshotCache = new SceneStructureSnapshot(objects); } internal void InvalidateTypeCaches() @@ -767,6 +771,7 @@ private void InvalidateStructureCaches() { m_structureRevision = unchecked(m_structureRevision + 1); m_objectSnapshotCache = null; + m_structureSnapshotCache = null; InvalidateTypeCaches(); } diff --git a/src/foundation/core/Inno.Core.Serialization/Context/SerializationContext.cs b/src/foundation/core/Inno.Core.Serialization/Context/SerializationContext.cs index 8705abe2e..4e9b73008 100644 --- a/src/foundation/core/Inno.Core.Serialization/Context/SerializationContext.cs +++ b/src/foundation/core/Inno.Core.Serialization/Context/SerializationContext.cs @@ -8,9 +8,9 @@ namespace Inno.Core.Serialization; /// public sealed class SerializationContext { - private readonly IReadOnlyDictionary m_values; + private readonly KeyValuePair[] m_values; - private SerializationContext(IReadOnlyDictionary values) + private SerializationContext(KeyValuePair[] values) { m_values = values; } @@ -18,7 +18,7 @@ private SerializationContext(IReadOnlyDictionary values) /// /// Gets an empty serialization context. /// - public static SerializationContext empty { get; } = new(new Dictionary()); + public static SerializationContext empty { get; } = new([]); /// /// Returns a new context containing the supplied value under its declared context type. @@ -38,11 +38,21 @@ private SerializationContext(IReadOnlyDictionary values) public SerializationContext With(TContext value) where TContext : class { ArgumentNullException.ThrowIfNull(value); - var values = new Dictionary(m_values) + Type key = typeof(TContext); + for (int index = 0; index < m_values.Length; index++) { - [typeof(TContext)] = value - }; - return new SerializationContext(values); + if (m_values[index].Key != key) + continue; + if (ReferenceEquals(m_values[index].Value, value)) + return new SerializationContext(m_values); + var updated = (KeyValuePair[])m_values.Clone(); + updated[index] = new(key, value); + return new SerializationContext(updated); + } + var expanded = new KeyValuePair[m_values.Length + 1]; + m_values.CopyTo(expanded, 0); + expanded[^1] = new(key, value); + return new SerializationContext(expanded); } /// @@ -59,10 +69,13 @@ public SerializationContext With(TContext value) where TContext : clas /// public bool TryGet(out TContext? value) where TContext : class { - if (m_values.TryGetValue(typeof(TContext), out object? candidate) && candidate is TContext typed) + foreach (KeyValuePair pair in m_values) { - value = typed; - return true; + if (pair.Key == typeof(TContext) && pair.Value is TContext typed) + { + value = typed; + return true; + } } value = null; diff --git a/src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs b/src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs index c0f00bbd9..eb2f65af2 100644 --- a/src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs +++ b/src/foundation/core/Inno.Core.Serialization/IO/SerializationWriter.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using Inno.Extensibility.Types; +using Inno.Scripting.Api; namespace Inno.Core.Serialization; @@ -9,16 +11,25 @@ namespace Inno.Core.Serialization; public sealed class SerializationWriter { private readonly SerializationOperation m_operation; - private readonly ObjectSerializationNode m_node; + private readonly ObjectSerializationNode? m_previous; + private readonly int m_operationGeneration; + private ObjectSerializationNode? m_node; + private HashSet? m_writtenNames; + private ulong m_writtenMask; + private int m_writtenCount; + private bool m_completed; internal SerializationWriter( SerializationOperation operation, - ObjectSerializationNode node, + ObjectSerializationNode? previous, string path, Type valueType ) { m_operation = operation; - m_node = node; + m_operationGeneration = operation.writerGeneration; + m_previous = previous; + if (previous is null) + m_node = new ObjectSerializationNode(); this.path = path; this.valueType = valueType; } @@ -28,6 +39,26 @@ Type valueType /// public SerializationContext context => m_operation.context; + /// + /// Borrows the immutable type snapshot pinned by this operation's converter generation. + /// + /// + /// Use this snapshot for consistent type identities during a complete capture. It must not be retained beyond + /// the writer's operation; keeping it would retain its collectible types after the converter lease is released. + /// + /// + /// The writer no longer belongs to an active capture. + /// + [ScriptingApiIgnore] + public TypeCacheSnapshot typeSnapshot + { + get + { + EnsureActive(); + return m_operation.types; + } + } + /// /// Gets the current diagnostic path. /// @@ -59,7 +90,17 @@ Type valueType public void Write( string name, TValue value - ) => Write(name, value, typeof(TValue)); + ) { + ValidateName(name); + EnsureActive(); + if (Previous(name) is ScalarSerializationNode scalar && scalar.value.GetType() == typeof(TValue) && + m_operation.ResolveConverter(typeof(TValue)) is null && SerializationNodeReuse.SameScalar(value, scalar.value)) + { + AddNode(name, scalar); + return; + } + Write(name, value, typeof(TValue)); + } /// /// Writes a value using its declared metadata type without constructing a generic method at runtime. @@ -89,7 +130,10 @@ Type declaredType ) { ArgumentNullException.ThrowIfNull(declaredType); ValidateName(name); - AddNode(name, ValuePipeline.Write(value, declaredType, m_operation, AppendPath(name), allowDefaultObject: false)); + EnsureActive(); + SerializationNode? previous = Previous(name); + AddNode(name, ValuePipeline.Write(value, declaredType, m_operation, AppendPath(name), + allowDefaultObject: false, previous)); } /// @@ -110,9 +154,12 @@ Action write ) { ArgumentNullException.ThrowIfNull(write); ValidateName(name); - var child = new ObjectSerializationNode(); - AddNode(name, child); - write(new SerializationWriter(m_operation, child, AppendPath(name), typeof(object))); + EnsureActive(); + SerializationNode? previous = Previous(name); + var child = new SerializationWriter(m_operation, previous as ObjectSerializationNode, AppendPath(name), typeof(object)); + AddNode(name, previous ?? child.m_node!); + write(child); + ReplaceNode(name, child.Complete()); } /// @@ -142,16 +189,27 @@ Action writeElement ArgumentNullException.ThrowIfNull(writeElement); ValidateName(name); - var array = new ArraySerializationNode(); - AddNode(name, array); + EnsureActive(); + SerializationNode? previous = Previous(name); + var array = new SerializationArrayCapture(previous as ArraySerializationNode); + AddNode(name, previous ?? array.initialNode!); int index = 0; - foreach (TValue value in values) + if (values is TValue[] items) + { + for (int itemIndex = 0; itemIndex < items.Length; itemIndex++) + WriteElement(items[itemIndex]); + } + else + foreach (TValue value in values) + WriteElement(value); + ReplaceNode(name, array.Complete()); + + void WriteElement(TValue value) { - var child = new ObjectSerializationNode(); - array.values.Add(child); - writeElement( - new SerializationWriter(m_operation, child, $"{AppendPath(name)}[{index}]", typeof(TValue)), - value); + var child = new SerializationWriter(m_operation, array.Next as ObjectSerializationNode, + array.Next?.capturePath ?? $"{AppendPath(name)}[{index}]", typeof(TValue)); + writeElement(child, value); + array.Add(child.Complete()); index++; } } @@ -168,11 +226,11 @@ Action writeElement public void WriteProperties(ISerializable value) { ArgumentNullException.ThrowIfNull(value); - m_operation.EnsureActive(); + EnsureActive(); m_operation.EnterCapture(value, path); try { - ValuePipeline.WriteProperties(value, m_node, m_operation, path); + ValuePipeline.WriteProperties(value, this, m_operation); } finally { @@ -180,16 +238,135 @@ public void WriteProperties(ISerializable value) } } + /// + /// Writes independently restorable property bytes through the current converter generation. + /// + /// + /// The unique member receiving the binary property snapshot. + /// + /// + /// The object whose persistent properties are captured once. + /// + /// + /// The complete owner context, or null to use this writer's context. + /// + /// + /// The source object is null. + /// + /// + /// The writer has ended, a key is duplicated, or capture fails. + /// + /// + /// The member name is empty. + /// + public void WritePropertiesData( + string name, + ISerializable value, + SerializationContext? context = null + ) { + ArgumentNullException.ThrowIfNull(value); + ValidateName(name); + EnsureActive(); + AddNode(name, m_operation.CapturePropertyData(value, context ?? this.context, Previous(name) as BinarySerializationNode)); + } + + internal ObjectSerializationNode Complete() + { + EnsureActive(); + m_completed = true; + if (m_previous is not null && (m_node is not null || m_writtenCount != m_previous.values.Count)) + { + EnsureMutable(); + for (int index = 0; index < m_previous!.values.Count; index++) + { + string name = m_previous!.values.GetAt(index).Key; + if (m_previous!.values.Count <= 64 ? (m_writtenMask & (1UL << index)) == 0 : m_writtenNames?.Contains(name) != true) + m_node!.values.Remove(name); + } + } + ObjectSerializationNode result = m_node ?? m_previous ?? new ObjectSerializationNode(); + result.RememberPath(path); + return result; + } + + private SerializationNode? Previous(string name) + { + if (m_previous is not null && m_writtenCount < m_previous.values.Count && + string.Equals(m_previous!.values.GetAt(m_writtenCount).Key, name, StringComparison.Ordinal)) + return m_previous!.values.GetAt(m_writtenCount).Value; + return m_previous is not null && m_previous.values.TryGetValue(name, out SerializationNode? value) ? value : null; + } + private void AddNode( string name, SerializationNode node ) { - m_operation.EnsureActive(); - if (!m_node.values.TryAdd(name, node)) + EnsureActive(); + if (m_previous is null) + { + m_node ??= new ObjectSerializationNode(); + if (!m_node.values.TryAdd(name, node)) + throw new InvalidOperationException($"Serialization object '{path}' already contains key '{name}'."); + return; + } + int index = -1; + if (m_previous!.values.Count <= 64) + { + if (m_previous is not null && m_writtenCount < m_previous.values.Count && + string.Equals(m_previous!.values.GetAt(m_writtenCount).Key, name, StringComparison.Ordinal)) + index = m_writtenCount; + else + index = m_previous!.values.IndexOf(name); + } + bool duplicate; + if (index >= 0) + { + ulong bit = 1UL << index; + duplicate = (m_writtenMask & bit) != 0; + m_writtenMask |= bit; + } + else + duplicate = !(m_writtenNames ??= new(StringComparer.Ordinal)).Add(name); + if (duplicate) throw new InvalidOperationException($"Serialization object '{path}' already contains key '{name}'."); + SerializationNode? previous = index >= 0 ? m_previous!.values.GetAt(index).Value : Previous(name); + m_writtenCount++; + if (!ReferenceEquals(previous, node)) + { + EnsureMutable(); + m_node!.values[name] = node; + } + } + + private void EnsureMutable() + { + if (m_node is not null) + return; + m_node = m_previous is null ? new ObjectSerializationNode() : SerializationNodeReuse.CopyObject(m_previous); + } + + private void ReplaceNode( + string name, + SerializationNode value + ) { + EnsureActive(); + if (!ReferenceEquals(Previous(name), value)) + { + EnsureMutable(); + m_node!.values[name] = value; + } + } + + private void EnsureActive() + { + m_operation.EnsureActive(); + if (m_operationGeneration != m_operation.writerGeneration) + throw new InvalidOperationException("The serialization writer belongs to a completed property capture."); + if (m_completed && m_operation.isSnapshotCapture) + throw new InvalidOperationException("The serialization writer callback has already completed."); } - private string AppendPath(string name) => path == "$" ? $"$.{name}" : $"{path}.{name}"; + private string AppendPath(string name) => Previous(name)?.capturePath ?? (path == "$" ? $"$.{name}" : $"{path}.{name}"); private static void ValidateName(string name) { diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs index d07a0f003..7f01d2221 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/BinarySerializationFormat.cs @@ -13,6 +13,16 @@ internal static partial class BinarySerializationFormat internal static byte[] Encode(SerializationNode root) => Encode(root, includeMagic: true); + internal static void WriteTo( + Stream destination, + SerializationNode root + ) { + using var writer = new BinaryWriter(destination, Encoding.UTF8, leaveOpen: true); + writer.Write(C_MAGIC); + WriteNode(writer, root); + writer.Flush(); + } + internal static SerializationNode Decode(ReadOnlySpan bytes) { try diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs b/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs index 0bf3ae028..631c8957e 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/ConverterRegistry.cs @@ -297,6 +297,8 @@ TypeCacheSnapshot types internal IReadOnlyList registrations = registrations; private int m_referenceCount = 1; + internal TypeCacheSnapshot types => m_types ?? throw new ObjectDisposedException(nameof(ConverterRegistrySnapshot)); + internal object CreateInstance(Type type) => (m_types ?? throw new ObjectDisposedException(nameof(ConverterRegistrySnapshot))).CreateInstance(type); @@ -367,6 +369,8 @@ internal ConverterRegistryLease(ConverterRegistry.ConverterRegistrySnapshot snap m_snapshot = snapshot; } + internal TypeCacheSnapshot types => (m_snapshot ?? throw new ObjectDisposedException(nameof(ConverterRegistryLease))).types; + internal ConverterInvoker? Resolve(Type valueType) { ArgumentNullException.ThrowIfNull(valueType); @@ -424,7 +428,8 @@ internal abstract SerializationNode Write( SerializationOperation operation, string path, Type valueType, - object value + object value, + SerializationNode? previous ); internal abstract object Read( @@ -460,7 +465,8 @@ internal override SerializationNode Write( SerializationOperation operation, string path, Type valueType, - object value + object value, + SerializationNode? previous ) { if (value is not T typed) { @@ -468,9 +474,9 @@ object value $"Converter '{converterType.FullName}' cannot write runtime value '{value.GetType().FullName}' at '{path}'."); } - var node = new ObjectSerializationNode(); - m_converter.Write(new SerializationWriter(operation, node, path, valueType), typed); - return node; + var writer = new SerializationWriter(operation, previous as ObjectSerializationNode, path, valueType); + m_converter.Write(writer, typed); + return writer.Complete(); } internal override object Read( diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/PropertyDataCapture.cs b/src/foundation/core/Inno.Core.Serialization/Internal/PropertyDataCapture.cs new file mode 100644 index 000000000..a335fb197 --- /dev/null +++ b/src/foundation/core/Inno.Core.Serialization/Internal/PropertyDataCapture.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; + +namespace Inno.Core.Serialization; + +internal sealed class PropertyDataCapture(KeyValuePair[] members) +{ + internal KeyValuePair[] members { get; } = members; +} diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotBinaryFormat.cs b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotBinaryFormat.cs index 6ac0b36b4..e35ad6fa5 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotBinaryFormat.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotBinaryFormat.cs @@ -9,6 +9,23 @@ internal static class PropertySnapshotBinaryFormat { private const string C_MAGIC = "INNO-PROPERTY-SNAPSHOT-CURRENT"; + internal static byte[] Encode(IReadOnlyList> members) + { + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); + writer.Write(C_MAGIC); + writer.Write(members.Count); + foreach (var member in members) + { + byte[] data = BinarySerializationFormat.Encode(member.Value); + writer.Write(member.Key); + writer.Write(data.Length); + writer.Write(data); + } + writer.Flush(); + return stream.ToArray(); + } + internal static byte[] Encode(IReadOnlyList snapshots) { using var stream = new MemoryStream(); diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs index ca0dfb65f..c810ec53c 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/PropertySnapshotPipeline.cs @@ -5,6 +5,55 @@ namespace Inno.Core.Serialization; internal static class PropertySnapshotPipeline { + internal static BinarySerializationNode CaptureData( + ISerializable value, + SerializationContext context, + ConverterRegistryLease converters, + BinarySerializationNode? previous + ) { + IReadOnlyList members = context.GetRequired().GetMetadata(value.GetType()).members; + KeyValuePair[]? before = previous?.properties?.members; + List>? captured = before is null ? [] : null; + int count = 0; + var operation = new SerializationOperation(context, converters, snapshotCapture: previous is not null); + try + { + for (int index = 0; index < members.Count; index++) + { + SerializationMemberMetadata member = members[index]; + if ((member.visibility & PropertyVisibility.Serialize) == 0) + continue; + SerializationNode? prior = before is not null && count < before.Length && + string.Equals(before[count].Key, member.name, StringComparison.Ordinal) + ? before[count].Value + : null; + SerializationNode node = ValuePipeline.Write(member.GetValue(value), member.type, operation, + prior?.capturePath ?? AppendPath(member.name), allowDefaultObject: false, prior); + operation.RetireCaptureMember(); + if (captured is null && !ReferenceEquals(node, prior)) + { + captured = new(before!); + captured.RemoveRange(count, captured.Count - count); + } + captured?.Add(new(member.name, node)); + count++; + } + } + finally + { + operation.Fail(); + } + if (captured is null && before is not null && count == before.Length) + return previous!; + if (captured is null) + { + captured = new(before!); + captured.RemoveRange(count, captured.Count - count); + } + var state = new PropertyDataCapture([.. captured]); + return new BinarySerializationNode(PropertySnapshotBinaryFormat.Encode(state.members)) { properties = state }; + } + internal static SerializationPropertySnapshot CaptureProperty( ISerializable value, string propertyName, diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationArrayCapture.cs b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationArrayCapture.cs new file mode 100644 index 000000000..327c1118a --- /dev/null +++ b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationArrayCapture.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; + +namespace Inno.Core.Serialization; + +internal struct SerializationArrayCapture(ArraySerializationNode? previous) +{ + private readonly ArraySerializationNode? m_initialNode = previous is null ? new ArraySerializationNode() : null; + private List? m_values; + private int m_count; + + internal SerializationNode? Next => previous is not null && m_count < previous.values.Count + ? previous.values[m_count] + : null; + + internal ArraySerializationNode? initialNode => m_initialNode; + + internal void Add(SerializationNode value) + { + if (m_initialNode is not null) + m_values = m_initialNode.values; + if (m_values is null && !ReferenceEquals(Next, value)) + m_values = previous is null ? [] : new(previous.values); + if (m_values is not null) + { + if (m_count < m_values.Count) + m_values[m_count] = value; + else + m_values.Add(value); + } + m_count++; + } + + internal ArraySerializationNode Complete() + { + if (m_initialNode is not null) + return m_initialNode; + if (previous is not null && m_count == previous.values.Count && m_values is null) + return previous; + m_values ??= previous is null ? [] : new(previous.values); + if (m_values.Count > m_count) + m_values.RemoveRange(m_count, m_values.Count - m_count); + return new ArraySerializationNode(m_values); + } +} diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNode.cs b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNode.cs index 6a16f838f..713e51a83 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNode.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNode.cs @@ -1,9 +1,21 @@ using System; using System.Collections.Generic; +using System.Threading; namespace Inno.Core.Serialization; -internal abstract record SerializationNode; +internal abstract record SerializationNode +{ + private string? m_capturePath; + + internal string? capturePath => Volatile.Read(ref m_capturePath); + + internal void RememberPath(string path) + { + if (this is not NullSerializationNode) + Interlocked.CompareExchange(ref m_capturePath, path, null); + } +} internal sealed record NullSerializationNode : SerializationNode { @@ -16,16 +28,23 @@ private NullSerializationNode() internal sealed record ScalarSerializationNode(object value) : SerializationNode; -internal sealed record BinarySerializationNode(byte[] value) : SerializationNode; +internal sealed record BinarySerializationNode(byte[] value) : SerializationNode +{ + internal PropertyDataCapture? properties { get; init; } +} internal sealed record ObjectSerializationNode : SerializationNode { - internal Dictionary values { get; } = new(StringComparer.Ordinal); + internal OrderedDictionary values { get; } = new(StringComparer.Ordinal); } internal sealed record ArraySerializationNode : SerializationNode { - internal List values { get; } = []; + internal ArraySerializationNode() : this([]) { } + + internal ArraySerializationNode(List values) => this.values = values; + + internal List values { get; } } internal sealed record MapSerializationNode : SerializationNode diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNodeReuse.cs b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNodeReuse.cs new file mode 100644 index 000000000..3ecb1b76e --- /dev/null +++ b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationNodeReuse.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Inno.Core.Serialization; + +internal static class SerializationNodeReuse +{ + internal static bool SameScalar( + T value, + object previous + ) { + if (typeof(T) == typeof(float)) + return BitConverter.SingleToInt32Bits(Unsafe.As(ref value)) == BitConverter.SingleToInt32Bits((float)previous); + if (typeof(T) == typeof(double)) + return BitConverter.DoubleToInt64Bits(Unsafe.As(ref value)) == BitConverter.DoubleToInt64Bits((double)previous); + if (typeof(T) == typeof(decimal)) + return SameScalar((object)Unsafe.As(ref value), previous); + return EqualityComparer.Default.Equals(value, (T)previous); + } + + internal static ObjectSerializationNode CopyObject(ObjectSerializationNode previous) + { + var result = new ObjectSerializationNode(); + result.values.EnsureCapacity(previous.values.Count); + foreach (var pair in previous.values) + result.values.Add(pair.Key, pair.Value); + return result; + } + + internal static SerializationNode Scalar( + object value, + SerializationNode? previous + ) => previous is ScalarSerializationNode scalar && SameScalar(value, scalar.value) + ? scalar + : new ScalarSerializationNode(value); + + internal static SerializationNode Binary( + byte[] value, + SerializationNode? previous + ) => previous is BinarySerializationNode binary && value.AsSpan().SequenceEqual(binary.value) + ? binary + : new BinarySerializationNode((byte[])value.Clone()); + + internal static bool SameScalar( + object value, + object previous + ) { + if (value.GetType() != previous.GetType()) + return false; + if (value is float number) + return BitConverter.SingleToInt32Bits(number) == BitConverter.SingleToInt32Bits((float)previous); + if (value is double real) + return BitConverter.DoubleToInt64Bits(real) == BitConverter.DoubleToInt64Bits((double)previous); + if (value is decimal precision) + { + Span left = stackalloc int[4]; + Span right = stackalloc int[4]; + decimal.GetBits(precision, left); + decimal.GetBits((decimal)previous, right); + return left.SequenceEqual(right); + } + return value.Equals(previous); + } +} diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs index 5d1b0a9e0..1800ae241 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/SerializationOperation.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Inno.Extensibility.Types; namespace Inno.Core.Serialization; @@ -11,17 +12,33 @@ internal sealed class SerializationOperation private List? m_scheduledObjectOrder; private Dictionary? m_capturePaths; private bool m_isActive = true; + private int m_writerGeneration; internal SerializationOperation( SerializationContext context, - ConverterRegistryLease converters + ConverterRegistryLease converters, + bool snapshotCapture = false ) { ArgumentNullException.ThrowIfNull(converters); this.context = context; m_converters = converters; + isSnapshotCapture = snapshotCapture; } internal SerializationContext context { get; } + internal bool isSnapshotCapture { get; } + internal int writerGeneration => m_writerGeneration; + internal TypeCacheSnapshot types => m_converters.types; + + internal void RetireCaptureMember() + { + EnsureActive(); + m_writerGeneration = checked(m_writerGeneration + 1); + m_completionCallbacks?.Clear(); + m_scheduledObjects?.Clear(); + m_scheduledObjectOrder?.Clear(); + m_capturePaths?.Clear(); + } internal SerializationTypeMetadata GetMetadata(Type type) => context.GetRequired().GetMetadata(type); @@ -38,6 +55,15 @@ internal void EnsureActive() throw new InvalidOperationException("The serialization reader or writer is no longer active."); } + internal BinarySerializationNode CapturePropertyData( + ISerializable value, + SerializationContext context, + BinarySerializationNode? previous + ) { + EnsureActive(); + return PropertySnapshotPipeline.CaptureData(value, context, m_converters, previous); + } + internal void AddCompletionCallback(Action callback) { ArgumentNullException.ThrowIfNull(callback); diff --git a/src/foundation/core/Inno.Core.Serialization/Internal/ValuePipeline.cs b/src/foundation/core/Inno.Core.Serialization/Internal/ValuePipeline.cs index 98e1d0459..fd36ee9b0 100644 --- a/src/foundation/core/Inno.Core.Serialization/Internal/ValuePipeline.cs +++ b/src/foundation/core/Inno.Core.Serialization/Internal/ValuePipeline.cs @@ -10,14 +10,15 @@ internal static class ValuePipeline internal static SerializationNode WriteRoot( ISerializable value, Type declaredType, - SerializationOperation operation + SerializationOperation operation, + SerializationNode? previous = null ) { operation.EnterCapture(value, "$"); try { ConverterInvoker? converter = operation.ResolveConverter(declaredType); if (converter is not null) - return converter.Write(operation, "$", declaredType, value); + return converter.Write(operation, "$", declaredType, value, previous); if (operation.GetMetadata(declaredType).requiresConverter) throw new InvalidOperationException( $"Serializable type '{declaredType.FullName}' requires an explicit serialization converter."); @@ -28,9 +29,9 @@ SerializationOperation operation $"Default root serialization requires exact type '{declaredType.FullName}', but the runtime value is '{value.GetType().FullName}'. Mark the root type with RequiresSerializationConverter and register a converter for polymorphism."); } EnsureDefaultSerializableType(declaredType, operation); - var node = new ObjectSerializationNode(); - WriteProperties(value, node, operation, "$"); - return node; + var writer = new SerializationWriter(operation, previous as ObjectSerializationNode, "$", declaredType); + WriteProperties(value, writer, operation); + return writer.Complete(); } finally { @@ -81,7 +82,26 @@ internal static SerializationNode Write( Type declaredType, SerializationOperation operation, string path, - bool allowDefaultObject + bool allowDefaultObject, + SerializationNode? previous = null + ) { + operation.EnsureActive(); + if (previous is ScalarSerializationNode scalar && value is not null && + scalar.value.GetType() == declaredType && value.GetType() == declaredType && + operation.ResolveConverter(declaredType) is null && SerializationNodeReuse.SameScalar(value, scalar.value)) + return scalar; + SerializationNode node = WriteValue(value, declaredType, operation, path, allowDefaultObject, previous); + node.RememberPath(path); + return node; + } + + private static SerializationNode WriteValue( + object? value, + Type declaredType, + SerializationOperation operation, + string path, + bool allowDefaultObject, + SerializationNode? previous = null ) { operation.EnsureActive(); Type? nullableType = Nullable.GetUnderlyingType(declaredType); @@ -100,22 +120,22 @@ bool allowDefaultObject { ConverterInvoker? converter = operation.ResolveConverter(valueType); if (converter is not null) - return converter.Write(operation, path, valueType, value); + return converter.Write(operation, path, valueType, value, previous); if (IsPrimitive(valueType)) - return new ScalarSerializationNode(NormalizePrimitive(value, valueType, path)); + return SerializationNodeReuse.Scalar(NormalizePrimitive(value, valueType, path), previous); if (valueType.IsEnum) - return new ScalarSerializationNode(Convert.ToInt64(value, CultureInfo.InvariantCulture)); + return SerializationNodeReuse.Scalar(Convert.ToInt64(value, CultureInfo.InvariantCulture), previous); if (valueType == typeof(byte[])) - return new BinarySerializationNode((byte[])((byte[])value).Clone()); + return SerializationNodeReuse.Binary((byte[])value, previous); if (valueType.IsArray) - return WriteArray((Array)value, valueType, operation, path); + return WriteArray((Array)value, valueType, operation, path, previous as ArraySerializationNode); if (operation.GetMetadata(valueType).collection is { keyType: not null } map) - return WriteMap(value, valueType, map.keyType, map.elementType, operation, path); + return WriteMap(value, valueType, map.keyType, map.elementType, operation, path, previous); if (operation.GetMetadata(valueType).collection is { keyType: null } sequence) - return WriteSequence(value, sequence.elementType, operation, path); + return WriteSequence(value, sequence.elementType, operation, path, previous as ArraySerializationNode); if (valueType.IsValueType) - return WriteStruct(value, valueType, operation, path); + return WriteStruct(value, valueType, operation, path, previous as ObjectSerializationNode); if (allowDefaultObject && value is ISerializable serializable) { @@ -125,9 +145,9 @@ bool allowDefaultObject $"Default serialization at '{path}' requires exact type '{valueType.FullName}', but the runtime value is '{value.GetType().FullName}'. Register a converter for polymorphic values."); } EnsureDefaultSerializableType(valueType, operation); - var node = new ObjectSerializationNode(); - WriteProperties(serializable, node, operation, path); - return node; + var writer = new SerializationWriter(operation, previous as ObjectSerializationNode, path, valueType); + WriteProperties(serializable, writer, operation); + return writer.Complete(); } throw new InvalidOperationException( @@ -192,9 +212,8 @@ bool allowDefaultObject internal static void WriteProperties( ISerializable value, - ObjectSerializationNode node, - SerializationOperation operation, - string path + SerializationWriter writer, + SerializationOperation operation ) { IReadOnlyList members = operation.GetMetadata(value.GetType()).members; for (int i = 0; i < members.Count; i++) @@ -202,15 +221,7 @@ string path SerializationMemberMetadata member = members[i]; if ((member.visibility & PropertyVisibility.Serialize) == 0) continue; - string memberPath = AppendPath(path, member.name); - SerializationNode memberNode = Write( - member.GetValue(value), - member.type, - operation, - memberPath, - allowDefaultObject: false); - if (!node.values.TryAdd(member.name, memberNode)) - throw new InvalidOperationException($"Serialization object '{path}' already contains key '{member.name}'."); + writer.Write(member.name, member.GetValue(value), member.type); } } @@ -242,15 +253,16 @@ private static SerializationNode WriteArray( Array value, Type arrayType, SerializationOperation operation, - string path + string path, + ArraySerializationNode? previous ) { if (arrayType.GetArrayRank() != 1) throw new InvalidOperationException($"Only one-dimensional arrays are supported at '{path}'."); Type elementType = arrayType.GetElementType()!; - var node = new ArraySerializationNode(); + var capture = new SerializationArrayCapture(previous); for (int i = 0; i < value.Length; i++) - node.values.Add(Write(value.GetValue(i), elementType, operation, $"{path}[{i}]", false)); - return node; + capture.Add(Write(value.GetValue(i), elementType, operation, capture.Next?.capturePath ?? $"{path}[{i}]", false, capture.Next)); + return capture.Complete(); } private static object ReadArray( @@ -274,18 +286,19 @@ private static SerializationNode WriteSequence( object value, Type elementType, SerializationOperation operation, - string path + string path, + ArraySerializationNode? previous ) { if (value is not IEnumerable enumerable) throw new InvalidOperationException($"Sequence value '{path}' is not enumerable."); - var node = new ArraySerializationNode(); + var capture = new SerializationArrayCapture(previous); int index = 0; foreach (object? element in enumerable) { - node.values.Add(Write(element, elementType, operation, $"{path}[{index}]", false)); + capture.Add(Write(element, elementType, operation, capture.Next?.capturePath ?? $"{path}[{index}]", false, capture.Next)); index++; } - return node; + return capture.Complete(); } private static object ReadSequence( @@ -309,7 +322,8 @@ private static SerializationNode WriteMap( Type keyType, Type mapValueType, SerializationOperation operation, - string path + string path, + SerializationNode? previous ) { IReadOnlyList> entries = operation.GetMetadata(mapType).collection!.enumerateMap!(value); var node = new MapSerializationNode(); @@ -322,7 +336,10 @@ string path Write(entry.Key, keyType, operation, $"{path}[{i}].key", false), Write(entry.Value, mapValueType, operation, $"{path}[{i}].value", false))); } - return node; + return previous is MapSerializationNode && + BinarySerializationFormat.Encode(node).AsSpan().SequenceEqual(BinarySerializationFormat.Encode(previous)) + ? previous + : node; } private static object ReadMap( @@ -349,20 +366,46 @@ private static SerializationNode WriteStruct( object value, Type structType, SerializationOperation operation, - string path + string path, + ObjectSerializationNode? previous ) { IReadOnlyList members = operation.GetMetadata(structType).members; - var node = new ObjectSerializationNode(); + ObjectSerializationNode? node = previous is null ? new ObjectSerializationNode() : null; + int count = 0; for (int i = 0; i < members.Count; i++) { SerializationMemberMetadata member = members[i]; if ((member.visibility & PropertyVisibility.Serialize) == 0) continue; - node.values.Add( - member.name, - Write(member.GetValue(value), member.type, operation, AppendPath(path, member.name), false)); + SerializationNode? prior = previous is not null && previous.values.TryGetValue(member.name, out SerializationNode? found) + ? found + : null; + SerializationNode captured = Write(member.GetValue(value), member.type, operation, + prior?.capturePath ?? AppendPath(path, member.name), false, prior); + if (!ReferenceEquals(prior, captured)) + { + node ??= SerializationNodeReuse.CopyObject(previous!); + node.values[member.name] = captured; + } + count++; } - return node; + if (previous is not null && (node is not null || count != previous.values.Count)) + { + node ??= SerializationNodeReuse.CopyObject(previous); + foreach (string name in previous.values.Keys) + { + bool included = false; + for (int index = 0; index < members.Count; index++) + if (members[index].name == name && (members[index].visibility & PropertyVisibility.Serialize) != 0) + { + included = true; + break; + } + if (!included) + node.values.Remove(name); + } + } + return node ?? previous!; } private static object ReadStruct( diff --git a/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs b/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs index 8aba706ff..cdadfaa2e 100644 --- a/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs +++ b/src/foundation/core/Inno.Core.Serialization/SerializationRegistry.cs @@ -218,8 +218,7 @@ public byte[] CapturePropertiesData( EnsureInitialized(); ArgumentNullException.ThrowIfNull(value); using ConverterRegistryLease converters = m_converters.Capture(); - return PropertySnapshotBinaryFormat.Encode( - PropertySnapshotPipeline.Capture(value, CreateContext(context), converters)); + return PropertySnapshotPipeline.CaptureData(value, CreateContext(context), converters, null).value; } /// @@ -373,6 +372,60 @@ public byte[] Serialize( return Serialize(value, converters, context); } + /// + /// Captures complete serialized state while sharing unchanged neutral values with a previous capture. + /// + /// + /// The exact concrete root type, interpreted by the current converter generation. + /// + /// + /// The non-null root whose getters and converters are evaluated in full. + /// + /// + /// The complete owner context, including required reference resolvers. + /// + /// + /// An optional immutable capture to reuse; it is never changed or retained by this registry. + /// + /// + /// The previous instance when all captured values can be reused, otherwise a new immutable snapshot. + /// Reference equality proves unchanged bytes; different instances may still encode identically. + /// Ordinary serialization and snapshot encoding produce identical bytes. Capture failure publishes no snapshot. + /// + /// + /// Writers borrowed by a snapshot converter belong to its synchronous callback and must not be used after it returns. + /// Every capture still evaluates getters and converters; this is not a mutation-notification cache. + /// + /// + /// The source value is null. + /// + /// + /// The registry has been disposed. + /// + /// + /// A converter, unsupported value or reference context prevents capture. + /// + [ScriptingApiIgnore] + public SerializationSnapshot CaptureSnapshot( + T value, + SerializationContext? context = null, + SerializationSnapshot? previous = null + ) where T : class, ISerializable { + EnsureInitialized(); + ArgumentNullException.ThrowIfNull(value); + using ConverterRegistryLease converters = m_converters.Capture(); + var operation = new SerializationOperation(CreateContext(context), converters, snapshotCapture: true); + try + { + SerializationNode root = ValuePipeline.WriteRoot(value, typeof(T), operation, previous?.root); + return ReferenceEquals(root, previous?.root) ? previous! : new SerializationSnapshot(root); + } + finally + { + operation.Fail(); + } + } + internal byte[] Serialize( T value, ConverterRegistryLease converters, @@ -529,9 +582,9 @@ internal byte[] Encode( var operation = new SerializationOperation(CreateContext(context), converters); try { - var root = new ObjectSerializationNode(); - write(new SerializationWriter(operation, root, "$", typeof(object))); - return BinarySerializationFormat.Encode(root); + var writer = new SerializationWriter(operation, null, "$", typeof(object)); + write(writer); + return BinarySerializationFormat.Encode(writer.Complete()); } finally { diff --git a/src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs b/src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs new file mode 100644 index 000000000..1dec6efbe --- /dev/null +++ b/src/foundation/core/Inno.Core.Serialization/SerializationSnapshot.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; + +namespace Inno.Core.Serialization; + +/// +/// Holds an immutable, neutral capture of serialized values without retaining the source object or its generation. +/// +/// +/// Capture again through to reuse unchanged values. +/// Readers may share a snapshot across threads. It owns only scalar values, strings and copied binary data, +/// never source instances, metadata, converters or delegates. It is transient and is not a persistence format. +/// +public sealed class SerializationSnapshot +{ + internal SerializationSnapshot(SerializationNode root) => this.root = root; + + /// + /// Encodes this capture using the same deterministic binary format as ordinary serialization. + /// + /// + /// A new caller-owned byte array; changing it cannot modify this snapshot. + /// + public byte[] ToArray() => BinarySerializationFormat.Encode(root); + + /// + /// Streams this capture in the ordinary deterministic binary format without allocating the complete output payload. + /// + /// + /// The writable caller-owned stream, which remains open. + /// + /// + /// The destination is null. + /// + /// + /// The destination cannot be written. + /// + /// + /// The destination rejects a write; its partial bytes remain the caller's responsibility. + /// + public void WriteTo(Stream destination) + { + ArgumentNullException.ThrowIfNull(destination); + if (!destination.CanWrite) + throw new ArgumentException("A writable serialization destination is required.", nameof(destination)); + BinarySerializationFormat.WriteTo(destination, root); + } + + internal SerializationNode root { get; } +} diff --git a/tests/Invoke-EditorUiAcceptance.ps1 b/tests/Invoke-EditorUiAcceptance.ps1 index 6e54f32d7..96c0d5a0f 100644 --- a/tests/Invoke-EditorUiAcceptance.ps1 +++ b/tests/Invoke-EditorUiAcceptance.ps1 @@ -84,8 +84,11 @@ foreach ($selected in $profiles) { & (Join-Path $PSScriptRoot 'Invoke-EditorSmoke.ps1') -Dotnet $Dotnet -EditorAssembly $product.TargetPath ` -ProjectDirectory $ProductProject -GraphicsApi $api -BackendPattern $BackendPattern -Frames 300 ` *> (Join-Path $stageRoot "$api.log") + & (Join-Path $PSScriptRoot 'Invoke-EditorUiProductJourney.ps1') -Dotnet $Dotnet -EditorAssembly $product.TargetPath ` + -ProjectDirectory $ProductProject -GraphicsApi $api -ResultsDirectory (Join-Path $stageRoot "journey-$api") ` + *> (Join-Path $stageRoot "journey-$api.log") } - Save-Result $selected 'Passed' 'Editor rendering smoke completed; interactive journeys remain separately tracked.' + Save-Result $selected 'Passed' 'Renderer smoke and eight actual-product feature workflows completed; native input and physical-device experience remain separately tracked.' continue } $names = switch ($selected) { diff --git a/tests/Invoke-EditorUiProductJourney.ps1 b/tests/Invoke-EditorUiProductJourney.ps1 new file mode 100644 index 000000000..d93bb655e --- /dev/null +++ b/tests/Invoke-EditorUiProductJourney.ps1 @@ -0,0 +1,94 @@ +param( + [Parameter(Mandatory)][string]$EditorAssembly, + [Parameter(Mandatory)][string]$ProjectDirectory, + [Parameter(Mandatory)][string]$ResultsDirectory, + [string]$Dotnet = 'dotnet', + [Parameter(Mandatory)][string]$GraphicsApi, + [int]$Frames = 30000, + [int]$TimeoutSeconds = 600 +) + +$ErrorActionPreference = 'Stop' +if ($Frames -lt 120 -or $TimeoutSeconds -le 0) { throw 'The journey needs at least 120 frames and a positive deadline.' } +$repository = Split-Path $PSScriptRoot -Parent +$source = [IO.Path]::GetFullPath($ProjectDirectory) +$editor = [IO.Path]::GetFullPath($EditorAssembly) +$Dotnet = (Get-Command $Dotnet -ErrorAction Stop).Source +if (-not (Test-Path -LiteralPath $editor -PathType Leaf)) { throw 'Build the selected official Editor output first.' } +if (-not (Test-Path -LiteralPath (Join-Path $source 'Assets') -PathType Container)) { throw 'A complete project input is required.' } +$results = [IO.Path]::GetFullPath($ResultsDirectory) +if (Test-Path -LiteralPath $results) { throw 'Use a new result owner; prior evidence must not be overwritten.' } +New-Item -ItemType Directory -Path $results | Out-Null +$project = Join-Path $results 'Project' +New-Item -ItemType Directory -Path $project | Out-Null +foreach ($name in @('Assets', 'Plugins')) { + $directory = Join-Path $source $name + if (Test-Path -LiteralPath $directory) { Copy-Item -LiteralPath $directory -Destination $project -Recurse } +} +foreach ($file in Get-ChildItem -LiteralPath $source -File | Where-Object { $_.Extension -eq '.inno' }) { + Copy-Item -LiteralPath $file.FullName -Destination $project +} +Copy-Item -LiteralPath (Join-Path $repository 'src/composition/editor/hosting/Inno.Editor.Hosting/Layout/DefaultEditorLayout.ini') ` + -Destination (Join-Path $project 'editor.ini') +$fixtures = Join-Path $project 'Assets/Acceptance' +New-Item -ItemType Directory -Force -Path $fixtures | Out-Null +foreach ($name in @('WorkflowState.cs', 'WorkflowJourney.editor.cs')) { + Copy-Item -LiteralPath (Join-Path $PSScriptRoot "fixtures/editor-ui/$name.txt") -Destination (Join-Path $fixtures $name) +} +Get-ChildItem -LiteralPath $project -File -Recurse | ForEach-Object { + [pscustomobject]@{ path = [IO.Path]::GetRelativePath($project, $_.FullName); sha256 = (Get-FileHash -LiteralPath $_.FullName).Hash } +} | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (Join-Path $results 'inputs.json') -Encoding utf8 + +$start = [Diagnostics.ProcessStartInfo]::new($Dotnet) +$start.UseShellExecute = $false +$start.CreateNoWindow = $true +$start.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden +$start.RedirectStandardOutput = $true +$start.RedirectStandardError = $true +$start.WorkingDirectory = $project +foreach ($argument in @($editor, $project, '--window-visible', 'false', '--smoke-frames', [string]$Frames, + '--graphics-api', $GraphicsApi)) { $start.ArgumentList.Add($argument) } +$process = [Diagnostics.Process]::new() +$process.StartInfo = $start +$code = $null +$failure = $null +$started = $false +try { + if (-not $process.Start()) { throw 'The owned Editor process did not start.' } + $started = $true + $output = $process.StandardOutput.ReadToEndAsync() + $errors = $process.StandardError.ReadToEndAsync() + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { throw 'The owned Editor journey exceeded its deadline.' } + $code = $process.ExitCode + if ($code -ne 0) { throw "The owned Editor exited with code $code." } + $evidence = Join-Path $project 'Library/Acceptance/workflow-journey.tsv' + if (-not (Test-Path -LiteralPath $evidence)) { throw 'The source extension did not complete the journey.' } + $verified = @(Get-Content -LiteralPath $evidence) + if ($verified.Count -ne 8) { throw "Expected eight completed workflow stages, got $($verified.Count)." } +} +catch { $failure = $_ } +finally { + if ($started -and -not $process.HasExited) { + $process.Kill($true) + if (-not $process.WaitForExit(10000)) { throw 'The owned Editor process did not terminate.' } + } + if ($output) { + $output.WaitAsync([TimeSpan]::FromSeconds(10)).GetAwaiter().GetResult() | + Set-Content -LiteralPath (Join-Path $results 'stdout.log') -Encoding utf8 + } + if ($errors) { + $errors.WaitAsync([TimeSpan]::FromSeconds(10)).GetAwaiter().GetResult() | + Set-Content -LiteralPath (Join-Path $results 'stderr.log') -Encoding utf8 + } + $process.Dispose() + [pscustomobject]@{ + revision = (& git -C $repository rev-parse HEAD).Trim() + workingChanges = @(& git -C $repository status --porcelain=v1) + editor = $editor; graphicsApi = $GraphicsApi; frames = $Frames; exitCode = $code + status = $(if ($failure) { 'Failed' } else { 'Passed' }) + diagnostic = $(if ($failure) { $failure.Exception.Message } else { 'Eight actual-product feature workflows completed.' }) + limitation = 'Public feature commands with real panel drawing; native pointer/keyboard contracts and physical-device experience are separate evidence.' + } | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $results 'result.json') -Encoding utf8 +} +if ($failure) { throw $failure } +Write-Output "Verified eight product workflow stages: $results" diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs index bbed2954a..1b58c184c 100644 --- a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Efficiency.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using Inno.Adapter.Serialization.DotNet; using Inno.Extensibility.Types; using Xunit; @@ -7,6 +9,25 @@ namespace Inno.Core.Serialization.Tests; public sealed partial class SerializationBehaviorTests { + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ReplacingContextServicesReleasesTheReplacedValueAtEveryDepth(bool nested) + { + (SerializationContext context, WeakReference replaced) = ReplaceContextService(nested); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(replaced.IsAlive); + Assert.NotNull(context.GetRequired()); + if (nested) + { + Assert.Equal("marker", context.GetRequired()); + Assert.Equal(new[] { 1, 2 }, context.GetRequired>()); + } + GC.KeepAlive(context); + } + [Fact] public void PreparedSerializationContextsAvoidRepeatedOwnerCopies() { @@ -17,7 +38,8 @@ public void PreparedSerializationContextsAvoidRepeatedOwnerCopies() long fresh = Measure(null); long inherited = Measure(prepared); - Assert.True(inherited + 256 <= fresh, $"Prepared context allocated {inherited} bytes; fresh context allocated {fresh} bytes."); + Assert.True(inherited < fresh && inherited <= 1024, + $"Prepared context allocated {inherited} bytes; fresh context allocated {fresh} bytes."); long Measure(SerializationContext? context) { @@ -159,4 +181,15 @@ public int unrelated public int ignored { get; set; } public int unrelatedReadCount => m_unrelatedReads; } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static (SerializationContext, WeakReference) ReplaceContextService(bool nested) + { + var replaced = new object(); + SerializationContext context = SerializationContext.empty.With(replaced); + if (nested) + context = context.With("marker").With>(new[] { 1, 2 }); + return (context.With(new object()), new WeakReference(replaced)); + } + } diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs index 187352102..b3c267f72 100644 --- a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Encoding.cs @@ -51,8 +51,12 @@ public void LargeBinaryEncodingAllocatesOnlyTheOwnedCaptureAndResult() for (int index = 0; index < 4; index++) _ = m_serialization.Encode(writer => writer.Write("value", source)); long before = GC.GetAllocatedBytesForCurrentThread(); - byte[] data = m_serialization.Encode(writer => writer.Write("value", source)); - long allocated = GC.GetAllocatedBytesForCurrentThread() - before; + byte[] data = []; + const int repetitions = 16; + for (int index = 0; index < repetitions; index++) + data = m_serialization.Encode(writer => writer.Write("value", source)); + long allocated = (GC.GetAllocatedBytesForCurrentThread() - before) / repetitions; + // Retain the per-call bound while amortizing one-time runtime/catalog admission during the suite. Assert.InRange(allocated, source.Length * 2, source.Length * 2 + 8192); source[0] = 0; byte[] restored = m_serialization.Decode(data, reader => reader.Read("value")); diff --git a/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Snapshots.cs b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Snapshots.cs new file mode 100644 index 000000000..5aaf3fe51 --- /dev/null +++ b/tests/core/Inno.Core.Serialization.Tests/SerializationBehaviorTests.Snapshots.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Inno.Core.Serialization.Converters; +using Xunit; + +namespace Inno.Core.Serialization.Tests; + +public sealed partial class SerializationBehaviorTests +{ + [Fact] + public void BinaryValuesFreezeAtTheirOwningCaptureBoundary() + { + byte[] ordinary = m_serialization.Encode(writer => + { + byte[] bytes = [1]; + writer.Write("bytes", bytes); + bytes[0] = 2; + }); + Assert.Equal(new byte[] { 1 }, m_serialization.Decode(ordinary, reader => reader.Read("bytes"))); + + var value = new MutatingBinaryCapture(); + SerializationSnapshot captured = m_serialization.CaptureSnapshot(value); + Assert.Equal(new byte[] { 1 }, m_serialization.Deserialize(captured.ToArray()).bytes); + value.bytes[0] = 1; + Assert.Same(captured, m_serialization.CaptureSnapshot(value, previous: captured)); + value.bytes[0] = 9; + Assert.Equal(new byte[] { 1 }, m_serialization.Deserialize(captured.ToArray()).bytes); + + var properties = new MutatingPropertyCapture(); + byte[] expected = m_serialization.EncodePropertySnapshots(m_serialization.CaptureProperties(properties)); + properties.Reset(); + Assert.Equal(expected, m_serialization.CapturePropertiesData(properties)); + } + + [Fact] + public void SnapshotStreamingUsesTheSameBytesAndLeavesTheDestinationOwnedByItsCaller() + { + var value = new SnapshotEnvelope(); + value.state.bytes = new byte[128 * 1024]; + SerializationSnapshot snapshot = m_serialization.CaptureSnapshot(value); + using var stream = new MemoryStream(); + snapshot.WriteTo(stream); + Assert.Equal(m_serialization.Serialize(value), stream.ToArray()); + Assert.True(stream.CanWrite); + Assert.Throws(() => snapshot.WriteTo(null!)); + using var readOnly = new MemoryStream([1, 2, 3], writable: false); + Assert.Throws(() => snapshot.WriteTo(readOnly)); + } + + [Fact] + public void ConverterTypeQueriesUseTheirPinnedGenerationAndRejectExpiredWriters() + { + var value = new SnapshotEnvelope(); + using SerializationGeneration captured = m_serialization.CaptureGeneration(); + _ = captured.Serialize(value); + long original = value.observedTypeGeneration; + m_types.Rebuild(); + _ = m_serialization.Serialize(value); + Assert.True(value.observedTypeGeneration > original); + _ = captured.Serialize(value); + Assert.Equal(original, value.observedTypeGeneration); + SerializationWriter? expired = null; + _ = m_serialization.Encode(writer => expired = writer); + Assert.Throws(() => expired!.typeSnapshot); + } + + [Fact] + public void OrdinaryWriterRetainsItsOperationLifetimeAndRejectsDuplicateObjectsBeforeCallbacks() + { + bool called = false; + Assert.Throws(() => m_serialization.Encode(writer => + { + writer.Write("value", 1); + writer.WriteObject("value", _ => called = true); + })); + Assert.False(called); + byte[] bytes = m_serialization.Encode(writer => + { + SerializationWriter? child = null; + writer.WriteObject("nested", value => child = value); + child!.Write("late", 42); + }); + Assert.Equal(42, m_serialization.Decode(bytes, reader => reader.ReadObject("nested").Read("late"))); + } + + [Fact] + public void SnapshotCapturePreservesOrdinaryBytesAndEveryDirectValueChange() + { + var value = new DefaultSample { count = 7, ratio = -0f, bytes = [1, 2], values = [3, 4] }; + SerializationSnapshot snapshot = m_serialization.CaptureSnapshot(value); + byte[] original = snapshot.ToArray(); + Assert.Equal(m_serialization.Serialize(value), original); + Assert.Same(snapshot, m_serialization.CaptureSnapshot(value, previous: snapshot)); + for (int index = 0; index < 12; index++) + { + value.count++; + value.ratio = BitConverter.Int32BitsToSingle(index % 2 == 0 ? unchecked((int)0x80000000) : 0x7fc00001 + index); + value.bytes[index % 2]++; + value.values.Add(index); + SerializationSnapshot next = m_serialization.CaptureSnapshot(value, previous: snapshot); + Assert.NotSame(snapshot, next); + Assert.Equal(m_serialization.Serialize(value), next.ToArray()); + Assert.Same(next, m_serialization.CaptureSnapshot(value, previous: next)); + snapshot = next; + } + Assert.NotEqual(original, snapshot.ToArray()); + } + + [Theory] + [InlineData(3)] + [InlineData(70)] + [InlineData(8192)] + public void SnapshotObjectCaptureHandlesReplacementRemovalAndEmptyObjects(int count) + { + var value = new SnapshotEnvelope { omitState = true }; + for (int index = 0; index < count; index++) + value.fields.Add("item-" + index, index); + SerializationSnapshot snapshot = m_serialization.CaptureSnapshot(value); + value.fields.Remove("item-0"); + value.fields.Add("replacement", 91); + SerializationSnapshot replaced = m_serialization.CaptureSnapshot(value, previous: snapshot); + Assert.Equal(m_serialization.Serialize(value), replaced.ToArray()); + Assert.NotSame(snapshot, replaced); + Assert.Same(replaced, m_serialization.CaptureSnapshot(value, previous: replaced)); + value.fields.Clear(); + SerializationSnapshot empty = m_serialization.CaptureSnapshot(value, previous: replaced); + Assert.Equal(m_serialization.Serialize(value), empty.ToArray()); + Assert.Same(empty, m_serialization.CaptureSnapshot(value, previous: empty)); + } + + [Fact] + public void SnapshotStateBlobsUseTheExistingPropertyWireFormatAndDetectArrayShrink() + { + var value = new SnapshotEnvelope(); + value.items.AddRange([1, 2, 3]); + value.state.count = 42; + SerializationSnapshot before = m_serialization.CaptureSnapshot(value); + byte[] bytes = before.ToArray(); + Assert.Equal(m_serialization.CapturePropertiesData(value.state), + m_serialization.Decode(bytes, reader => reader.Read("state"))); + value.items.RemoveAt(1); + value.state.name = "changed"; + SerializationSnapshot after = m_serialization.CaptureSnapshot(value, previous: before); + Assert.Equal(m_serialization.Serialize(value), after.ToArray()); + Assert.Equal(bytes, before.ToArray()); + Assert.Same(after, m_serialization.CaptureSnapshot(value, previous: after)); + } + + [Fact] + public void SnapshotFailurePreservesTheOriginalAndStillExecutesConverters() + { + var value = new SnapshotEnvelope(); + SerializationSnapshot before = m_serialization.CaptureSnapshot(value); + byte[] bytes = before.ToArray(); + int calls = value.writeCalls; + Assert.Same(before, m_serialization.CaptureSnapshot(value, previous: before)); + Assert.Equal(calls + 1, value.writeCalls); + value.fail = true; + Assert.Throws(() => m_serialization.CaptureSnapshot(value, previous: before)); + Assert.Equal(bytes, before.ToArray()); + value.fail = false; + Assert.Same(before, m_serialization.CaptureSnapshot(value, previous: before)); + value.duplicate = true; + Assert.Throws(() => m_serialization.CaptureSnapshot(value, previous: before)); + Assert.Equal(bytes, before.ToArray()); + } + + [Fact] + public void SnapshotReadersShareOnlyImmutableDataAcrossIndependentCapturesAndGenerations() + { + var source = new DefaultSample { count = 7, bytes = [1, 2, 3] }; + SerializationSnapshot snapshot = m_serialization.CaptureSnapshot(source); + byte[] original = snapshot.ToArray(); + m_types.Rebuild(); + Parallel.For(0, 32, index => + { + var next = new DefaultSample { count = index, bytes = [1, 2, 3] }; + SerializationSnapshot captured = m_serialization.CaptureSnapshot(next, previous: snapshot); + Assert.Equal(m_serialization.Serialize(next), captured.ToArray()); + byte[] mutable = captured.ToArray(); + mutable[0]++; + Assert.Equal(original, snapshot.ToArray()); + }); + Assert.Same(snapshot, m_serialization.CaptureSnapshot(source, previous: snapshot)); + m_serialization.Dispose(); + Assert.Equal(original, snapshot.ToArray()); + Assert.Throws(() => m_serialization.CaptureSnapshot(source, previous: snapshot)); + } + + [Fact] + public void SnapshotDoesNotOwnTheSourceInstance() + { + (SerializationSnapshot snapshot, WeakReference reference) = CaptureWeakSource(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Assert.False(reference.IsAlive); + Assert.NotEmpty(snapshot.ToArray()); + } + + [Fact] + public void UnchangedLargeBinaryCaptureDoesNotCopyItsPayload() + { + var source = new SnapshotEnvelope(); + source.state.bytes = new byte[1024 * 1024]; + SerializationSnapshot snapshot = m_serialization.CaptureSnapshot(source); + for (int index = 0; index < 8; index++) + Assert.Same(snapshot, m_serialization.CaptureSnapshot(source, previous: snapshot)); + long started = GC.GetAllocatedBytesForCurrentThread(); + for (int index = 0; index < 16; index++) + Assert.Same(snapshot, m_serialization.CaptureSnapshot(source, previous: snapshot)); + long allocated = (GC.GetAllocatedBytesForCurrentThread() - started) / 16; + Assert.True(allocated < 16_384, $"An unchanged 1 MiB payload allocated {allocated} bytes per capture."); + source.state.bytes[500] = 12; + SerializationSnapshot changed = m_serialization.CaptureSnapshot(source, previous: snapshot); + Assert.NotSame(snapshot, changed); + Assert.Equal(m_serialization.Serialize(source), changed.ToArray()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private (SerializationSnapshot, WeakReference) CaptureWeakSource() + { + var value = new DefaultSample { name = "Weak owner" }; + return (m_serialization.CaptureSnapshot(value), new WeakReference(value)); + } +} + +internal sealed class MutatingBinaryCapture : ISerializable +{ + internal byte[] bytes = [1]; +} + +internal sealed class MutatingBinaryCaptureConverter : SerializationConverter +{ + public override void Write( + SerializationWriter writer, + MutatingBinaryCapture value + ) { + writer.Write("bytes", value.bytes); + value.bytes[0] = 2; + } + + public override MutatingBinaryCapture Read(SerializationReader reader) => new() { bytes = reader.Read("bytes") }; +} + +internal sealed class MutatingPropertyCapture : ISerializable +{ + private readonly byte[] m_bytes = [1]; + + [SerializableProperty(PropertyVisibility.Serialize)] public byte[] bytes => m_bytes; + [SerializableProperty(PropertyVisibility.Serialize)] public int mutate => m_bytes[0] = 2; + + internal void Reset() => m_bytes[0] = 1; +} + +internal sealed class SnapshotEnvelope : ISerializable +{ + internal Dictionary fields { get; } = []; + internal List items { get; } = []; + internal DefaultSample state { get; } = new(); + internal bool omitState; + internal bool fail; + internal bool duplicate; + internal int writeCalls; + internal long observedTypeGeneration; +} + +internal sealed class SnapshotEnvelopeConverter : SerializationConverter +{ + public override void Write( + SerializationWriter writer, + SnapshotEnvelope value + ) { + value.writeCalls++; + value.observedTypeGeneration = writer.typeSnapshot.version; + foreach (var pair in value.fields) + writer.Write(pair.Key, pair.Value); + if (value.duplicate) + { + writer.Write("duplicate", 1); + writer.Write("duplicate", 2); + } + if (!value.omitState) + { + writer.WritePropertiesData("state", value.state); + writer.WriteObjectArray("items", value.items, static ( + child, + number + ) => child.Write("value", number)); + } + if (value.fail) + throw new InvalidOperationException("Capture rejected by its source converter."); + } + + public override SnapshotEnvelope Read(SerializationReader reader) => throw new NotSupportedException(); +} diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs index b9a2876ae..1a3e2b401 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/LargePanelPerformanceTests.cs @@ -3,8 +3,13 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection; using System.Text.Json; +using System.Threading; +using Inno.Assets; +using Inno.Core.Serialization; using Inno.Editor.Interactions; +using Inno.Scene; using Xunit; using Xunit.Abstractions; @@ -12,6 +17,64 @@ namespace Inno.Editor.PlayMode.Tests; public sealed class LargePanelPerformanceTests(ITestOutputHelper output) { + [Fact] + [Trait("Acceptance", "Performance")] + public void DueDirtyInspectionsMeasureTheCompleteSavedSceneTraversal() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + Inno.Scene.GameScene scene = LoadSavedHierarchy(host); + Measure(host, null, "10000-hierarchy-dirty-inspections", + () => Assert.False(host.workspace.IsDirty(scene)), static () => Thread.Sleep(110)); + } + + [Fact] + [Trait("Acceptance", "Performance")] + public void NeutralCaptureSeparatesSerializationFromSourceFreshness() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + Inno.Scene.GameScene scene = LoadSavedHierarchy(host); + SerializationContext context = AssetSerializationContext.Create(host.assets, new AssetDependencyCollection(includeLastKnownPaths: false)); + SerializationSnapshot? previous = null; + Measure(host, null, "10000-hierarchy-neutral-capture", + () => previous = host.serialization.CaptureSnapshot(scene, context, previous), + consumer: "complete-authoritative-serialization-capture"); + Assert.NotNull(previous); + } + + [Fact] + [Trait("Acceptance", "Performance")] + public void SourceFreshnessMeasuresTheActualSavedSceneLoadBoundary() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + Inno.Scene.GameScene scene = LoadSavedHierarchy(host); + Assert.True(host.workspace.TryGetSourcePath(scene, out string path)); + SceneAsset asset = host.assets.Load(AssetPath.Project(path)); + Measure(host, null, "10000-hierarchy-source-freshness", + () => Assert.Same(asset, host.assets.Load(asset.identity.persistentId)), + consumer: "canonical-source-load-with-freshness-verification"); + } + + [Fact] + [Trait("Acceptance", "Performance")] + public void SceneMetadataReadsMeasureTheOwnedAttachmentBoundary() + { + using var host = new NativeProjectPanelHost(supportsVertexOffsets: true); + GameScene scene = LoadSavedHierarchy(host); + IReadOnlyList objects = scene.GetObjects(); + long checksum = 0; + Measure(host, null, "10000-hierarchy-metadata-reads", () => + { + foreach (GameObject value in objects) + { + checksum += value.name.Length + value.tag.Length + value.layer.index; + checksum += value.activeSelf ? 1 : 0; + checksum += value.transform.parent is null ? 1 : 0; + checksum += value.transform.siblingIndex; + } + }, consumer: "public-metadata-getters-with-current-attachment-validation"); + Assert.True(checksum > 0); + } + [Fact] [Trait("Acceptance", "Performance")] public void TenThousandHierarchyRowsProduceRepeatableFrameMeasurements() @@ -58,6 +121,27 @@ public void TenThousandAssetRowsProduceRepeatableFrameMeasurements() Measure(host, panel, "10000-asset-directory-rows"); } + private static Inno.Scene.GameScene LoadSavedHierarchy(NativeProjectPanelHost host) + { + string? input = InputDirectory("hierarchy"); + Inno.Scene.GameScene scene; + if (input is not null && Directory.Exists(input)) + { + CopyDirectory(input, Path.Combine(host.projectRoot, "Assets")); + host.assets.Rescan(); + scene = host.workspace.Open("Untitled Scene.iscene"); + } + else + { + scene = host.CreateScene(); + for (int index = 0; index < 10_000; index++) + _ = scene.CreateObject("Object " + index.ToString("D5")); + _ = host.workspace.Save(scene, string.Empty); + } + Assert.Equal(10_000, scene.GetObjects().Count); + return scene; + } + private static string? InputDirectory(string workload) { string? owner = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_INPUT_ROOT"); @@ -77,11 +161,18 @@ string destination private void Measure( NativeProjectPanelHost host, - EditorPanelExtension panel, - string workload + EditorPanelExtension? panel, + string workload, + Action? operation = null, + Action? prepare = null, + string? consumer = null ) { + operation ??= () => host.native.DrawPanel(panel!); for (int warmup = 0; warmup < 5; warmup++) - host.native.DrawPanel(panel); + { + prepare?.Invoke(); + operation(); + } string? requested = Environment.GetEnvironmentVariable("INNO_UI_PERFORMANCE_REPETITIONS"); int repetitions = requested is null ? 5 : int.Parse(requested, System.Globalization.CultureInfo.InvariantCulture); Assert.InRange(repetitions, 1, 5); @@ -98,8 +189,9 @@ string workload int gen2Before = GC.CollectionCount(2); for (int frame = 0; frame < milliseconds.Length; frame++) { + prepare?.Invoke(); long started = Stopwatch.GetTimestamp(); - host.native.DrawPanel(panel); + operation(); milliseconds[frame] = Stopwatch.GetElapsedTime(started).TotalMilliseconds; } long allocated = GC.GetAllocatedBytesForCurrentThread() - bytesBefore; @@ -117,12 +209,18 @@ string workload } output.WriteLine(JsonSerializer.Serialize(new { - workload, configuration = "Debug", samples, + workload, + configuration = typeof(LargePanelPerformanceTests).Assembly.GetCustomAttribute()?.Configuration + ?? throw new InvalidOperationException("The benchmark assembly has no SDK configuration metadata."), + samples, framework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, viewport = "1000x800", warmupFrames = 5, measuredFramesPerRepetition = 10, sharedInput = InputDirectory(workload.Contains("hierarchy", StringComparison.Ordinal) ? "hierarchy" : "assets"), - consumer = "native-command-offset-validation", supportsVertexOffsets = true + consumer = consumer ?? (prepare is null ? "native-command-offset-validation" : "complete-authoritative-dirty-inspection"), + preparationExcludedFromFrameTiming = prepare is not null, + supportsVertexOffsets = true })); - Assert.True(panel.isOpen); + if (panel is not null) + Assert.True(panel.isOpen); } } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs index de520992d..69722d10e 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/ListPanelKeyboardNativeTests.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Numerics; using Inno.Assets; using Inno.Assets.Pipeline; using Inno.Editor.Assets.ImGui; @@ -35,10 +37,17 @@ public void BrowserResolvesCurrentSourceEntriesAfterAnUnchangedRescan(bool tree) Assert.Same(current, host.runtime.interactions.selection.selectedTarget); } - [Fact] - public void HierarchyUsesVisibleRangeAndKeepsTextEditingExclusive() - { + [Theory] + [MemberData(nameof(VisualScaleMatrixTests.Metrics), MemberType = typeof(VisualScaleMatrixTests))] + [Trait("Acceptance", "Visual")] + public void HierarchyUsesVisibleRangeAndKeepsTextEditingExclusive( + float zoom, + float framebufferScale, + float width, + float height + ) { using var host = new NativeProjectPanelHost(); + host.native.SetDisplayMetrics(new(width, height), framebufferScale, zoom); GameScene scene = host.CreateScene(); GameObject[] objects = Enumerable.Range(0, 12).Select(index => scene.CreateObject("Object " + index.ToString("D2"))).ToArray(); GameObject child = scene.CreateObject("Collapsed child"); @@ -76,11 +85,17 @@ public void HierarchyUsesVisibleRangeAndKeepsTextEditingExclusive() } [Theory] - [InlineData(false)] - [InlineData(true)] - public void BrowserUsesTheFocusedVisibleOrderAndActivatesFoldersAfterTraversal(bool tree) - { + [MemberData(nameof(BrowserMetrics))] + [Trait("Acceptance", "Visual")] + public void BrowserUsesTheFocusedVisibleOrderAndActivatesFoldersAfterTraversal( + bool tree, + float zoom, + float framebufferScale, + float width, + float height + ) { using var host = new NativeProjectPanelHost(); + host.native.SetDisplayMetrics(new(width, height), framebufferScale, zoom); AssetFileEntry[] entries = Enumerable.Range(0, 12).Select(index => { AssetPath path = AssetPath.Project("Folder " + index.ToString("D2")); @@ -118,4 +133,11 @@ public void BrowserUsesTheFocusedVisibleOrderAndActivatesFoldersAfterTraversal(b Assert.Equal(entries[^1].assetPath.ToString(), browser!.browser.currentDirectory); Assert.False(interactions.history.canUndo); } + + public static IEnumerable BrowserMetrics() + { + foreach (bool tree in new[] { false, true }) + foreach (object[] metric in VisualScaleMatrixTests.Metrics()) + yield return [tree, .. metric]; + } } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs index 3a8a60346..a578c2819 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativePanelHost.cs @@ -33,6 +33,7 @@ internal sealed unsafe class NativePanelHost : IDisposable private readonly bool m_ownsServices; private readonly bool m_validateVertexOffsets; private float m_time; + private Vector2 m_panelSize = new(1000f, 800f); internal NativePanelHost(params string[] assemblies) : this(false, assemblies) { } @@ -121,6 +122,18 @@ internal void Frame( } } + internal void SetDisplayMetrics( + Vector2 logicalSize, + float framebufferScale, + float zoom + ) { + ImGuiIO* io = NativeImGui.Raw.GetIO(); + io->DisplaySize = Unsafe.BitCast(logicalSize); + io->DisplayFramebufferScale = Unsafe.BitCast(new(framebufferScale)); + m_panelSize = Vector2.Min(new(1000f, 800f), logicalSize - new Vector2(40f)); + ui.widgets.style.SetZoom(zoom); + } + internal void DrawPanel( EditorPanelExtension panel, Action? afterDraw = null, @@ -129,7 +142,7 @@ internal void DrawPanel( Frame(() => { NativeImGui.SetNextWindowPos(new Vector2(20f, 20f), (int)ImGuiCond.Always); - NativeImGui.SetNextWindowSize(new Vector2(1000f, 800f), (int)ImGuiCond.Always); + NativeImGui.SetNextWindowSize(m_panelSize, (int)ImGuiCond.Always); bool open = true; _ = NativeImGui.Begin("Panel acceptance", ref open, (int)ImGuiWindowFlags.NoSavedSettings); try diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs index 7432e9df3..29db505f5 100644 --- a/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs +++ b/tests/editor/Inno.Editor.PlayMode.Tests/NativeProjectPanelHost.cs @@ -10,6 +10,7 @@ using Inno.Core.IO; using Inno.Core.Logging; using Inno.Core.Settings; +using Inno.Core.Serialization; using Inno.Editor.Core; using Inno.Editor.Diagnostics; using Inno.Editor.ImGui; @@ -95,6 +96,7 @@ internal NativeProjectPanelHost( internal EditorReloadCoordinator reloads { get; } internal ProjectSettingsStore projectSettings => m_settings; internal AssetPipeline assets { get; } + internal SerializationRegistry serialization => m_engine.serialization; internal string projectRoot => m_root; internal EditorInteractionRuntime runtime { get; } internal IEditorSceneWorkspace workspace { get; } diff --git a/tests/editor/Inno.Editor.PlayMode.Tests/SceneDirtyCaptureTests.cs b/tests/editor/Inno.Editor.PlayMode.Tests/SceneDirtyCaptureTests.cs new file mode 100644 index 000000000..59994fef5 --- /dev/null +++ b/tests/editor/Inno.Editor.PlayMode.Tests/SceneDirtyCaptureTests.cs @@ -0,0 +1,167 @@ +using System; +using System.Security.Cryptography; +using System.Threading; +using Inno.Assets; +using Inno.Core.Serialization; +using Inno.Core.Serialization.Converters; +using Inno.Extensibility.Types; +using Inno.Scene; +using Xunit; + +namespace Inno.Editor.PlayMode.Tests; + +public sealed class SceneDirtyCaptureTests +{ + [Fact] + public void CompleteSceneCapturesReuseOnlyUnchangedNeutralState() + { + using var host = new NativeProjectPanelHost(); + GameScene scene = host.CreateScene(); + GameObject owner = scene.CreateObject("Owner"); + GameObject target = scene.CreateObject("Target"); + DirtyCaptureComponent component = owner.AddComponent(); + component.target = target; + SerializationContext context = AssetSerializationContext.Create(host.assets, new AssetDependencyCollection(includeLastKnownPaths: false)); + SerializationSnapshot before = host.serialization.CaptureSnapshot(scene, context); + Assert.Equal(host.serialization.Serialize(scene, context), before.ToArray()); + Assert.Same(before, host.serialization.CaptureSnapshot(scene, context, before)); + component.number = 42; + SerializationSnapshot changed = host.serialization.CaptureSnapshot(scene, context, before); + Assert.NotSame(before, changed); + Assert.Equal(host.serialization.Serialize(scene, context), changed.ToArray()); + Assert.Same(changed, host.serialization.CaptureSnapshot(scene, context, changed)); + component.number = 0; + SerializationSnapshot restored = host.serialization.CaptureSnapshot(scene, context, changed); + Assert.Equal(before.ToArray(), restored.ToArray()); + Assert.Same(restored, host.serialization.CaptureSnapshot(scene, context, restored)); + } + + [Fact] + public void DirtyInspectionIncludesReorderedComponentSnapshots() + { + using var host = new NativeProjectPanelHost(); + GameScene scene = host.CreateScene(); + GameObject owner = scene.CreateObject("Owner"); + DirtyCaptureComponent first = owner.AddComponent(); + _ = owner.AddComponent(); + host.workspace.Save(scene, string.Empty); + byte[] saved = Hash(host, scene); + Thread.Sleep(120); + Assert.False(host.workspace.IsDirty(scene)); + int original = owner.GetComponentIndex(first); + owner.SetComponentIndex(first, owner.GetComponents().Count - 1); + Assert.False(saved.AsSpan().SequenceEqual(Hash(host, scene))); + Thread.Sleep(120); + Assert.True(host.workspace.IsDirty(scene)); + owner.SetComponentIndex(first, original); + Assert.Equal(saved, Hash(host, scene)); + Thread.Sleep(120); + Assert.False(host.workspace.IsDirty(scene)); + } + + [Fact] + public void ExactDirtyInspectionFindsDirectFieldsConverterStateReferencesAndTopology() + { + using var host = new NativeProjectPanelHost(); + GameScene scene = host.CreateScene(); + GameObject owner = scene.CreateObject("Owner"); + DirtyCaptureComponent component = owner.AddComponent(); + GameObject target = scene.CreateObject("Target"); + host.workspace.Save(scene, string.Empty); + byte[] saved = Hash(host, scene); + Check(false); + component.number = 7; + Check(true); + component.number = 0; + Check(false); + component.state.value = 23; + Check(true); + component.state.value = 0; + Check(false); + component.target = target; + Check(true); + component.target = null; + Check(false); + target.SetActive(false); + Check(true); + target.SetActive(true); + Check(false); + GameObject added = scene.CreateObject("Added"); + Check(true); + scene.DestroyObject(added); + Check(false); + Assert.True(component.state.reads >= 10); + + void Check(bool expected) + { + Thread.Sleep(120); + Assert.Equal(expected, !Hash(host, scene).AsSpan().SequenceEqual(saved)); + Assert.Equal(expected, host.workspace.IsDirty(scene)); + } + } + + [Fact] + public void FailedCapturePreservesTheSavedBaselineAndRecoversWithoutPublishingCleanState() + { + using var host = new NativeProjectPanelHost(); + GameScene scene = host.CreateScene(); + DirtyCaptureComponent component = scene.CreateObject("Owner").AddComponent(); + host.workspace.Save(scene, string.Empty); + Thread.Sleep(120); + Assert.False(host.workspace.IsDirty(scene)); + component.state.reject = true; + Thread.Sleep(120); + Assert.True(host.workspace.IsDirty(scene)); + component.state.reject = false; + component.number = 9; + Thread.Sleep(120); + Assert.True(host.workspace.IsDirty(scene)); + component.number = 0; + Thread.Sleep(120); + Assert.False(host.workspace.IsDirty(scene)); + component.number = 17; + host.workspace.Save(scene, string.Empty); + Thread.Sleep(120); + Assert.False(host.workspace.IsDirty(scene)); + component.number = 0; + Thread.Sleep(120); + Assert.True(host.workspace.IsDirty(scene)); + } + + private static byte[] Hash( + NativeProjectPanelHost host, + GameScene scene + ) => SHA256.HashData(host.serialization.Serialize(scene, + AssetSerializationContext.Create(host.assets, new AssetDependencyCollection(includeLastKnownPaths: false)))); +} + +[StableTypeId("942e794b-1c53-45a6-80e6-a641d76fdd09")] +[AllowMultipleComponent] +internal sealed class DirtyCaptureComponent : GameComponent +{ + [SerializableProperty] public int number; + [SerializableProperty] public GameObject? target; + [SerializableProperty] public DirtyCaptureValue state { get; set; } = new(); +} + +internal sealed class DirtyCaptureValue +{ + internal int value; + internal bool reject; + internal int reads; +} + +internal sealed class DirtyCaptureValueConverter : SerializationConverter +{ + public override void Write( + SerializationWriter writer, + DirtyCaptureValue value + ) { + value.reads++; + writer.Write("value", value.value); + if (value.reject) + throw new InvalidOperationException("The test-owned converter rejected capture."); + } + + public override DirtyCaptureValue Read(SerializationReader reader) => new() { value = reader.Read("value") }; +} diff --git a/tests/fixtures/editor-ui/WorkflowJourney.editor.cs.txt b/tests/fixtures/editor-ui/WorkflowJourney.editor.cs.txt new file mode 100644 index 000000000..b8123ee26 --- /dev/null +++ b/tests/fixtures/editor-ui/WorkflowJourney.editor.cs.txt @@ -0,0 +1,289 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using InnoEngine.Mathematics; +using InnoEngine.Scene; +using InnoEditor.Core; +using InnoEditor.Interactions; +using InnoEditor.PlayMode; +using InnoEditor.Scene; + +/// Drives ordinary feature protocols while the actual product draws every intermediate state. +[EditorModule("tests.editor-ui.workflow-journey", order: 10000)] +public sealed class WorkflowJourney( + IEditorSceneWorkspace workspace, + SceneEdits edits, + EditorInteractions interactions, + IEditorPlayMode play +) : EditorModule +{ + private readonly List m_verified = []; + private Guid m_sceneId; + private Guid m_firstId; + private Guid m_secondId; + private Guid m_componentId; + private Guid m_duplicateId; + private string m_scenePath = string.Empty; + private int m_step; + private int m_playFrames; + private bool m_finished; + + /// + protected override void OnUpdate(EditorContext context) + { + try + { + Advance(context); + } + catch (Exception failure) + { + string directory = Path.Combine(context.projectDirectory, "Library", "Acceptance"); + Directory.CreateDirectory(directory); + File.WriteAllText(Path.Combine(directory, "workflow-failure.txt"), failure.ToString()); + throw; + } + } + + private void Advance(EditorContext context) + { + if (m_finished) + return; + if (m_step > 0) + { + Require(interactions.TryGetPanelState("scene.inspector", out bool inspector) && inspector, + "The actual Inspector was quarantined or closed during the journey."); + Require(interactions.TryGetPanelState("scene.hierarchy", out bool hierarchy) && hierarchy, + "The actual Hierarchy was quarantined or closed during the journey."); + } + if (play.lastFailure is not null) + throw new InvalidOperationException(play.lastFailure); + if (m_step == 0) + { + foreach (string id in new[] { "scene.inspector", "scene.hierarchy", "asset.file-browser" }) + Require(interactions.OpenPanel(id), "The product could not open " + id); + _ = workspace.Open("FlappyBird/FlappyBird.iscene"); + GameScene created = edits.CreateScene(); + edits.RenameScene(created, "UI Workflow Acceptance"); + GameObject first = edits.CreateGameObject(created); + GameObject second = edits.CreateGameObject(created); + edits.RenameGameObject(first, "Workflow First"); + edits.RenameGameObject(second, "Workflow Second"); + first.transform.localPosition = new Vector3(0, 2, 3); + second.transform.localPosition = new Vector3(0, 5, 6); + m_sceneId = created.identity.persistentId; + m_firstId = first.identity.persistentId; + m_secondId = second.identity.persistentId; + m_componentId = edits.AddComponent(first, typeof(WorkflowState)).identity.persistentId; + m_duplicateId = edits.AddComponent(first, typeof(WorkflowState)).identity.persistentId; + interactions.SetSelection([first, second], first); + m_step++; + return; + } + if (m_step == 20) + { + Require(interactions.selection.selectedTarget is null, "Closing a document retained a live target."); + GameScene reopened = workspace.Open(m_scenePath); + GameObject first = reopened.GetObjects().Single(value => value.name == "Workflow First"); + GameObject second = reopened.GetObjects().Single(value => value.name == "Workflow Second"); + WorkflowState state = first.GetComponents().OfType().Single(value => value.number == 7); + Require(first.GetComponents().OfType().Count() == 2 && state.reference == second, + "Save and reopen lost component order, values or references."); + m_sceneId = reopened.identity.persistentId; + m_firstId = first.identity.persistentId; + m_secondId = second.identity.persistentId; + m_componentId = state.identity.persistentId; + interactions.SetSelection(first); + m_verified.Add("actual File Browser and Inspector: save, close, reopen and reference restoration"); + m_step++; + return; + } + if (m_step >= 21) + { + AdvancePlayJourney(context); + return; + } + GameScene scene = workspace.scenes.Single(value => value.identity.persistentId == m_sceneId); + GameObject? firstObject = scene.GetObjects().SingleOrDefault(value => value.identity.persistentId == m_firstId); + GameObject? secondObject = scene.GetObjects().SingleOrDefault(value => value.identity.persistentId == m_secondId); + WorkflowState? component = FindComponent(scene, m_componentId) as WorkflowState; + switch (m_step) + { + case 1: + Require(interactions.selection.count == 2, "Multi-selection was not preserved through actual drawing."); + m_verified.Add("actual Inspector and Hierarchy: mixed multi-selection, duplicate components"); + interactions.SetSelection(firstObject); + break; + case 2: + using (var gesture = new EditorEditGesture(interactions.history, "First drag")) + { + for (int sample = 1; sample <= 3; sample++) + { + int value = sample; + edits.ChangeProperty(component!, nameof(WorkflowState.number), () => component!.number = value, + "First drag", gesture.mergeKey); + } + gesture.Commit(); + } + break; + case 3: + Require(interactions.history.Undo().succeeded && component!.number == 0, "One drag did not undo as one entry."); + break; + case 4: + Require(interactions.history.Redo().succeeded && component!.number == 3, "Drag Redo failed."); + using (var gesture = new EditorEditGesture(interactions.history, "Second drag")) + { + edits.ChangeProperty(component!, nameof(WorkflowState.number), () => component!.number = 7, + "Second drag", gesture.mergeKey); + gesture.Commit(); + } + break; + case 5: + Require(interactions.history.Undo().succeeded && component!.number == 3, "Separate drags were merged."); + break; + case 6: + Require(interactions.history.Undo().succeeded && component!.number == 0, "The first drag was lost."); + Require(interactions.history.Redo().succeeded && interactions.history.Redo().succeeded, + "The two independent drags could not be redone."); + break; + case 7: + using (var gesture = new EditorEditGesture(interactions.history, "Cancelled drag")) + { + edits.ChangeProperty(component!, nameof(WorkflowState.number), () => component!.number = 99, + "Cancelled drag", gesture.mergeKey); + Require(gesture.Cancel().succeeded && component!.number == 7, "Cancellation did not restore the start."); + } + m_verified.Add("actual Inspector: continuous edits, independent Undo/Redo and gesture cancellation"); + break; + case 8: + edits.SetGameObjectActive(firstObject!, false); + edits.SetGameObjectActive(firstObject!, true); + Require(interactions.history.Undo().succeeded && !firstObject!.activeSelf, "Boolean edits were merged."); + Require(interactions.history.Undo().succeeded && firstObject!.activeSelf, "Boolean Undo lost the initial value."); + break; + case 9: + edits.ChangeProperty(component!, nameof(WorkflowState.reference), () => component!.reference = secondObject, + "Assign reference"); + interactions.SetSelection(component); + break; + case 10: + Require(component!.reference == secondObject, "The reference was not assigned."); + Require(interactions.history.Undo().succeeded && component.reference is null, "Reference clearing through Undo failed."); + Require(interactions.history.Redo().succeeded && component.reference == secondObject, "Reference Redo failed."); + m_verified.Add("actual Inspector: discrete booleans, assigned and unassigned references"); + break; + case 11: + interactions.SetSelection([firstObject!, secondObject!], firstObject); + edits.ChangeProperties([firstObject!.transform, secondObject!.transform], "localPosition", + () => firstObject.transform.localPosition = new Vector3(11, 2, 3), "Move X", componentName: "x"); + break; + case 12: + Require(firstObject!.transform.localPosition.x == 11 && secondObject!.transform.localPosition.x == 11, + "The common X edit was not applied."); + Require(firstObject.transform.localPosition.y == 2 && secondObject!.transform.localPosition.y == 5, + "A single-axis edit overwrote a mixed axis."); + Require(interactions.history.Undo().succeeded && firstObject.transform.localPosition.x == 0 && + secondObject!.transform.localPosition.x == 0, "Multi-selection Undo failed."); + m_verified.Add("actual Inspector: mixed transforms preserve unedited axes"); + break; + case 13: + interactions.SetSelection(firstObject); + edits.SetComponentIndex(FindComponent(scene, m_duplicateId)!, 1); + break; + case 14: + Require(firstObject!.GetComponentIndex(FindComponent(scene, m_duplicateId)!) == 1, + "Duplicate component ordering selected the wrong instance."); + Require(interactions.history.Undo().succeeded && + firstObject.GetComponentIndex(FindComponent(scene, m_duplicateId)!) == 2, "Component order Undo failed."); + Require(edits.RemoveComponent(FindComponent(scene, m_duplicateId)!), "Component removal failed."); + break; + case 15: + Require(FindComponent(scene, m_duplicateId) is null, "The removed component remained attached."); + Require(interactions.history.Undo().succeeded && FindComponent(scene, m_duplicateId) is WorkflowState, + "Component restoration lost identity or type."); + m_verified.Add("actual Inspector: duplicate instance order, removal and identity-preserving restoration"); + break; + case 16: + edits.DeleteGameObjects([firstObject!, secondObject!]); + break; + case 17: + Require(firstObject is null && secondObject is null, "Batch delete was incomplete."); + Require(interactions.history.Undo().succeeded, "Batch delete Undo failed."); + break; + case 18: + Require(firstObject is not null && secondObject is not null && component!.reference == secondObject, + "Batch recovery lost an object or cross-object reference."); + bool rejected = false; + try + { + edits.ChangeProperty(component!, nameof(WorkflowState.number), () => + { + component!.number = 99; + throw new InvalidOperationException("Expected workflow rejection."); + }, "Rejected mutation"); + } + catch (InvalidOperationException exception) when (exception.Message == "Expected workflow rejection.") + { + rejected = true; + } + Require(rejected && component!.number == 7, "A rejected mutation was not rolled back."); + m_verified.Add("actual Hierarchy and Inspector: atomic batch delete/recovery and failed mutation rollback"); + m_scenePath = workspace.Save(scene, string.Empty); + break; + case 19: + Require(!workspace.IsDirty(scene), "The saved document remained dirty."); + Require(edits.CloseScene(scene), "The saved scene could not close."); + m_step++; + return; + } + m_step++; + } + + private void AdvancePlayJourney(EditorContext context) + { + switch (m_step) + { + case 21: + Require(play.EnterPlayMode(), "The product refused Play."); + break; + case 22: + if (!play.isPlaying) + return; + if (++m_playFrames < 60) + return; + Require(workspace.scenes.SelectMany(static scene => scene.GetObjects()) + .SelectMany(static value => value.GetComponents()).OfType().Any(static value => value.ticks > 0), + "Play did not advance the ordinary script behavior."); + Require(play.ExitPlayMode(), "The product refused Stop."); + break; + case 23: + if (play.isPlaying) + return; + Require(workspace.canPersist, "Stop did not restore Edit persistence."); + Require(workspace.scenes.Single(value => value.identity.persistentId == m_sceneId) + .GetObjects().SelectMany(static value => value.GetComponents()).OfType() + .All(static value => value.ticks == 0), "Play mutations leaked into the Edit document."); + m_verified.Add("actual Game view: Play, frame advancement, Stop and restored Edit"); + string directory = Path.Combine(context.projectDirectory, "Library", "Acceptance"); + Directory.CreateDirectory(directory); + File.WriteAllLines(Path.Combine(directory, "workflow-journey.tsv"), m_verified); + m_finished = true; + break; + } + m_step++; + } + + private static GameComponent? FindComponent( + GameScene scene, + Guid persistentId + ) => scene.GetObjects().SelectMany(static value => value.GetComponents()) + .SingleOrDefault(value => value.identity.persistentId == persistentId); + + private static void Require( + bool condition, + string message + ) { + if (!condition) + throw new InvalidOperationException(message); + } +} diff --git a/tests/fixtures/editor-ui/WorkflowState.cs.txt b/tests/fixtures/editor-ui/WorkflowState.cs.txt new file mode 100644 index 000000000..9a33876d6 --- /dev/null +++ b/tests/fixtures/editor-ui/WorkflowState.cs.txt @@ -0,0 +1,15 @@ +using InnoEngine.Reflection; +using InnoEngine.Scene; +using InnoEngine.Serialization; + +[StableTypeId("e669c627-771a-4d3c-a09d-307dcd6b457e")] +[AllowMultipleComponent] +public sealed class WorkflowState : GameBehavior +{ + [SerializableProperty] public float number; + [SerializableProperty] public GameObject? reference; + [SerializableProperty] public int ticks; + + /// + protected override void Update() => ticks++; +} diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs index a259e0902..ad47add50 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ScriptingPipelineTests.UiLifecycle.cs @@ -1,4 +1,7 @@ using System; +using System.Runtime.CompilerServices; +using Inno.Core.Serialization; +using Inno.Core.Serialization.Converters; using Inno.Editor.Core; using Inno.Editor.Interactions; using Inno.Scripting.Compiler; @@ -9,6 +12,45 @@ namespace Inno.Editor.Scripting.Tests; public sealed partial class ScriptingPipelineTests { + [Fact] + [Trait("Acceptance", "Lifecycle")] + [Trait("ProcessIsolation", "Required")] + public void NeutralSnapshotSurvivesRetirementWithoutKeepingTheScriptGenerationAlive() + { + m_fixture.Write("SnapshotPayload.cs", """ + using InnoEngine.Serialization; + public sealed class SnapshotPayload : ISerializable + { + [SerializableProperty] public int count = 42; + } + """); + using ScriptReloadHost reload = m_fixture.CreateReloadHost(); + reload.Start(); + ScriptCompilationResult first = m_fixture.CompilePending(reload); + Assert.True(first.success, FormatDiagnostics(first)); + Assert.True(reload.ApplyPendingReload()); + (SerializationSnapshot snapshot, WeakReference retired) = CaptureScriptSnapshot(m_fixture); + byte[] original = snapshot.ToArray(); + + m_fixture.Write("SnapshotPayload.cs", "public sealed class ReplacementPayload { }"); + ScriptCompilationResult replacement = m_fixture.CompilePending(reload); + Assert.True(replacement.success, FormatDiagnostics(replacement)); + Assert.True(reload.ApplyPendingReload()); + CompleteUnloadVerification(reload); + Assert.False(retired.IsAlive, "The neutral snapshot retained the old script type across retirement."); + Assert.Equal(original, snapshot.ToArray()); + Assert.NotEmpty(original); + GC.KeepAlive(snapshot); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static (SerializationSnapshot, WeakReference) CaptureScriptSnapshot(ScriptingFixture fixture) + { + Type type = fixture.ResolveActiveType("SnapshotPayload"); + var payload = (ISerializable)Activator.CreateInstance(type)!; + return (fixture.host.serialization.CaptureSnapshot(new CapturedScriptPayload(payload)), new WeakReference(type)); + } + [Fact] [Trait("Acceptance", "Lifecycle")] [Trait("ProcessIsolation", "Required")] @@ -77,3 +119,19 @@ int version Assert.True(item.status.isEnabled); } } + +internal sealed class CapturedScriptPayload(ISerializable payload) : ISerializable +{ + internal ISerializable payload { get; } = payload; +} + +internal sealed class CapturedScriptPayloadConverter : SerializationConverter +{ + public override void Write( + SerializationWriter writer, + CapturedScriptPayload value + ) => writer.WritePropertiesData("state", value.payload); + + public override CapturedScriptPayload Read(SerializationReader reader) + => throw new NotSupportedException("This fixture only captures an existing generation."); +} diff --git a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs index 44931447f..75fb232ad 100644 --- a/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs +++ b/tests/scripting/Inno.Editor.Scripting.Tests/ShaderEditorWorkflowTests.Performance.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Numerics; +using System.Reflection; using System.Security.Cryptography; using System.Text.Json; using Inno.Assets; @@ -103,7 +104,10 @@ public unsafe void TwoThousandShaderNodesProduceRepeatableFrameMeasurements() m_performanceOutput.WriteLine(JsonSerializer.Serialize(new { workload = "2000-shader-nodes", nodeCount = controller.document.nodes.Count, - edgeCount = controller.document.edges.Count, configuration = "Debug", samples, + edgeCount = controller.document.edges.Count, + configuration = typeof(ShaderEditorWorkflowTests).Assembly.GetCustomAttribute()?.Configuration + ?? throw new InvalidOperationException("The benchmark assembly has no SDK configuration metadata."), + samples, framework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, viewport = "1200x800", warmupFrames = 5, measuredFramesPerRepetition = 10, sharedInput = input, consumer = "native-command-offset-validation", supportsVertexOffsets = true From e3f02ebeacdb908da6e274063f44c640adac0750 Mon Sep 17 00:00:00 2001 From: Aaron Liao Date: Thu, 29 Oct 2026 08:38:13 +0800 Subject: [PATCH 30/54] fix(imgui): own supplemental glyph data through complete context retirement --- .../Sdl3PlatformApplicationImGuiExtensions.cs | 26 +- .../BuiltIn/Fonts/Supplemental/LICENSE | 92 +++++++ .../Supplemental/NotoSansCJKsc-Regular.otf | Bin 0 -> 16437364 bytes .../BuiltIn/Fonts/Supplemental/README.md | 12 + .../Context/PlatformImGuiContext.Fonts.cs | 53 ++++ .../Context/PlatformImGuiContext.cs | 26 +- .../Inno.Adapter.Presentation.ImGui.Sdl3.md | 8 + .../FontCoverageTests.cs | 228 ++++++++++++++++++ .../NativeWindowTestHost.cs | 9 + 9 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/LICENSE create mode 100644 backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/NotoSansCJKsc-Regular.otf create mode 100644 backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/README.md create mode 100644 backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Context/PlatformImGuiContext.Fonts.cs create mode 100644 tests/editor/Inno.Editor.ImGui.Tests/FontCoverageTests.cs diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs index 7efa5e7cc..397a835d5 100644 --- a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/Api/Sdl3PlatformApplicationImGuiExtensions.cs @@ -17,6 +17,7 @@ private sealed class ImGuiState : ISdl3ApplicationExtension { private readonly Sdl3PlatformApplication m_application; private IDisposable? m_registration; + private bool m_isRetiring; internal ImGuiState(Sdl3PlatformApplication application) { @@ -24,12 +25,22 @@ internal ImGuiState(Sdl3PlatformApplication application) } internal Dictionary contexts { get; } = []; + internal bool isRetiring => m_isRetiring; internal void Register() { m_registration = m_application.RegisterExtension(this); } + internal void Release( + uint windowId, + PlatformImGuiContext context + ) { + if (!contexts.TryGetValue(windowId, out PlatformImGuiContext? registered) || !ReferenceEquals(registered, context)) + throw new InvalidOperationException("The retired ImGui context no longer owns its application registration."); + contexts.Remove(windowId); + } + void ISdl3ApplicationExtension.ProcessNativeEvent( Sdl3PlatformApplication application, scoped ReadOnlySpan nativeEventData @@ -54,9 +65,11 @@ uint windowId void ISdl3ApplicationExtension.OnApplicationDisposing(Sdl3PlatformApplication application) { - foreach (PlatformImGuiContext context in contexts.Values) + m_isRetiring = true; + var retiring = new PlatformImGuiContext[contexts.Count]; + contexts.Values.CopyTo(retiring, 0); + foreach (PlatformImGuiContext context in retiring) context.Dispose(); - contexts.Clear(); s_states.Remove(application); m_registration?.Dispose(); m_registration = null; @@ -115,11 +128,15 @@ public static PlatformImGuiContext CreateImGuiContext( throw new ArgumentException("The window must belong to the supplied SDL application.", nameof(window)); ImGuiState state = s_states.GetValue(application, CreateState); + if (state.isRetiring) + throw new InvalidOperationException("The application presentation state is retiring and cannot create another context."); if (state.contexts.ContainsKey(window.windowId)) throw new InvalidOperationException("This window already owns an ImGui context."); - var context = new PlatformImGuiContext(application, window, contextFlags, interaction, renderer); - state.contexts[window.windowId] = context; + uint windowId = window.windowId; + var context = new PlatformImGuiContext(application, window, contextFlags, interaction, renderer, + retired => state.Release(windowId, retired)); + state.contexts[windowId] = context; return context; } @@ -150,7 +167,6 @@ Sdl3PlatformWindow window if (!state.contexts.TryGetValue(window.windowId, out PlatformImGuiContext? context)) return; context.Dispose(); - state.contexts.Remove(window.windowId); } private static ImGuiState CreateState(Sdl3PlatformApplication application) diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/LICENSE b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/LICENSE new file mode 100644 index 000000000..d952d62c0 --- /dev/null +++ b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/LICENSE @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/NotoSansCJKsc-Regular.otf b/backends/ImGui/runtime/Inno.Adapter.Presentation.ImGui.Sdl3/BuiltIn/Fonts/Supplemental/NotoSansCJKsc-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..dc15562470b4f842321894787a0d066879ccff8b GIT binary patch literal 16437364 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

NB2A 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

#{csDOLDM`f? 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

)}{C@d;W=N6rKS)f@X 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 z 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ð%Pn3hlunI<;(;MRdyG4GWS}|wGl~g6^E)91gr{K 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

DsqInoi*gg7ws(DO7JfBT7mO;9-{?{K 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%0AtOx=N0p6_`9+l%d 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 YsguepuN3YI2& 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%0DPxTcIn(e}Auf2WxQnIm>xi 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⧁C*G8VA63(2y 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 z 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


aV5h8(9kyoovg{n6qQyK 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±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

0try$3c{SUCmb@v@KytM4-?=D6q9uc$_;v3(X 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

mMI*7#tgL9E9S5 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

;xZ{<@}_i=v=wqiqCEfNz#Pf~ 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 z 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�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 z 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�}d%>s&wMp#f5N7r2MJ6U^sps)Wt0DDmC zjhvTozR8Q7F8&n?6R>y}QsMTm;$H}JcF;coP_NNL`!W-wpol{;a5PzbIR z{=JMxg?`ke!1}sn7iq)JmFOU>lAdAF*a&Y!iwSF8WxUi7U@K2M&r3coU4CNEUJsp1 z+kDmrCHo10XM#Qy{aW~S+Z~4y`|D1CiJ0acEz&8hsw{4R;_a6{JVsPYLA@O|F-$=) za$nADbmZsuKI7N;$e8Xyd~g-Qx4BPG2+U|HsyM2DEi#ZC@}*H%j6V%hwS$ z7~&GwxI3}qCKucb)xCGX^xlQ&y$Okq=)D-zOh+IgI=H9qCXP#z&F*G5$%uF4?9zLUZ(sN0rf*M2Lh>OYfDBi?) z=0w@OmWixM=t=F-lxj9JXlOeg;-Je0XlMt#;0qU1_$5v^5k``B?a~(i9F^=x(y8y=3-|D*kbulArf95e@@G+nesdFcxpT_43soTMU{?}f7AX#|ZoaJ{eg zLL9LPFm20slc|u4pkUK6)P7~Sz9V%YtgA=$tlXLSM2G&w2BpFlc5VHg&s2SbXp34k zl-2jg2o1?I5-;2#eZinHzfOAL>%L$kOel3kkZhj6@!nFb2S{4SFgJ zdMZnt@g7gSG$cJh=AIaUR!Y%PIeNRA)}F%pOzE|fQ_`ENQh}(-rPHOJ{#QIBzmk7E z#eN`H5K-gP?E#|=D}bRL-8qWyaLyQ@JHThjLDlfWlDrV1#f;LWsI{!WsK?l*!rdAo z;;Y8ms$7PzA4?HZ3&`#-qG!h$tDGso3T2u*}vvy5aj=Pwy8HEH3|p!KvE-M%>q2D z1B>tVhuRj@SOC?^wiz>WlTwmW6Vpu$m-En~=FUfXh^Fro((2h*z&)7dH#R?G9CSE) zb4q?8^5O33ARoRlI$G=*WCs{W_oxJr-Fc78f4`DOGc`t>SHNb%EO`b11RjsVN`qBP zz_)<4W-C0PbQ>c3R;55& zgW)YjAxa$bn2XI{Sum9WRen`IyR6q-UFMza<{Y)Rkf<^E`VwGaeOYj-MHm= zz2JJjeGa`r6T*0Gxv=xZDQQTqTWUy6ZPne9F1e@NUFKikCxlh@46)oNO`KbhU6xTKk>X0|kRJ)rn!*fJ8lOqT!Y@u&b`dx%bY%!{4b*B1&s*s;(Q1%l>k(M}71KJMxdV z5ptLswpT@F_&y1MXSqW}h@1MzxSyvAlx zmcIaaSoNL`uKynC4PZTFa1LV`}nLjLnCom(?Q^L-^j z1H+}?XCedoUl6|OyFrovL0YU^7H$5ahC@D;u2bwPOjE{0xRcvtBdVW7B35FJVq=j^~H!7-z zOApuCw4A5YvkOWyq$k?04msUxMQ;_N#hFM5Y=c65QROh~tW=SvOjXfWv50vAl7({u zFzvF$`TKx>hJH))Qzbo=wxyR@vHb_G6*SQNxY0H+4tHHt>C@s}?IaQuTq`)0W?u#= z+Q;JSqTuQJL$Gsvf`jfe1ysdULw}Og_@O_^(s4zX@Rq!MJh0=3$EqDKUqBi211Mu| zfBA%zF;{ITmvHYn^&c1QOh z(3nx^!XG2s(+x@3prVkolt6%MduG`KJSfm{vEC zY`hpaPodE~w2+%so?V?)y$^;C9DU9O+yqhJ=8W)C-$uU*#LvbAG~=`JbvBLr1`PMa$NX1vK^O@bw{&qFE0b0ZP#y0m16=x=+=kY8as6Qwv0z zRZ}vi*6z;Eob$ca0^gR)lQ(*x6tDf%?k>;P&yU5l*bJEgNvr~%dtp%O6cREVu<&}7P*O)nW_lG>5*ZVRtvCS+#FReN? zj}ANbVU*pxJvQm3XU$x)eY5=1Xf%c2J2(k!^o~I{Z`ohZ=#@N6M;~g|!W7bP!=~T= z4Vs}lDb@0C*)^lNni%{B<9h4jl`D=ke_@1CV24+$OKnzurd(PGUFu=&yhA`=b^Vz2 z$;0-UA%$*|p!%Zv@~T>?zr;`CbLF^&m9?UA)v_1i zKQ)}GJ(YW{wMXF6d+>A1ftWNwYei3UYXkl2S{55ye5GPGK@w|0z5I$x5Zs$sKo>Ia z`=;y4u)5DnRW%T;YEi!_4d-*MQ9rfo|BPcxTwZM;Z2Ar7E^0= zEnSw(hOo#%Jj}Al5y)`^K#mDHg)x+G!og|?GX*bsQws~#1+$s@V2Z~q#jF2*aOq;J zp8zj?3>y=}t?*NgHF}C|CQ#4-tQYU>BSYr1z_rKy^{y7K`vh z2E7Z#2oAFO2C7aQtW2>D)N;_fAF=QK72f;E%2XST@=2RP2cpX*G3ygB)k@qyhGHJ3PNpj|&UOoydEkA4cu7QRV5%n} z>RoIk7)TgN98Q6dZV|U5M_dVvrM1Sk{slLR&q!_=V}qkGc6)s9mIJ#TGJ^>?>0#sN z4;4Tc4&2B>Zuj<2cRji)&B-gu%Gg=Cb@1dEP?4)Qn&#IRwB<7_ab?I=~K&r~EBth+y zFOrr(JIHSEzntUe%ICugnJ6GNW|VLL?g32>TFsQ4EjXHbZ6se1(cm`V)=E1eTLvZL zH*qr5pZ761Tr&%Wb6>&O7#tbm`Uq4Tj*Jf(f}-RbTv=~wZty2>3gaxCKiuh50|=RT z`#QKfd5dyG^F!qo1@+|>6&D%>zCO;buAZS~;_|R^%V0s#^7=I$LK+8fnnr6r&wGCH zV;=tG2zs9*x)F5K-_axAvCuW=@>xNJ4-nFNLfuGYV1N3`mCJ5f0Jf9_Hdi#>sqG}< zDV)oY#Pfwh{Z4Mp>4wWKPE*@?7fP<)wr{J-t1qem&Di}~Zzk~6tzGVCY20x{(=rI^ z0L9H^{e|%JlC_qv3^iL=&xBIR$s*T)RF@5s(Ub^+Q4RH&20??rFN-Y$7`z-{Reyk> zAt!wM`+utzehY}zmxv~$jLfE&UqRWFIJfP}>#VBw?P2@&@lFtmP^YOHRWM}#c@t0( z0Qj;Q8lmg)%KEPc)gZQgfA0u-XBdu{IU?k1IywTF8zm#RfNJ`HlwSz%%O9Ks>^KVH zB=9bNyVZm~$J0Qif`J*OZ8rFT4LV?fJ*_l(O6)y}j`!i?eZW2ds@zp*ipiW-<-TT# zz6Mope^!;-gj0|bQ5x9dUukR^l;NDgYIB<$)yUOLOz*xtm}N-1BgZ*VGrZ#ivZl+3 zEcYB?9GbCjwJF*IvfS_NgDNH7Lu9%C`#o&{3Ssy$*2yfdK@};cynt+}f$$ofi2_~& zpEjRbAnhVE0;xBupy2zTY>AzQBvaxn$pN97ElBte$-#TTGpk1l{|W9E8fqW>@akLB ze?n#T4QtT{&(KT&Zs;aWq4;%vxMcX;T`w3DI?*-Q6`;yjNeT8EHbR-wRjs^K&%#?OK_W;5$Y;_zBLsr~*@h*$ZFn89KM7T85@0HbOz;}f<}D`g7Vc4|^X%d%1SPPb_xa$PCVRm-;dyRW#UxIcPO&8JfA%%>Ws{FtxJH=4W$P?LGTGGCZ~!hsU;N7KjT z_8;Tb|Ns6#c^jHy3&=QYgv3Zl=2_9-q4*CYLtJ~|i9MEQ0IlJX9#U0aSy$Fbqp#qj z-wVTf{_b=L0M#K~2Zv68>M78Hse(FleqvD~3~Ln@)ubY>M57NLy$D7jFb8O5i=`zr ze`zvLjwX>SNBf~RlKE`KRt}o?KV`hTpD?8-cSRU}i_Af}$NY)Sc5IC8knt+Qc`UU? z{nc4l+jB$eT>IP_jrrD><&?HabDU*Iw1hCcg5*7bVTNJZX5T6}F1le^lK1S=aaThO zPIe`p92d5@Py@w<`~)~KFdtF)2i4^4mwz8v`U4ch;j)_^EnE5g`=`8>koN5CKyTf> z+jzIJt?goSTTg5I8K^v-ZSU?lcYD^8v9#Z*Z6fM3(+Ja7p{Dm+NB?mjxsAk|_L*8t z-h6Utl8-lhjdp&;_*}ht>bmoggP^Xas-vKZRz))zH>771oJM7WsPd4zfwlC%!x*`f z{8E~$I!6#%>V3zvg8n&zfvNtRGWXs>0mQ5|?#1Bz-=MU`QL+4qEmq@;_W1;>fSmHUt1cWC7hV!hS->V1&!kOIMH?>0R&&^i;Uo_ zsPl$e5De3MpNeTUL?-QvO{1aAI1k>UmQyE9qZK_#DoL}<*sIl${PQQoYkaMuXp|^s zibG3-OM;vOZUx>7k%i^@rHZSvYI3XOr2)nMxq(&nf`AO~OplDh-2B|^yfYw418nv_ zK~7nYSX#Q-RYza~nQoZ{ zIr%x+xo0y4L4m%3-oZ|pw=!I$G+wQJk3l!i0dVXagEn;IEEm^($KbIkeXe+Ss8fgt z2FS%gMI9V_GyYm)T2P8td_YUg9iSV96f8L_qX49eWxvu1Y4JHQKmND7D6|oW-bLa@ zEN(-&Z5(YYcAx(JGq(;{13S#%t&TzE`GL=rmsR}=Z@9V|9QqR48hX#v{Z3hks8G}H z{uFgpThWk6W!H}cO?)FDDJj^Yg;SwX1{^jNz&Ngo>Gn%$;&MkwBR2){j_$iqoR^aSE(pnd4gxsO1_gyMi& zeHwl*YeC+Yo7NXjpSYGOF7%a%8mj7US2okTN~*5c&uh&uLT{y+1tt5-L^66qWVl=4 zJU`F6M?Pka4=TUO|Iu0^2tMe4%)?GJB>pV&dr;|B9^%wh8A6$Sxqq&Gu}e0M*W+)Q zuG#!WE)5aO_v$YP!-k7G@&}aN z9z{XWQVdHA8=5uQ=%|dY?*?iz^?tpGNX>FV7Fi;Ls%u9gnyvlTv=9MNv#Z1* z#g)b}M>I`?ID`{f7Tg>R_0)y0I7IyTB8>mw?*FZRyhF5b%dYuZ;vzp;a7Rl?UuHiI z2DkuS;WU`hQa5n6#igvJ zBSq!i*41uL+KbRd?T>G7Qk<@TtnBQ0-jg)Gf9zK^zv=k0WX*P@WKL_UI=C=hcabS^ zD7h$eX{!*3N?n`WDu@u`Y)V`53~qSBJ;M^H=!T8_nQ87Bwsi&MTyVovA{cukC_z1r zQ1}N-P{Y$)W6o+ex8WmQ$fX^-+}pPYUpj*>o#evITLp7LbD;$W;e?bqKHGto!pB`h z8D(=j#2J-Y?a~(G%MutB)w-V6oa69Qgw&cypGe<`09uN*7trF+l+(3A2rqPAZ5eNL+Ht=t2oShyV`{q-l#u*frx_s+A3l%{2dHg08ts!S>a{=pc*s--WvnpDk7CIIu!?!%jI$N-O+ z5`^^2h8pYDUtD`(*LJm`MR2_FgT765>18RE$u+dE_7xLpCEg`IMf&S+DbZVxltK;+ zJT65GCVKbYv)Y5r?T|TMu%4T8JGnXOHn}a(k*>aBug;MHyWU3%dsS0EDc&_+?z6p< z(^1xYzJ6HQ-fykEt4LJ&_civ}41Ck?)4_k9+AK0z-24Jz54QH zRO25sNVhaz%>IRAT=emr&-ZWP!2FsKgQK~4eK_{nf;Y|Q&kEL1km^ILE#Pbsjql>28!pW2W2>=_$MR? zG9)Ew$gDb67FQIXiQn)L{1q8=VgnGNuFh!!xyg%a*4`yj$C9C#Pl*UcX>-v~Ew z5N8DgsxSH?7yt9V=L6`^ZSWzkRc4@iKL0ywu~0@@cCA@?_ma&5le|(xB=pIc=rCVs z*@e#Yj84omQ*e8;2jq84=vQvYk6E!5Tz3p{-M!$tV>l*j(gD5t@&!yASp&%_i@Sb? z47kK0Z+-R|Fxbwb*yi3=IL*@=;@WwpbNtU+a=-bal59y8K!t~ucZZsK?SL9t??0O& z3+doLJ>VhKZHvBWdRqX*rrVY`Ef@9tuWk7Nwq=8ETRh0ND1q`g9G}~V7tPO(C<~B@ z=zD0H6gi`nbAeNt-H|Q*(Ax+Fg44i?LjeMrdxjI)kpe; zY!ZYdJ+C(jz;Aj+$DqtLy)}Q@6~1kSp85a1HTRHtCpI7CYDdu~X2mAhnPjpv(77*u zLrB5?#Bs^~n7{l6BEkwGrJL1aw`bd0X4A8;08;jbZhJPd+jDaGuRzDaN1KdFkJM&V8UDC}O$hm5Q6#;kX z^=^#xM9ThH0$pql9}{)WUm3upSjGt>Pmv#kE3_dwu_NwrgvuB$R2E1|${NB222@WI z>%Tt(tto3{@gcU*+H$w&`Hs!!+sY|Qp7AIyYhHI=(bs8azDXcaA)}8*M~8R^9)`ZO zTV!&nSr0nUgAb_D=5D-s013OXPz5-q1wz;%3mE>gxCaDP_{3K*CQTWL^eKERm^8(l zQvusxV2LVJOE_Vr!L_1Nx(jYE@udLEzza0q2Hy=&DLR7M;99ky7E*&dxh-h6A>IYU zPw1UpP>w?H8azbr?Bv3b!F!B0MuqG^$j-pkG%Us{36%UERYcx(0gu zl^J($D^It177q6V0^tt>^OJ*aNF}{P3AWuJH|mcnN!*)8GF^`SIW2KZ2p)lHi?8+a?^?Gn3!Gq_*$P5L? zdZn)Y=?9H0$^eAVoBx0r4WTB0z`|R?L|gQ8z9FB|qxFTSMtZp~lB0UpM;aop$NgGg zYe-iqgK~W#j^YlZCsEhwJg**nqLC#<#gw zVq$8pMFM!h2pluYtm?(Zb~U(>dnq!_1|2CiT(0k`Abng5|eSdA}z6xzfi`hI>SKW&%V4Lc=J;2KF zlO?-ox92sH?ftb)cd%TXnNXsUTBg0HG1L*Vj41u`m@;VWyRB zK1$m`S*z?}c*~=^w?|xC3TpWjk7Fd#1cR;@dT|)P;2c4Q4E|Vi9l;dBar`UP7BcoH zT&RQQB-9o_A#9xvg$ACDpwJ>8lhPAKO_)<_h;Jioly1&#%;s-EM zTQ#1c@#)cSQ)*llSi%v>1IhwnDk#>7+E3y2+Dc}h^*Hxz*Kbp%JHnUP?wl`;$PZ2v z*R z7JDVmDJBRKB_TL~*pQg8*l0RBIyPp0Ol(}tyqK8y#8|V~r1;E&xd`wF;8YAK>{ZT0 zxQ1L2In$)NO596vhN15NMHL2C2NpR5vY~d0Cw&bxm>CNE!q|x;X9uk;&P?LBThQA> z?T3}hHmh#9`3#pCPy?MeIpv=jYU9u~X2li&_<(!}=+w~wo{|G6K{)1zRs`1sml-=k zfiKq%3Vd=&ghjX`z`w;Ti;T!Eo)A?L$_}6@Du+?tJhs3mhcTmc6wwR35ppJv6JvUI`~)O*wj?8D%m&WDbd5icCRt zVQ@uI5&cZeWO-#eCwq723L^6YD+2Rq?K*s)33c*yaQD6ADJU(-sgc&ug$azrE!82x zt5GV5%@sL+I$Lin#1~dGXmvnd#YCAf??4CFBHBLVjVw z!Msu=slyT!%qv4FbufqpQJEf9f;w))E2B_Kt9OXOP(Y!UhQhn&xOkQUAiqK>S9}h> z!CXX@zM5$ogEN2*N?%BMtTv&CcyNsy>(kWX?-}fbLb+~4!Ph%E(6}xuN+!+=%`%?k zMBa=Hh=`ygBO_zQ^TPb%61>bJ<5NPD{NqK&Q2yLQ6~-3FX2j>lXC%tfGc!`l0ALoM z8lMSI4V5edIKU;yxDFe`KVF39)Z#f{%;#=)G=l?rK+JI1Ucd=!*r(M#P^!ArMQCU3q$o=O0x5M>@=qU>SC!s*BecEx<-61~jo zGNWXndEzW%a2gPHfBi8WK8O*nXqTKuUvk&jVAFH>E$>Vg--!g}X#L;|80y>w0f7j< zA6ampeS+2=L+?B5pq_xPA;0F`JZ##u9;%>UcWB|c$gjQ=!_Ci;6{Qwkg84kx08h8z z!T`a{?JD${I$`WD=-B?ThLKsn3?cr#OZV!>(bOcr_F@eHcC!yp=KZzkFGa=?{i{6I zcpMBizOPAU_+x*Kz*G=k7P6j(pgb7g2-xi74fOG$5PJGU=y};p>QAJx8edk!K$fF1G!@baL=ka1`Yd62X2Ve)Pi zoK6Soh5%41yNHhgB}MH;^cI@pk}uzaS{{6P3`@8p`JzgwXw?}beeEsAe}nU$OEwou zE|y#>%x%nV&*_9D7k|w3I;n0duOGGEe08ClEHKA0B>-`1zP&p>_T=Qf6L+uNRSC{E zSafXf=oqQ_QvNUcuVc7}ldH(;lXdNu2t>#IEkTsh-Fqj_PoQ_$p7yH()BBev_}Ph?vMFB%b>OAAgQJbsS~J;) z9_2qTepX@}p<3#;%x!OwG5<`uW(Dd7LYN87Ni>`5yUIamW0xT+c1;UzYp)q7yw_~q za&RCphmwl~XsuR{69P2i!Dr(VPMM|qNnMis#$%h}OQm$FEG-WvSuC(4 zSMr%5JOkQ`>a+)N9kS)bxJJ81yVKfwbd}+^FAm34ux>-8u%XujHhFu~B5`Pp|2)65 z@yVymWT17H;M*SE7FPlTSM7EciFqkG@+uR7h8I^5H<#a;zKxTK^m5PvDVhg+dH~uh zV*GX>UJ$f`#$4D_U`t06{Wks9zfi6F-i72(Jtn_#4vWU8vW!eIr&W6!bs#%EF z2Ye})ffRWN`Hk>J;WF+JDr1nJE%t)|75Ujs{|&K{o(Kxkeh|hA zfg@}Z?_mOLJx{w{bZPbM@b95_^fT7QT=|}|19jHt?T%lzag&B*dZYyQm-jT@?X5rE ze7VCVEhncqLw2h8!tHDAbn`F(YkqJ508g}`)(ysJ%U!D-DjY5!Id%HD6O221rUfW! zx;mPM3f%HMGJP6b1%U;gWo~7!a(R`i;)$$Jfg|ZgmIz3KPFll6b;zZ_E50{e%A5c) zP2*=86NVqnqr{jB#LEKV0E##v_~1!73_ci?% za|%zQBnF#u`NceHNb52IeY+!TID}7iHSbz{x?j3$qMrf)kVR6uNS2ycGZ*d$N;6-3 z*>o71%={TvlqIUf-{49PYQQrY{tR;wt{Ox4_Cw(FkOHQRnan>vUBX2(=UAW>c+MC) z%bB@l(qy3nPjtVsdvI_TnyI!<4Ide3?HjQg5{;nE_tByUe2?q(Q_R$-48JvE4GzO| zcB47mXnfQgUj{Y39eLt%?~Fh+9~~+{o{0!Xr53@m{vLv7dpgrWpaL2=&;J_njxui& zM>NIWi7iPiOU#o1dty^MGM8yKnHYjJsxu1XX!ErHX3$3AN2Z7|xQ90Gf!3=bg= zEQ>E#CmR3_VC49q3X7p-_%gf^t0B>I`wjI%A%OF3WK3T4{vtOoRcGN=T(g9s{( z--(U%_7mTT3!CR3om6h7F+&UPabFeUECvDZ;TjIC3tu1>-9u~kaDgT=h2apc{%kZoN?Myay~9NqINo)BL1lmYW|=iMqj<3#4=* zYs{MYQ2Mr_`q6u*z=|h`6&DjL=71H;$50nou|8OF1hL{rV8w6D*$Y-8)gQ+TTGb;u{&`)8>JW7L*o%8tp2BST@S{~Q2~zXy~2l>oG|1U^q> zu?t&}2fv3bz}9AfB(Dm^F?cbIP3nsVwcw8}ki{a7C@MPCWY)LPRhkWAri-m#KVKDl z$_|+0iZP_x4~R;r1>iN{knMyAkQ=f+$iZt2w*UGBdKz*QWQ5FsKKSLUecZsvsKEI_ zvA|~?FZE7$Oz@#6+Y@A^W@WPUf|j{c`}!xiS_53bAPR>qG(Cp8811fshcLaVfyDOF zUf8&P^@Tqil$ToFRWj*3Fsnlq1DWgiU6?{2iW=w|KyRzZC;9d%wHn{4EXIPQRLPbz zU+v=hMMU|>&iA#9O*m*K^^!UOq4t-G1X-0?MTV@PWA4&(eRi$QP3JYwf>6?~H&)-?7?%O9~H_TY~oAv7_hr z|3~|;t!I$#JUVdpo+B_Lf%e~?vSY2Ml>J|}{#=S?U(Itg#onC6+lja1q4C%Yvu`&e zjDVH;PYQsx9IuZZ_guETq*DOYUc@U+-h0mDuQ84}cec>gJR(5rdo_uXj6K#XTM+c(SjYH1{tkn-yzLP1P{M)yOg=hy*Eupo?*aB41z!s?exJTaJ3lk5*d3^1AECKYbO|k<3k=uKQ63jZ<3=WCxgnq<-xh8<@C5{JJ6-UcHJ`xP zl23ZjPcJIbA}O+^KZUu`3kiX;;Q1l4+^8H$c3k?rtjxr;bTg?WH6tmXD2c~+`6qiz zL!|L1W6s9<#RNx-qd`eLIwCeAA|ftoUSwoKVzgOIVr**iT#d072FvDh68>iR+K3BA zoXZ2?wi$)H1}pv^$&0DtlYHT9n6HdB7C{y_HCu4;i_gT3-rD;HVL2e}%!Ur+CJr!%|R7%gop`@Bw<$2I>}>MZ3tn3}xs&tRi>Mav zWOBBJy<{`>%0;JOr|OA)#uEF~OBQ0$D9Rz%)b}Fn1orvQInaERP$%Pe4C#Z?mgc;NnDd%st#|6=Xr z8Cj0JAS+|b-BmxHdX`-$(0-|f@rW%#I}(`OM_YCS!ArC`ZUP}`Y^wYcbSU||^vw_8 zMy+@V9pds8ln8BEOhUI%pu%^GbBs^*mylumw#vBl0A@>(JqKmQ7C^|%rsNYy09MN0j)ze4x;@D$yTdP#jDx0CvHpTF<|e91&t zqJL*OtiW%L!PMowfm3Sq_6Yw@ zsi)@D)B)yI#?X}A1yldkjytXGGDU?RX~B2fs`};QtyXOZi!QX?5jb}p`trCsrCflP zYhQ7Ko|*<DdAN!R!X_6NuRjEW;~tWb-j;^GvdJd9=A#9d+ZqLKZPpJ@_DgC~>yjF1trG8L z&=TQsVorQx;^vn?JeN-5IXG@6CxK3P7bR>dGg+JtLB|?V4+)CoxRe|k$XRruEbuv` zOoy^pNhmwGn)~A%8?4B)uBUZ z!D!BC)X3k}d^eAerv46bLdY)H_q`7*`~^ko(jN=HptYZfnW9}K%cVyKvwJs zrsTXmxQF5Iq2}!S6$++j{6K{G-OcQ@gg-L-z65=eirneXu$Hqe%S$~!<8GX6o+76n zy`N&{o9rwLhDi6Vm?%F#kuK6rE;d72w1CTBBaR)%7trqq@b6r0JPu~iI^k({kp%h> z^sa*-O&_Y@6~yy^y+KMa44!8lc%JAeF-Zv=AtjKwB>55;)E|*B7#wjxLfui*vlWsH zvW=`|8tZ`eo8y>KR7xC>mk@hRrv<;vIjYMFc0qipw3_@?@qmpkJ4Vka5Y=8Wddp-% z2d{tZYb3?*O+xd*XlnaZKDZv~C|AqrV%!cL-*V!FOav1o!Aj-8)A}cL76#V@u7aY` zdaWH$_eP7J@h*cXwV0yQeI&OMedzw%@iH(rS*rZ@T+Q*n0R6e4(Z%0PQz0l-K0_UA zd$>*x(x7bTlt5#7etuCt=zbg;)6{Uw+KSq9?*d1P>#j2!?Do@$RP2_Dj>6GugQLaY zWJrx-g$egQk6#i+nau43;lbC@Y9+L@-H-(vt?onAIA`Y82m7W2EV!4dCvJe{6ih-E z)X^{QJ29Ya>QURO#HxRdJVWWgqRQ7Zr>Ay97G#0aA#l{|VjPYf++lqNrlmclf$HAL zuUa0`MYx*T3ly3960|^jkP}?TyF>)wywe>Ld><6O<5FDAJ7pRfzp zw(VZA@2E65*HsdbpPMhwFQm1>BbqU8MPpfW0T4!IXlFC%L!qrMUHlNN!yuZ%xx_lI z>WjLyz5`$#AF}t7m~a|7%?Een0!RAppROm!lZbhMCuZ*_&{-izt(KTaG%*i-@JAuj z_tQh+^!-HAD2lzGb`GB&f6?18?x@QcC&FN@E{@m3Jw5>VxLNGMBqkbCiKrfyF*#0X4Hrdo}J8Ada{c?HI>9 zkbnf=LhWPi&nC7r15#ejgudPz4X54~FDu^I;+^4VuzW?YRt82x*z=8ENyMa>CaI@9;b5 z-xSm-8aNAxgAE1yijTV-xN-WtBO!@R4QQ>YYbjUKtHz&u{xS~)ITG{It0Xyetn-I) zp7TEZDF5YQv+5Ul=q(9abv=m)cUW_3pVa(V^uxsxfv?yQ&)yF*gnSC-nZ4HAZx(q3 zJ9@hYMnlCNoWmx?S!K!+L3Y0hlSs>z{W6>0A-yiK zEa8G&O?>~mfNWyJ&O&&vN9(CV2hi&OKHribO?9VRE=`7l5Ht#nDAxT0KbkH8ofS=; zMfso&mp^^N_1=Z;e|^N~mQ&FGd7F7Qyq5dxoI_}V`7oO%>HJ~&h$kDVpB;JF?K_%9 z>Oupyso_IxU1%_L2-LtzNB9#(o2I^m*+gJ!ECt{kyv5+sUr{LsfA>nnWM`I1(XrvE ztjO}{JiPL(0JS~iw&K0t072;7ed+o6^t5x4C%E(W}X~ zHK>C|$298?n~JIf+5&2i`U#{kHYb6OfyJwxoE!c`3pL$0<7P5|gY2O+^aM>1XMsyd zY|%emE7G|NF3BH533mAPrP=nUOLN*S&Y*O+<_rOQYqk<+k<3OQVB^!yLJw^G|En$y z-<){Tn>!H#Ir{bCi)@bm&7d_2*nT1_HmGU=$2b@|SUZR~WiDf(!-(I{U%3-f!)?f) z97^3I7eH3Y*0)HCu@-87QE<_NdvlyGfdl#H2L^KzvO7)n7*V>Qdc>SM0Uqu@4^1fm zBlBVQ=b&Y(5G51@GasqdqFsF84ld5DVf0jploz{0-uJmCyvsbVem`t zr68|h@qD<4ol=Oso4-Y6rkA9nja_t1Mr>)$T)g0#Aq2t;2k}DmK3Ai^9j*~dPjt)a zl>I4Z8VL{KDekTupW*qLLr??k%_;vm#mp}mN`O-O~Z)1n*xKgYt8yy&XH2(GY zSvB82{)I^MYHElnyjgNC3#I`)@h7AIK$>Ov=L6^$6V4Of@ED{A^bC>7rguOG9nb(K z7I3?45Xz}nm3R?@76~`$F4#p7qb3tODx~DFDVUo}UVv(oz;D z&4(Ok`uLgaj-QaG6l}b|*N7q)Y!R`fAokkXNmJNu6eW^-=QscfuS`d?Kg_X3ix$DS zA!p`}(IbP1s}L(odaHr%l4OFTic=$>bzK-~g(_cPw8BoUYU@?o^~Vy9pc(h}523e) z_{r7JL{qI(T}-W0!-X8Dt_p#F#pUK3WgyD5ltbYy(F~w-uO38KH{zYIc!R+60qA4E z*>bVR5b^*TZhrVuCIhzOT`R5r|pe1H7MB z!tiIC81OW=h9*&9PHdP~LNw|*hNc{wcNtD`uXK^ughkOjVv zHb7pfw+nn+8Jk#=x|y8n)w-);jCR8ZY>*quUhQeXhneI@gZYzeQkD!19DGUvE4>C+ z!+i=fXcVg5!B)X3J4+~@$idyoIf^;ojebb#-A5s}G#o_`*=`*;KKP(FL{ZWc)tfUA zIlZ#Oy&fdNRt-d`tVZs)tH%xx#0+(hqksv1<*hNzPv|Iv0`@|a63sLiLjn7_Q2OID zTAOhgDhSvItew#zBKz2cEAu6Ozx~SVyG^c4APUKaq?hJunNFrCtsxmT$)&y$KWHjd z2z=Yl-n-as=PJnd&i2gm#8-j^(8xFVYV@lw9)5keQsD03?BMK#d0~QMV z-k2Ytj-d*ihbze4`3_|33l@oR%or+w^7lrPP^^X}S#SjWt}9vv8O3S?E1;tQ-Z3Hd zhUR_DW3)lsoq2wPCT|LO{n<5$n_}jl3|yB|dRcU|pI6{J|;aW52jq2xcCkNu_;QAvRptkQYZsTh_`@SgLiiQ zq?+P|LycuMlz0*UpqReIG(#rH|`WZ)j zFvKe0gzkGiN*yYki;4d}){i*7Qe_Go_qC|6Dr}YR#g*_pGrGgZ5(j7f!j>`=&0UaQ zg$`s4AsQ{Q>HD@1bdyFMdiYigHeZ~&q^W1Lq1-(w{y8D@L$Zq^^XC!t;~rTZ;Gy#h z5>v9wGE<~bL`}&}>`Z9#OYxEhXT+Y3xe*f(9T*WF9!1AQ1jUE~z2Jrk36D<-|DP2w zM!rDL{97Cg*s*9aEVLW32me%$1b@1M6g`Tz;4_4Pv(dAX#@4_lROVgnnD2fA>mA>*_lztwABeBftd~#F z;UaXO=C2ej6*;7eVhwk2vLS1`0mg{LDoK? zuj~%0zrU_z&@v%jrmIFS0~;?CE~Rh=TO{w&rG3lTKA|{_P5YoI@S|z#pOQW>ZF>4% z(w`0scZRd~QYAUS;q1L+zKgH@>$ z!6dkpa|eSIT}uD%KDF)Otg!*nfGs3|3W#$=LvrY zJr_k7p!zkqo^uebWVF=h+#8?R3J;$I6+dsOsH&{0uIvtdk8^49zBOBqWrh~Ox_ou- z(a&w@O?dYPo(Cc@8+O1Tr%$>FXh=nR&%U{bmwEZJllD`c<7z3vdYLI($AtkES^fF;L#P%UT)y*9HH|#6m&0VZ+oV;|EP9;9! z67{+^#7|DCX_;hnu1VAd51#nyYKzvYi`EIM7w@$5I%Ikpfw-bQG9G@M8s|Ju!k-Bk69qg`-5FH)7+j z1Cw)_TasTN%bi3QN>d5bt08yhreWLRCkk%}Dc$?-sGWvSu~Aj~KmbJPM~9%gEK))v zqNN8K5n^@8Nc2zw;KOqjNyA~o{6!QcpL!q3Oy1@3?2sLU*s1t6F{Dokqr=4{eq`BSPPOrXq6DJV!PlxHY;2O21yk_#8f~m(o#gSLbe;Rip>fk zy?Nb@V5{yVAtdRn?&3C}blt|Q(QKM)KR(b78M~z_LWOG7{5xglQ;Mn28IvUkc&|>9*1-qDRctOR zAhq5P;Gqp65y@fw6~H|VDW%qH0NSe`um_1vaBj`#@6w~lVY*wPD+5zPC<6n`V;O6s zZ2ZjnsToFW10@q4R?k6%*;=q-+Dk1PM602PGI)9HTg9VZ=kaPbz>6DBP}~`a=;|Ck z(?6@TFG_J-t-9MYXxA63%vC$wt5H5s-F^UE{z3rYYm}OY49Z%Lvmo&^Kv^r{#U8XC z2rD(qHAY-*z$^LtrjRxm$~Em{*U$)bFGo1)IVk!*jLj+G?+4m{^B%x!{st289sni! z*G1j?XdQ%K;7_2AH64GE%wjs=U8f6pr=5zQq}Y^%yWmxH;g{YnHr%a(a2Grag{pVx z!Y~MTtxc|hSnTe8WT@!tzt%gcwAJP8{f|^9yI{j!_MeoBhQ>QuhHX{;8Bfr*V*^js z;|l(h3YkVarC`dg)YxX(50nW4s&4i=L)W%S>&NhyUgU1v_cga%}7(a~C-cnXG>n8{z2b z0%(pLdx9%^`S~jDPjilge`W^JL5O1^qBXhO-gLXMyz4?sZA(L4-Q5gy@Is41Jqzhi z)~T8XZ3g{SXq&Q6#6@*;F5#sYkkM}kO|M?IylUr@ zAX^Mhm0$DT+?3zqo8Q2lsItfbhiBrP?od@c&%YInm-1ipN+N;&xdSbOid zrq1?%JSv_X(V{gT8x#chs8(&QU21XfJ=q8%dj=8+nScuc83`jHkc2(M2w`uO;bw@d ztsPHWJMDOShu)$6T_?8vJfG+D{qy(oC4}Qkl5?N?zOMK6zQ+6Lo!{;t;@;s~tP#b# z&x~C#U>fOaA`2>Y!XAo;rKh!FyNOxV`25vX9zZy2!NgVmhs{YTn8tco%*j?K+JkP8e0)nBM_q zHt*vju&nzrlS1U~-L`1|-(#CheWtDdSjLxDQDYA!c z<20x}2s>P4KmK=q@vEMw{-n&$I}l?qtD zioHxchNgiiP=6UxKpqyNoyqo-R+y%s%Mcafr4SaLf$CLR~C{%!Q%cu(vS{7?{$rKl^5GWKUu55 zY=}cT?{qk|_YIuvoPFbZeD8_tgAHvr9B;%8RP;UVLR$w|Kb`o>SUWz0Lj5~&KY@-~ zp#I(PqRaGz%08K~Bgv&JlYO~KG$iOEZ-}Y<(?vUEZXJ-EYzVy<+Ckomqjbk~Te45L zg9UFp|KL1`Pfxi(@s9^=^YLg4WLGzpjOJY@KWL^DX$2|BPMN~LCP)_6sn~IKk^SLK z=VRCan4;2o>*Lt06%31(?xwp{x1CDZv0MpP%sq64U07LImsjUl$mSmRIwBT|1=3jY zW$3exNR~iEdhJeXxECi-B94xT5ySvp4!4sgl5>(JWcD~r2yZcNLoF~2VqzJ5fWj1H zpOebJ7LzR0=dq==oc_?J^L#dFc}As2y%o!D>uI`MbH^D*R78m2&)&=Qo9oSQ&Vx4> z!JEg%iY}*%)8WlC;LY!+hIw;>q!M0)AchYgN*)gHYijwJLhH9e7XnQUEMcgJ(R$kT z#xk%PL2eOhPP-ZEPE+J{03qv_)1^5td4a&Bw3k#SE9(5_cP zBJGC*W&))22wiSj181*y?pL?hGXE1WZ5llKGEVa8Z|V>0b~7VQZFhEu=0?=O>=qhn z_Fp%24IJx_?Q6Wf?|REkpLWHqgbvBAq8ox%mT$>tCF9k`Ug{|n0oqS4!OZuW_%+0& z9%JSv0<+-N7-qtlV3M3P3tfd_EnD#Xhx@l+z-)83-yBBsx853sp?MFPg)mhm$}_~< z-(R2?wZ`^>XRx2V2U>wWh~grCPJlXCY5f32np_|c>_Q}kB!wo0LGu;EVh8qGLTrs) zwtw>i)rFFnENN?h-bZP}RlzmBc^pFnn^WRg?^zthXD60THK0|@jIFAD+Eea+PQi|m znIcsP6dhnw9}%vujkIfOsBJImC9yTyL=_3jxtb^vvu%i_mY)(Y!P0uX z;h!|f%M6H_9W-NIni>SG+YdI9&VbPQ@4u))8J*#z3vm9f1O;jWuo4}cbpA!cGOo{J zoPmdBhP;VpL)i@^tn@F)3mD1_d4DoPUZ}&_L|E;-m)rJfdw;)6vsYbTXLIvdQ+p4T zMR(udKRPsk4iAhx+I_$0E{pS9=aBIL1*__SH{AYh9M~A@k~D2Q{`<5Z6V%@oMwRnn7FYFL3I4*Qq)e;sqwkF& zIMs$og1oVFlAI?Gy2c}K>aRlCWKm|SMmG=LWtx^@@F<62emTK-8}f+<0VCn40X<4W zhnZGr=0JUCsJ>gTZ*Mf*Fx2!x;&Q0{zVz0MF*N0_V5Dt`1<1A+CSDuukC^O{J_(sDThn{k z7FdN#;TI$0xg`DwwNW^22)U2X0fv4u%#etcTG9ltuN^dzM*>^dp}U%{wbRN|6y@{K zF}TI~1AU#*`Zj${yUx&IsOp*xRQQei0k_c2yFMfR!z^MCx;f6Kj5AJ~W)M4})oFoI ziJ49noC#fPm%=WFUx0^WeGvUYIK8*b1&2js#wbJJ zCR56h#m!D+F^cCPn`6kGE6d9TDQ0{KN+Rlae@z;R0n| zb-evq`$&6H=P7V!(`)N%s#(;pxUV9c{7JIGf56X7V#)NR!Q} z@vk~fuMk2t4@SR$N);ZWSkq?BqGw$08nN+Xi|F+vyP&e$3S^JhWwj4t;*Lc0RH%DM7OocHHDZc~l0u zCRWG?qg{9P#C$xRE3dPw9=C_;E&S{Zg>tvyT*B(;^_#6%uD7-&_FV1P_4sx^y5Z8% zelzU$?EBY6w-Z}FZ%w`%(>rz-N$;~(N!E=|F}_bB=_)KGj$0t8cG;clmHfTNBxtM75snKU(U0L&x^l9qe`np~p!(Bop(E04k2I$4801 zFsTHeMq_k@Z=bRbO}j{$a*flCx%eov^f`0@-!|>v3vzQ8g5vz*f=JfL>kg*Vpqwk? zqz9+NERN&*jXQ6iw2dnf>m)GZGstw7GC5v1+Ms59dm`I1@Twn8O~V*Ovb8J`*mt%3 znyPdjaJsk+*kpXQ#ei%dC{73^Kw6nE?q_IQ8$I$-^K0(I`9TnX%yPD{32Q`cED+d$m67%I`zARk@B}pu%)%$48nf9dH;f5B2S5J0>rqiXe zx>DWi)KFX}=#cBn+e15M-|Y&!?RrPtn0r6FnLp%oFQm7uD`23vS8yw~>-sHD4^Z@y zRT2;@j;21d-$0xMLIL~0jr%aaIZgdwzvAyJ-f#t)uOaV0k&0JR=zSObKCuS|Y0g2t z&?^QW%@WuH53HnqKEH%;8=@AW)4u|+nFaXW2?j5a`_} z)XyI*A@I8}pyP*4^o7UN&zqJICXI0il^T^AnF5ykz!~G(b|>2l+SuYa=-ocU0J;pw z@GqeB2iZYe-x=^n(9USVU@V1}zB8~GjGxf9cD&7i=0g>b6;kvc`je3-VZ__EH8sMY zj@fA8Jy!epJz$mgIAbfT{(GBP9Pn^H8R?a6fz3S>|Rj^P=uOTraGB@9zXlgrVJ z&XGH0B&2Ef?!oh2u`MTCm>n+TqZK7+LDz$i$%$|A7D_AAT|+HunJ8VHBJC()^UHl2 z{L0Cn+wdn%sJanX|NZr52MBgmY;c4`P*9+MxFk*>61gk_&SmzNCZ9u;@g3sy-fgEgoZV>H*>(CRX|=xx z+4eskIEXgfX{&FjYc#ad3$@aAd~o~$wciO_x~>gZid5VT!FQ!-TF2N8GISAj=mgB+ zSO(W~xN^a7iH1iOKU4`qho*@x}1 z+p!#xIy{|^ICqB7t7Sm6qd-FHC^{|iCXtkod?5CT5s~q+k_6s-?vcclqjp(*Ikdc7 zztnxTI0?>YN^VY3zM@NsW*gU8LJ~EjF|%FPNKTL{`eCG?-U7K;CQg;M7qNMjZcUz5 zFngp8|5}gA8gN-1Dr>}L1~k}enu(S0G+0DvP)LL{Rv;F}3a_N`<$*vSu-Fn*0-|5p zVudhUh`G`1B5hG&KFIGZAgn$I;c<35y{#JCCn5WLC}niY_-W9K#srAfRyko+bjHhO zQFRe&?1C-1k~B%tD$055=53o@l#*g@mUy)D+0(9%Npvrc8aw{{eZ#36dbUgF>PHSB zgpg4S&U_SQFj0+BKpi-R3CDFI@ECX*LKyl;bdUCvVR$P257XTihbzp_0ZlGLGulUQ z!h^6h?^hRi5ZNNSaS*L4MRr~EgZL7%w;EYxGO8#{m!wNlVtb3&(IwvXex)ROr5%4_ zv$)o>9oD66!x;!kLIZ<2l31Zcd_j0QO_&}Afe^@QSyEZzEK#NqheWdr)rICoSxQ(L zGh1|qDnm`nB-lVdjiP{&Dd-l2&L^3>`0Mc2gA0^mZKOPgM8}8o(d5tP^)QWlK$CvI zGq!YtJtl^mGw-qXv0gHCEp_N5X1c>36T==8{sw!meds0`x{TVl7teE}x0ndFSd4j# zb&ftEC%(t~;Ne%nNi0f}vX^bd?{CHHf|Yz#m@FDCuR?E*AjY#+uYvP-0?EC2Yy^!E zPtJc9{7H1CD!)XPjo&T7d+)6pQ!3f+MSN{&ahTQ`S+wBiI#g!BWmV>{HK?b>MB+;u zoI(7Q6CS~fjTcHpkPQSnFeq6_!j5@`%9Lh-JS`55X6LIk8nuRm=3grmcXR)r3(7PBL3oqIiM9Xaf5QFcsr3>NU&g#|^W`Q;?icjB+~sN8_d zYf*UvE^kDA?WXB)#7-fK2=@;Sg&(=dgr6x8FO|qG50Ihzopc*};(W+f(37U~@ z$4xS$)i*dQio?gJoqV8O@V{ma~4~k zUl#>tjVlv=TYxO}W9=ko8MWU9Pd~Sumnl;4D2PIgy!BH*47*gKM9Ec7Fw7fmLf)hJ=TPbNM`z(vn?jjPH!HT3(FbgudL}F4)TL zjh7}!_-!eArS)+)$yq|}JB_V87DYlSsLfm%Eyzmk--oT;v5n2FFD=2fwGTfgC)Ptg zE{N%UE)Z{sT#zYIhr-OpwM7H^tTsq{TqKTVN8DYI^XzKg{DP9iz@< zfCN5&+trx)QJ2%zcKu9KD_G`}K!SS;^}e0KeH`nBJUm^3$%~nStgsYV`E;~Qg;L90?)dJXUG@jKC2iqwDdR^fUE!TB!$Kamw-dzJ9yjj4i&hU zlaK<bZi9dVk2MuO#s7L*6u(COCIf4#*XjlkO8ZU^ITohfg@mOzZ_5F1;yXW|y zj9M^UzuTt$+M+k;8}uzSM0hm(>%E^xSf9KYzXOnJpC_Ji{*5TwMac!CILhz5*30)uD(tWmTc*re3PmvYDda({4!LL&o;kOR|ZrO)dAjCAKVMN_G?%T;U?L!ACxEx$3mjBCyEU4fm5OZk@z6AIBhFtyDe-Gd3Y4#zRXJgmqJ z2^dNdxtB-^P7XLWZmHGR)Yq5~uyM*mR!3ViRk&|V_g&+cjn@4BWiCrxc7<41pjzrK=h-=Z zU|kQ}qwMYKy?NyL5u=lh`zp)RnDkf{t`O(YZlV;Ct;09TpjFhqeKw0HTbkpd_6J}U zn~;#g3G1BEt4vl8Cn7v5?t(B*dPxMQDGm5^O?pKVGUS3FUhsR4n-8DPk(NYeNywREHm93*_n|SLBvt!ElJ&8$M`JGiM$|NY$P%?GEF+fk6Vss*XQ%KfhB?Y)@W7>egQ-0^th}B zmDST<`&$US3M8&GW3wcg63i8|wF+&n8Y)Yc5LWNBd2}MHMju^GV|jh0L8tdbgbyM; z^<~xYr*8I(d*OGChI`kBS!*{-Y}|yFo2dh^f6p(A$`UK0<-&i%nXVu1Cb=7J&MmQI zv9~!)d};#bYWls=@s(SL9c?GtKxsvr4nbstN)8)36-J>mHZxiZ1^L0{A%)gI=w3;Y%+H<3yK42@tYua{zy6HdcgMg6h7d3OU$IoR_6m zpgi(QJ8LDb3tE<31EW8^u zy8oUrdZ?|GHT>217}$s1Uf9ZSRkT8^;LAlnYS7G@hb<&$2el6zwzoZPH=<-BWKjUW z1-wecX4z0+U%vETBHW}e>dVnK{h>YaNUJ%DgE{S1*hrdb{rH3bj*?4;z%6v{0 zMPN^XTA^0u0CP8u&P} zm`u|3(uEk#{9_3pmYwJ%QA!hTfYb~r0u`nU+8?)J|0S55%Jh$j ziHTnT@0}^h;iig6^dlW|YUrd*`yZEVpCmp@{y2sHY}dHyJQ?&abQ1Vee1^io_iBvM zK0_#wY3jstsK>_WO-!p(-S>NXx^6;-xuLp7S5vL0O|;Vp;*P=uN!FjCHXipaqu-!C z_;l}OV2c%?MM?W71NbN2a%vY~Y)?Fo3;z&FyQf0hJ&ls4C=Bdy0ijl@HMu~_`w&1& z&7y)2IUPOe)ey|aQ%?R)b(z-RM4cVDb7bo_m<^nvlB;CE%t7rnh0l!?88zW!0DMi~ zh#KMs8C}LTcxeVY(Y^#Z6L(sb70+OLYAW%o}Ui>R--{ZJU`w> zcRI2U35=9p0IUdzhJ>3+7sQCiyNt( zNARL^cs}j&>59$81!!60qahM&@H>?ETegRN@CTT`ae+dV7YhGz`&|&4Q1oRVz6oi$ zHTyGip!pmu+}WpoXw&t1$exJPGisYpl9ZGj#)BG_g1 zwKvOqptnde@#rTiFTFAeHROUl0UiVlAfvS;Cf{Ed1kjkY@%+&eK}E$jMg%tkwl`Sj z%k=VWWuytyLLtb92?MD5OR69O^Tb+(Mx_R=hjy^0t)5I^ZohCkn!Dh_ zh4{;ogqSNqSN)S95DQP_Rwk5QEl9~t&Q8nBASt>{Xi!lHpo0I_UmH0J z_A70a0ix0ynftnVBYXY#Mjjz29B>k);3~p1Uu#_@zwougIeJIUGTU?+Y? zNY%n3q55nrJ1b3C5K<8WuyU3Ge^Cd!u$KP&awm#6cmSFR3k%^&CkfUJ7?5(xjKU;Zr6xB|ZB6$&yjbUg&r;}nLKFe*df^~; zK}fXGrBMYWegp5Juwb~-c*SoN37Do~yp;ONcx5dJ1zb;Wy4kF2ZEo$lapX@%GJyDF zwrIMizOuSpS6(+8EgIf+=gZq)-23e|C;z)qRx4K9xCv)TJeDnCUn>cDb`u& zwK{ma7=41)Gw~-b6jJTSDgtMN{+l+vDjAijGDLDwD(qIquZ-5hn8JXnpkiyp)Z_1I zGhU9$b-29zrN8t6e>sn+wGE93=1VS+aU$U*(R}(HfO|FjOGxCS{O_pZFnmg)&eAB= zDr=A;1Ezl8@t%w#v=&C6?z`Tguc@oi)zfwoC5^F8Kch`#)T+NtWVYEv((az#Sy4;= z(?mw33I8#Xf6Rsy&2i(JKe|Amdh8F7dM?xC+Fy4*PX%2*g{^0xHh+@ zu)Mr8^hQ8=M5BT2lYgM%M7}&bU6HB)RXyl5k;i24ZhF~XFIjy1YYcm7YcyIq22#V4O&t62U$hRG6+dhNccS8f4`6B0+LmuYOxk`1o;iY8%;!hYI^d^4P?55c zW#?+LGw@_3rYhEh;#psdX<-|Irl%o`j8C)4iOZP)J%HHl)ykwau?rqjqyZXJLw>Hu zkDv@UMU-~s%G3hXqQfmvcM@tt*;yh6lN%i+jJqT-KRfz;QiWy@3A3C~&(~BgAM<1K zvT_wF7&p2d>J&RsLTgifW1}0IUGp~rrq4t2C~|{fmnKx6*!Z+qZZaDYpZ^1b-=TIL zrO7z}4nRTCr=kGDoQK2Y#C6k7Y9MmVrzX6uhJCJJbH$-N@?9vx1X4Gd_GBWO(QheY z3$?)&A%zen=xt_7EJ46Aj2rq72sYzidiOaX!#9*R@-O(C3!i?8IhM`C(l4Yi2z^6E2lzUL1zitk7Zz3Oi($;gpP`f{tiU^i zbF+jk(T3?bg{1MOQ9}n}PI%hc6%h)t5@zlEyBLwpL;Ym1jproG|B1aIKfjO>5fL8! zzk)BRI;|oZ8C0^^bZKfF3Gdxj0$8||4vl91KSQIHxF15JOlhVRM~T>4r8ZXsq0vg> zE^sx@CYa_m!rX8KzXm`k5^~=hIUm`SqUHTx!_?8OqcFYd>rH6+A!HL|Go98c%EPZ2 z-t2~={a(|`7GlZgaEJN6a!wyqXlcVMBLFi#h4P6F{HyB2lUnuvK*Q+u0W0y-z{J-=WK5byQsFjudtJx zc;^yT>``(g!~0e)TT~O=8(2>ozky1QJucYNohJX3kX-q$1X;E|?1ALUWa{8)JpJra z$TQ?bf|Z#J|I#u#NCx9c)Xsz0&K0vGfnKIl5Q|FCqK>BzNZbK>u%ZyIH!pyMao>W{ z@ET6(p-8qWQWc_rM1AmKXqpbe*AI_F^7IA@L4|#g2hS-`73UO#jiM(6S;D_t5-_fD z(%(!+KFxGg07WWrmllf~a>Td>vTm<};EfE?V5CO0^ zPIN((@E3oD3V(&v1n?XZd=}!{9Bp16q$YqSJ=}rzH1{-iwK)IDhysYfw{6q`u&QpN zSw7GEjSqevM(^Ff0EfGId}#bJwciuJ;=7s$iJ531u#0DZJ>({XKp#Qz;C)h zaZjH=;CAkI2)n7i@@{?yiD#o>I%EJ4oR=YyNmHe5keSs4lm(rVV0Qg#s z`kTzo%nh&>hZhk;V`4hwd&yRisQ62&JREaG8qoMtL#(%wV1=&2>#>qToa2~7pq3sO z5_%klgW?v@?M0lE=8Ii$k)9ZeCjmxl4Y=-F7}#CQ4lg-sI0H}6h>|DXfVRe7!YaSM zsjjIGxal@WWPJ|3>S3s@uBom8r?~Wv|EN(q#v1?5%$V)*!j`_km`NZSGt-D>aJ$xQ zY`wwQ0tAtZUNR0nB=^m~AqWqQ?cWfj`5~Mk7%qXyj*km8-0vA`*FxB`* z8)5ZRN3FqA4^6ktE`Ks2B#en}Tg33xR{-dxVFnG}Mk61v9vNw{yTwd4`TzkdQAVeW zeuZtTf6z_xH&NS9&;jmi^wRObPuTsB)vYIsBl5tC2_p?%3tdvmPaJR3ZJwwuGZ(qVdi;s;y3gvol>A~R8*n4Uxyr=@0=%CsFb#{OfV_8xzm;!t4N=brt3Wr3rd7Z)9DEfSh~3 z)Oa>z7*(5=Fq@UlISsi`h1i2H6Zdt4wcQP*X*B?hPIQLp6XnhK;*&TXG~|tEkDAUB zKUwq`&mL!59cvr}#XWYoLiYVU^`&@u_a>tC;NaNjIy4J{(t2jSd3W6lfU&B zIq*MZqoB2zU*KO&lLOPth62=jxCQdTfi}qk_?@oAtndO^!j515B$Oy-=>>(yfM`gvf4W z+fY+oTT^4Ec=q=l?L)3N5tQh@I>@?t7gElTsGVo=t6pm&0b7A5`yNv64Nuz17(lMh zkc!5Ps4St9hU)~KaDH@y=6WDkv(O7Kgn!AVvstSO(K0&WK5-fkQpFJ^zFA?0Vzw|p zs4`eXI%5u15>e`(8Ky6Nc}-1%(osKTrpr=LZK)zEGb&xsR>Y1e_1F7DGSmVHt|`y( zH?*;(X@#1XIcO1tUbBes@UST91v*njTa%l2(_&;HFDnt4RB;gY=K)+ZGhFkCTpR&S zSTkVeuq$2pRWb)0*c|G_H_Qu%xrcVTX2)r{a`EHNf~TpYByz`WBKGp-Sg6PhgIP#W zk(n`SYk&g6T)HbY2MP!YyqsCkzCGg-KQ1CJ(wen`1PXZ{=uJ*B?gGh{V@z*>=eq$5 zvSMMFn~40XPTSWrFB!dhig_@Tb7MhPGtBK!sS~ed*j-Ux(OglIpgol!7N3Rob;uHE z?atf6VD0mu_ZSfz9PZ2W&I>HymXOQ3sFPz%KdHv2EU+fj+KGEycq|qM|8i1e?q%Il zUoYto9_Bugz-0Tv?5rJWd$NzK+pG1>1%2nGdSNzQ6tEDxOkZLafSp;&{Jy9Ic(_%bdrRQLg~a^%~%V`KWOIoUxqjQ zmvoXIIFbo5hMMQ(`%Ngyd87pinXF|A64zmrjIUQ1?OK;Gb2Z^AH??<`^{0hnRn1K& zG;F$70ctfMBcBD2y(PU_-jEd<%r25v#1z6tzgieVl;;WZf(wGx)?euHr^KZs2;(mO zfkC-}W;mdPZw2G;mxKad-l#QA!>2cRA|^93nkS0AC@}ltGzjdHCHZN(WHg{w#IQMG zrnyeWx&8!ep_H))9id7iFjth9nWxBASc9%6QkXzct8)_LtO>@-J25%D?ugso<)PNi z`cSodp*tM=fA1m0moLX8ED)SZNIDGiq?r*gqH(MxL0)QC260m5ynD#I9eXobE2c1D z$Uxc86w8K?oX1hnvmI3rrR7y9iWu2KA>nx}BpzE-A)zmm4l-vob~aVlRn=6~dHu=Y zz+<{=TMsOOuC#vkCqu%!M~96qqnAcl6D#4senjo?!E*vv^OeBOW{AF5quEtY8_1Y# zbnVD%A(#=9EzX9lD`EnNPz9`QJ!KwlwU)2+DtR$1hKVH9Y#-9|G6HL2PFOm>}7wc`FWK6^;9OU zC`AJ_MRIn-Sr}my$>)l|>t7IG7Q;EUC1BK3a57N%#Xr)s2#Y!Ryg-$y%2I)I1T?HDlp8G=fvMG+bVNN>!@K{?YsUJ zl#i33v^LYF8JIDBjZtqId>{Qj1X>NyL-G#g<@o-w-F}&(ya>5)r1sgZ&OXwZ4&(^r zcgeGFLLyZeogwJS9Ao)FYZAgC4n8?HAE^^w7=seK3Wd{1n{1BQ)` zAeI-Q1O>e!JQ&vKu9IjEDRLcie@ z983w9du-e6Y(xBFsV}XpEodU&8Tg8a7S2b>F_HKZ)#%u6(6cTT_jhh=cg3xO=Gj{Jz_reIJU>zW*TKlp2TN} zBiU|6L7lR~wBl=};Lg6M+z+kW{k~VGSRF1CmMTF(O#_w0_fZP7w5X_918r;5B={)w z*5ixoTtG+|hJDf0{9kcV~jf@Ef$IiN8o zHv5|in7`CXRfghl3E=IVJiyy42&-Kk=mwhO((HnkRyUN_lvh^(p9zI%sY5$PSUNR| zM)Rnu=(>HkUPT(K#^1A9f65Z4nf6m4Jro(~6B5APsbqf&6y}Z%1h(>m8Ua0^;3O$*^1Q^oocJ*|kTp;IxWnift?ldH(7u%IUTW0yZJ|-eV z(Oj>wzy>}0sOIgW4cZb_nWjh!5gob-?_>{LWD4pll1t50h47r|H5W7hvrz0;`(rA2 zkw^hahYTSi^U$)|9~;Si#Z1gNhM^ec9q`_Cr3G57L2pzeW(Q&)M=L_mGH!Z~Dp#3> z7c1~e4Yq15RI>fEr`}>BRsdS*_9T#GtyS06?JHPZx)j*_{AQDVtt(NhjnM=Z2g5>7 zYOpy!Vu>aZ5zrn6SThzfuW+8#4rv0gt^khM_Netos(=Uef9h;7rI2@BNm%7o)$N>Uq1~Bc!JzMZSRc1Fdhf#vcE9sY$6TuDj*Fd8B z1VlYknc-34;D*R}A*A8v(;pI$c?6{4j4>u?2bda;rUVTF$(b_Q{v+M~+~tKUA?dLS zqT{s?9j_pEhb`o-7OSMiky)Y_Dm15JtnOSU8zq9i-=t2{WTOLx&iX+`KD;@Fg8gtX zm}H3rZ-Hj1qHw_c;L?D=guEA&gmrNmYU|4DDw?`mozU@>bcC1*oJXGRe{h*6?tFTe zJBaq&Ly=u9A^+L6gf=&fr7{k^4*KE zei8En12y_kyRP2GyCq}0GTGc?foIOSa$BPZ#KR<#x8j^Xw%T57yxg47M#3n;S0*_m zIVC%%LI=LE0c~l`tYN(gkS%s4a<%Y*}`C_Ngp3+DWjYwn+lI^vR7n_A@Ts%ZO1fMD(l2Mao_Chj>uVco1G*IkkC#Z`58JvDU1?Pb zgz^@5yzibKzS(!UKmC3TX!hP6Lh~N5e$R4&!5Y0(gKyXA{DAH%c4+yDR(Al>R>lNs zk2ih=;GDUnK>S>TX4E`xCi%Om{qA^%&ypwwz&x70($a-$on-ij)Slxsdu2?D2Y1PdCwMJ|YmG}`a`n5D?C}OYdJR4@>wPi>&#gg=4Fn#-mnl7y^xgb!{44Rqwggq_a24~OZ7Hg%f)^}?436*s&E`12B$R3tES^priP3s|VVPccER* zEr!;dW*?njm8`=@=UY|OP}=1BCnFS&+hN;(d+Lj5IH#Ze@Qz?;-`#lR>vFGrxb+^| zI23*t=Ey)v$rkHpKXb@Cs01|xBvSWRZs`-NSa0w}aYs1}_T0 zwgSi{ML`wAGH@i-4{t$Akt7)t7{|a$!fXr>$pE89l6xMbA#5{@Y|vwPa|Z^w3FM`q z;d~F8X@VX=dTLfF`mAXpz!i;x3^Hlq5X6KE3=r(3TUGE;P&gr>RRt|XZ-a|xFeG^H zJ^B(ujpjmsCrrNpQG08+bYK!=9kNCnH#6g422FvbSE5WPk4lLlJFBmz=i3#?%;NO3 zO|LR@4O5L9TmP_dGE#)VV=7R`(*rWlM^#!KJXqGb!z;KJ18zx4P^IFk%d zM$!Mms|-dOqZIY&!DzdJ(PqHyOq$H##zcwZE&^F~nWj|ek3~0PUjw>|F$N6|B#EORSY$f zyq(m+bC?WV&WGy|mHk@{9&iXqXa{PqE4B&6Y!UrDAp^K57cH!Watyo$l1oQ9YyG$K zq6$I^eKffsR(tC;iE&o_M95iyy&rH^M7=G_h|T4uNWiaYQFQod0aAxQC_ua>%&Ud|HVKuN2K;uNf)v|A&JU6~Ero1R zPJ|{r7s74)1-QY>v`enQ#3`%uKjG|GvBJJ%HoQWilR!tKN&s{ zp?zul?fL4$@{+RR%GsZc?HCf=;SL+aMm3M1dzy`uFt5QF1|NyR4mO1`{VnJpCOfc0 z;0LtI{1YRE9ST1g`;1V*?~BQxbukJlRDMaG=b>qpWA!9&KXm{qMgx|?K?Zaq`Av;x z=!RR#h~4x#rojzBqHJJs(Gm?>q<=U_PJ9ClM}8{!rf!u?!e$XiWS%Ku0aLLT$R#Ba zGAZ4(MVe-B80#2+7wV`O7Ea9l%2g@{xqlG*a{!i{>(P}}?8wxqBea0L#WNFw8J_`( zuuR|=_pCq-C>TE_w3-VbDJ4>c=ZC7Tzo`aP1g5~g5S5pkanWRZcnD?Ym(%P47gfR$ z+q3aJnmQ3`8_o|C#a@CI67%_j6-XD$xYuGy)(d+F^aTGx$;FWQ5kb`u@tNY=qLHFQ zIqY4W*Lfd^4MFU;&m$cXxYOZ0J_~bORFNqXv`+P9Nk=n@{zXWGr2;Vs2gXUSa_93B zjwBvUk%`j;iBj@X-;Jw9^Gg%+l2meSma=M_F0>EQ*L`&YfQ%{ z4TUvjHN_BE%m>qc>ITFYf`R_r{5yMlp@lqRl+~TR&jAAWVQRZSo*VwUh%V%l3x80e zIVGRy$(Uo*b}wubf-Qs)@X<`0r3%?pe$-Be?gM*F?r_+C1}_f+*)fRT>Dsf`bJ6OW zrvPQ2LuoXW`K6-z(k$rjNE1StpEy6FG`xuX3K}`!u@DZRAW7FSlmU?jCT^MoAk_~o zvHJ%1Uzt(-Xi+>c^N{(0vp~O5s^DdmZuo`D<>JUlRc4MNM`;c9Q4$9zrc9$8A+(S4 zQ=-Vi)sHmu{P*?uR0IAtOtZjK!g$57w_UamW&Sk_n zS6fn9R8^vzji|e)?=%m34}RHue8BVm#lE7RukW0_?bXU^j_+5G-x~jfMuC3+Z9r5_s+5fzaOw9KmSb$4ZD-({Q^iw0=IC{2ad}QhGZMeM1?}Y<5G2|CYuhU zUI8IixHZUCJJLNS7r1fU_?XzF*yQ+YWWeRPi~hcN$<^@r&Z6Y(1Upt2WA-dms=QK( zON3MLMPMX0zU?tyWUzn6G!<>68t9E}aRia>F!Q7VO><}Vz&A^G0M!gqjkBS^)gSMw zV4~Q$Xoo*^apTw@K{CmYmQ0GBTZwk~F|DrFLJ4tAMeViPZhb?Y7osq$uAeZlq@MDsW@5a%kd8kYv8a?&76|n&CALP{3)aLc=$V${AF~lv__Xw_k_~APUmha3 zXj1qf&w8lraLyX_8bIUokPH_>H6g&Xr@6Hypl3Q1Lii<%GvgM-W~wje$Y8X>{H)x> zNCDjC&)qnITKHfU>nd>_-%xPJp*rSKvW&3s`s7WMP%gD z$BAJ-i($C-C>5=d!#0Lt_>h@~0=3qBwj?*ah@&C@Rfj<*jy4To;o*3gmi`YlF&hMn zUlI~W;fz2tJfNRxL?k~_6n9YsoGi`Z(EA}>B9EZKEdht@-X7|?`*z#xEV|f>ggp=Q zPWL2H&t35xA~Or>zv$dA1OYJKd^wdPz(=I;A!p5EC}m3cA0)B|v~(w^G^nXwU!|+8 ztE{fjmDiLRbZs3iZLLmYv%kIn?O@=I6Met*z34yJ=YNZr_w5jiv7GVkS=&GC?*DGb z)L}08m+j1;(AtDrRg!Hey^= z&Syz1CY7jCvNP<`qthawcB#E0nVx2onVwfYZ~PXFPjp~RnYc~QnU*d{kVZg#1q2_9 zuE~=#lN2$@k!hlIGT_R^_z3P>(4yvdA*IkRgUOo9Sb_r3AskRiu;v+kVaD?gP*hYZ z6w0g|pOSU9du%n8lBB8SZOc2pY+2X#T`N!<&mG@#{`@pF`I{mrUna|@YYDckB9P-; zw90pv?cpI9om2@0D}8=cC0gajgb}%SDM)LD22MR{%bKDtxdc@)ll+(1qRkZ)n(2>c z5_t0qxD-HNDu+t}aOo1_!%7vm!LxmL7M|4ceap(WUs`dV(@&1CJAZo`9>m?qhCBMI zs;W9&_1)^Q!ixH^!m9sy5TgVB*MrRuLfq$bGb(ok2H6mo|9KGTZPN!(d%K%9#lDm5 zH$WhN2!(qy59f&7MV(Y0 zlgySq%h7;nw`qVBsvLA9+NHLA-ubA3Elt?VM7(v zSyh47QQuAqZS%$3i2IO%+2+f{yNv7qTF}Xj|E~qbGhwh2B>PnIoD3!G^$!UVOz{%M zl2$veqi-|N+D7Q-ZLDi@y*v9`2$yfPwVxS4EBe)ggOIbSFy^C6)N$X1!RrOtu?k^^ z^qXA7D2Ie?y$kiBD_-G^XGdpAl`%3YVr$T$u6e!y*ne4Va!23WZ28&J zw8rF5@nWVT7o6^fPHtvyiP*|LCDgjJyK@HBgBsGciMh)iGeTBGXNVQysp9W7Xp(NM z{hlLw#rwOcR7I91Giw!8N|YUFNK0o!p!r6})I)(D_r>*`bQT*^h7V21L)@8{)dhZo{NgpJCQ3C(s___Z0G23dLriTtmTV zzGtA{Vw#Cwr4AT34l;2M9J(yOtg|U0Bcb}kCJ*vo#cDMq~J3I>si95_=9fr z!RPH~2!$@EQ2{bFx4)oFJ~xbSz>txTLTzJXK>-0IY5D1#q!_dQ6bRtS%(>>*{%1{Y zbUwM~(lszR`@wbIpwPS`4{t^Gv3`@G9k3j%xg|EPFj%`AZawp_Ti@gG^43Lg>+6ik zRPAZ~PL&IZqj&WXW%vyPw(ZALPrtWo-+CuSOhKS5=HvD|gPo5$whSJ4;Fh7*lqeOO z4Et`LdP?3vlWLH4A$ptazlTGKj^gNMw;``~>-{*FLasy9!iu~ATF;tirj zK-{sxvXK|K=muKHK0UXRgHA8Pr$NL~eIV|!2ZDqFoCC_`a3C60o^MuVY6Z7R7F8B} zja?MuZ|Sn_&jj59@JtojN``DwGW4ErNfF-VqB0CS5oO~W?h%<8knIRW z?2-yGI?+VM%HzuOwDD3W3DU?ymQu8hk5;N|RBO1qQUXChQY6iZW}O>HFC+I~70FC- znk=P&O36#^ZqVjMac%^q0Vb3ge=^<{q$C4l!{cJ8*qG?JsOSigl8lLAr6glh60*y* zm6h0m%tSgGs2(J{3`UKHj`X-(L>t=w7&qv zWS@{?qnqiM;hgAmHNGysC>efmJBL$}BnL(H(o2#jQk3ymI`@Du^?FKcN(=Z3L*Qi6 zMfKS009uFF!?=$g@Q6nx(b4N+b|cIX_yE|yL$ul7SGArSXq}j11;0PtHOc2%+_i+F zB+&m(l^2v_&dSWs{P4*Sx#gcucgK$D(0999(m?_uI z%}imB%Ub9H=n?gO*XhuHbD*%-qr{`XwDWmabbrQ>R$sr@aLsj5+t8^0KyAPK*l%5b z-naldEhin73gvUU=ycJ3v2A@86JFyv`_3n_G{rRP3>tGW;-?`pw7UOP3_1&PH0 z2S+e(2}~X3h}=e6)L24Y*8*gtLD@ zlC>nz9eqTr-XFV$B(^t{FzWi={H3bd(8!H+vHv0=GC}?Rd8-3(xD#Sqj zOJ@bhd?i5SxacfP2hEwyVJm$i_66nwK;g!jIa?2LQRHbS3n<14D_J{ zo{mpGqzhsrzA$j~Wm|%n9XQI!xuT6MrY_@)6HwmXMSi)D4$BWH34ruEfC%#qau4=} zNM38U0Y~KyoT;&=qmD*e0i7Tb?HC;t8A(M&N5+TE3-e4&cGcx^(r4>&(n3>x6GN%E zvI>xl$WN4}$}+RF#FZICRH8TufHZuVNbQAeVCG&C2W^H3&c_mcZVJiC9MMval6*po zLfCkTD%sk6;`DsmKcSAscI(*qS_N1I!Oh*v`IM6xk?WThUMFW@asX)IIT~z9!~ky~ zJ1aGiUi}v0VqqD~=)+HI>2Xp65Y(yGk};Juk=hIH~KIk4EBKDOAycAH^xpxbT zeiG~YCc6UR4z)8jBGmdkW&mt334;s;h&&6R!(K^V>M@X@ucR8h8MBK63j*cA-B%gj z+mV*Fn_G8OQz)kMFBr8;AZ(rNpcB_eJV_2?Md zNmqni3b^R6S}tPh3a^&eKzB_rq1udJ(a^gT?Gx=2=}X~r1nM57k@z4kCerd#3&0`a z?muZ1Lqr;Dz~Z5s{WOX+#tFo4_&J@OT9SemXG`3e<~-45=XR%B{+Ylmzbqe#4p+!yaxVJS{+O;JGB) z`~kc(7jX!l^*jS8n;)j}dw7G&d${3u96F3b|FjuZx`Wu|d%(`DlF?BoL$-&U@QP)4 z?}<4AVKbL^!%R=0d6tVlu1pIABH&PdQ(kx-%;Go0Q^A;-XQmhbbCIkidqCE~=g}n` z&{R7uI8?)BAEj(iI@r}4(|u>~f#4NS#{!*|Tr>;K?n2t7h=#5^a^+PYq_0znt|VNV zhu$u^gfwNkekrcv0134uGBn_GV;qxUId4e-(Da~I7~s>!hsfGz8u$?Yg61tt<@`a! zpN~5iV^4wN^JlN=KlK(1Q)-f`tF-ZFXf4fq%b4?Gfj)sYy%Nk>%pGNeRp?TBk((Nn z5}X{`l*9a0B#cn6p=Ag&y@tEPncRlF9!d8RDHFZlb9bbFgny(c3^+XCm!$)V2ehAP z9#^2pgAq5Hy6eYc6Kc*NyFoZ(J(fmx_^_)&PoETCeX0k)_{9$zZ>-OKvp0#ahTAgp) zRm^-E860SE2qz?*p7&8e=5<}(oztiWy$9=QNDAgQbFnt;&q8*8e*LWrkrjxburc{b zy$||G=MuV_PzAeFT7lk@>G~(TX9P&nk{Di#!0n5@XwLpe6gqJOA19LQ6YHwAOQ%}7 zAZa+$P?rH~50qJLSioY36cH!+4T4161dK5ziapY0cnF?wU-3z%CI2yQjUh^Wnogj-Qr>!4Pz4f5-6GS8q~s zoW!!a8_yIWKK_XaiH-|lIqFX*oJvyl3&-mdBxSm#l8k~HZS>2NQ4+ttP1|hRw`uQj zS!ljnMreKx2)06-*yNycH(63$RGU{z@m8dAR98Nw1xG+j^BrT(quqqWQfOTk6J71s=hFy-g-ba0VZhAolkUym8JS;S&?)QW zpO%Fma@!jj$d499M?}YfJR->}UB!8+)HW1KFG{PWRlB5&V7C)jj5~-=;a)6)d!b-g zo>N@>P{J-eu+maSQK6!}2XJn}sdJ>}E`d?=D|X-Hb+rPiW>+#6EoOt*B;>N-YL6>!bBK{k=`RoZ>aR3`Rl^zlfu zfGhr20xmf^GR2l9CkjslZS&fD{sg6TM_>Hq%T?VXgMiM{Dn7{;0#&$VY!{oJUT+mE zWR{euaU?2Gw9g3VCz#J$5FoD6MZSlzAGx8K$?rUfVbSGjGoJ|c<8_st0ZE17$_y<{l&c9yNg zF2kv&3YgvfyS;j3H^pD2eMfZ&Ez@F-Mw>*M@dK{}Rs~d2nq~@=)qT3Wbs)S$0g4}A zjqU?ctu+H$4+g#)-#dP=`PBeYBEv#>E}^y}NB$aC zI%hT!=Mdu%bDmlR%7iaz^xnc9!j!7yE6JcoV8B}S+1riLTSSb$TDx%13z+BA-zThA z`O=b%{B$&lJO#85Y{<)-M4(t%Dh zL9;W_nk=+Do8J!MS`gkCq!$HaFh6sU5E2%2@+>|{>;*1W0eT2B*TV?7*xZucQrsz( zucYAaQ#Nv~tqqsqz$PhiH95qgWyW7=0m} z8f$^kI&)}xdWcKl2FcPOpn}IuhdIBLh?;yRxZJ7ErTkm~bMgFw@Vo+GNh{}jq-!Af zzwm#E;_HUlQ9)r|;V@Q`jQvqEact9KQ*7o8)Z?yfpXymCfCbi~S-1O;1| zn>4BjexXx<_9PU~`=X%mQ94*Y7^-6f(QuKKJ^nbsZQha4lrj*ZMRWdYyQrp+gcELTlWKeW9=tG3*<+KdjvQHyHZ#O;K|v9yR)t9A)lp|X;vwvT0dn*OTm%rUFZ3DK6v$|gg5@+ zB-X_gCUe4A+N%F@@rTK^SN+gpFroEeLeZS!GDOJOz#qsYs5OzIpdAMxX0U(&ETdNU zdy-uV7_wK-(Jk%eU`ORz`VT@KfaxszzXPy!0F;$*Ud(3$us`Wn^A`}BrxZP1$gn$X zFrnqzec@lR!tSPybVYc()@c93e!IaT#l4QMQ?3f6_5{ zZopLMqd(BVFf2HtZay4xF zN}bE-M#7bOFke;lOr{$M)u_M>iucsVAx)Z^m0Xi}nID|ypBW~JKOgTN7aAKD6BQFn z#lxslM0l{eKAD&d^~vM}v0NLOlT&Mf8v7x9oa<)=d*Zm7#{CNU#g5VDgyOnZ>%qDX zdAF!%KoQleH9Bx_@Z_L_VjPrmzv#O>PyifugU9+q#k^}Ly_7GNk&u22G_k>v0cSSA z`a?6qI*Ahz0pa`i;X-yBIsW2W-J4|g8Bpy@f1JmcovM?E)%i^LcJS>)rCaui3R(&V zWeUE9JYtvY=MldA$N=EXh@;SsW2CrQgE#y!;PJ3zhl!fscS$0hLe7Lb@bO$&GKTQN z@1DjVEW@+1MMYkcpocHp&^zU54u!qRwRL`I1;oPRY(%C}QuK=i*35@pGvx-T_pFx2=pB%Y1cfkD(jd?83my4S5MlJ@mOO$811P?rH`^#STIAuoH6eJOV-)KbdZF19 zx+Zsm%CIFNDVJW7T-qUFqMXBQKu?jUp~q2i4e37#FB=bA*K?FJ!Q&>1&~tS{`$>ENHp4L#trHzI%^9vBe{5oVCs zjg3Ke44hH?(to$VM&UE$c-f=mHmJgZCK+#JXtZg=CP zq3gWc9ZS8++Zm4nvr9Jllo|G*%MM>Jx>nR!%onvD^E&Be6T(Nm<#fGQ$N4hv$`-~g z-<18yFF?Q3&CT4#(W~wpQ&rGdS$B!baidksAWU(=3yjtEFWQItngK^Q}Uj921LgqO|vufb=)?^ZN6HxsI z=(KVfD6M9dWnGqD+Ad)tokMIw9H|}9^!tK{wTrfe^Pv1PaQuWa@D)X_nKWW!#=#kb$uwq`-&D9Trcd->a@&dLJzpXaf=9w5Ji9yynx96 zPj~U~$JW=SuR$Y#TyfPKEd|pz%$h!;r1Y6oSCkZ#8j>o@N-s%igzyZ=O{rrbejFHP zmk=?}KQ1Xxm-qcHE%Xlhq???23r7O(l9z^v{4{^YF;phX^-2w5NxZJ#%2s>vcZkDT zf&DttR+v`JNz^cn2owPaa6Xv(1p$%<-5X?TaGDo5ZEV77_E4sxhXm_ocr%uqMLWNy zO_iaY*m(%;>%jZ&fN1uX6KD&V&tP)o%wBkJzedo zg9RM`*?-ypwC`x2-O#Y&@IcnJ^sbiX?%w#8KzTz~>;SLU(Y^o$PkvAH!nMxMjdTgM z76DfYPqhfTRlG3`y-RpD*2oqWdS{0G_yh{Q7ZFT1ZXs7(^F<$nU+zh$hJ`LIdGRLe z7gO6JLqfLi$JuQD>z~=Ei>d*aGN0*T;C~G6L*m z%@v46;BxUeM+-Q=;+cT?d;;HoglDcMnju3|xp6_mG3`N4ngdfOjj0Lf_iN-s{b4T0 zE}X@>AQv|d&uz#7tc!nI8hyZH7lgWyi;LJ$m;Ie-A!#t92d_&2rw^QbkG>LG?_Ujd z3=1xnQd-YLN@5oYUsSvjK13BU$jC{B@G z)c*EI(x2rb@o)5DDlc7Wx!7ncXToAMHVFMQTgqChtLpf?RZC`y94bR%HSmKI=n~=O z=i=uUaxTYKeqKr?tK4X8(R>-LL0d??1_;z*bx99TuQDc+h)aRA6L4^z_EUFNBR% zPxf(+*K=@>@;mxiyv6}x$&dy*HK^Ko5N#s-4gkoEi+hp%AU17AroHO_-dZVyDrcll zlpi!;^Hzl&;G|6#I>Hy4YdG68BJltKUIM#(6+JVb{ zFyeYzMQ-Q$?y9x|g=44INcX)iufFCUw5-2rFs|oJ!_W~E?p?8G76=x7^0+KMi8 z&L^Qm6gYCANnPaacOk%?+9wPx@n%V#aPd?aotcrB`Z6!Wg-K3?K{M~w0x-#lyNu_Ju zyi9Roa=NZVxNpSKeYh;)BpZT*QRY#!2sOQcqck6qkKK@bgaFddhkmkN4FV1=kAwuo z3iCl5TNa)VF5<`Enh{lmNDA9S2I7mBflIl`W*{2H#lNsFB{U^8B|Rs#IJqh~4f;T3 zF~_2Og5oJZ|J=)gx(zjT*UMT?%Nbt>S0_go3I=;-hgU|{L|voMXYB;2TN&feiP)OB zy6f7;gZq!w46FXVml0Y|y<;id_IUNPU*gPxFe7h7og)NER-K>At>ngYUaHrV-ywbOP9t-FqrC^swI{CriF8x=E6v1R&vyzJrIUfiIAT1<1oHfk~)3?#t-{CCi!dck) zZzB3Gdk!EG@9)mXg+`9tmpk%~_aHnvuYavWdP0m18`}s^Zw?0mk zmxM_}vVvs%nrrnve|{DnC!*)kh)fyi0f$ z`PYUHrCv{NPK2J0Ip!SIpZD2K1exP+X*d&ZV)xwZem*=P>f}KKIfwHcP)SRWCm~;dUT4o9^TSpWtJ zU(MCyaQdLYtOtJvAqswA#>bQ%2UA_pNa5TM^}Y{)8I_|qgTq;1$-aQLTQCpsKf;S_ z7^TyW)#J1x0X{x}w)Cq$GDe1n2~giPps}I{fiC?j&if+HD2{7EaU(dc6~#>e^Av{@ z0W*1IB&>r@bU=dlyo?~R7s#YtDr6f`hD?nR8T)n3a!s!B5HklJ+Fgf$@CL@XLxT9XY#J)cS<|YNSTO3Zs1sA zB4Q(BqFHhxj+O`=M~juZ_#`PUmsLqF?aX2z>9-1XptcKnh8&KTfl3z;A5T2@1pZ(? zo|P3|2=o?D|L8<-Rj{dl9JyES4=#2bTx^@X_#x|JJu_KgJ1z#=8jb^#if0VC*y$&| z4OoC7hY{gD%H{O000+c5035Ie-~cF7&z`>(@PMQ%NoqU*GHU4je_!nP1XBXZBlr6;%W-6$?z>VV_Xap9(EheQv7hQBC(`#Yq z!;mu*N&(7MCc;ZU9n54?d#D~*;vgC*IA_COCW^I*v5vL|5`Sm`<3Om$10~PzvF0)& zwLYaj3Df~$>1WQxJZL=l9@5KqG&T{?Uhxk76@KS4A{&xZ{R2LaTppMnpk&UO{$oaX^WGc1T$b6DV`Z^N^=W zQ)DU9lTs$k#?8_m8sdy$Zia*2MQ7Vp-e$-d?AG+(vMzi}}tZ55@Cg(4x>n!oGsh z^selV;{KOFE##=*v{WNz=c{Z zEWDCilLMUk!3Lo1M!PLH#dFtxA`dSHnv2R>27iN;N>B$POBzZ#@|w?r4|$)+EX^p% zD5h4Z&eK0HT0x*Cf6zZJT19|TM-Kh-V)l*a^pA^I0r$sUSdbS3&xd}!eNf?A0aBm+ zZ5`n~m)kQGDCZ7u(oDG;t)NR!mh6?BX@SSP?l$TMk7sZeFLI)Dj^*x1J>4#2B3t}# z`UB7S#aO&7kWM?4y2(_#BSWJz`s;{69lQh{@Sw*Uk7MwFCB21DvIc?abCRHUptLDn z(l0XFCtk-denY8EC;*#`YN^{>A$QYI;eAKqc4PfB6U*zkaJ^RN$L?=3E_%5MV_mz%oD zDXD3RX%>Zyf1Czr1FLAS6_$AJ_LcI8Vn0bxQZj>^NJ!Vv3)DvBh)VpWqRWk!x+?}L zC3g(}PUJSqTVyS}a~Pgog(KF2leT9A^$ngw7B`f%=QUfUa5o4u;B2L_XUl>9UVlA- z<|_lZcs}gK_lw!D1f(9<0d`^mk#|L215G2Z1n-^TYHk8Llc66bOyH;?j^gA^!CWC1 zg?`Ck1VBDq-HMfPQ`ZDtQDP-w;U_gGbPF<%gIeORtx; zhXkAEv~@Y7w8;#<}{1e z=~L|wrPWB|{Kas*oE}lF^L(Ry{5Sc+uZ|iPoAXtgd8=~_# z^$to!4mYs_ATowGupx9MTigT^Iw((EFjoDp@}M8-16;H41^u1=1_CdHg6;hUj4RiB1&HG9fOB3XAf#NxhWCWv?fqO#5`}j(G|}n77yZu% zUkn5e3&;sB@7Dq)1?})fR>jA}_H9tHs`2Rd0D8bR23W^cFy}Z|xmKy5L2kO0fir?3+9Y)d9MlpYZMS}++y%JnKDMOcAU$j^w3o$m);MhgEG9d z4KKK{P9`rc&XE{?V~Uo0p*7Tg)i7Ohs`N=0?zQV|ApeU>K!icKO%wlTJF^GsZD~n zytq6FVCPNk=JHgcDPSd%<6{E;02iMFQP`kKgHuV2yDF;f5fUXfG*1ev?vGJ zCZSanOy5awDW!-U7d#In;Y=`ID;klj^@1B8*QOyw?s^aRYjk&Cv z`jZHmzHdJ4ecK85t+YawkwV0p$E45c!{4b>gWV((pv@Qo$&X_Qao`!D~NXEDtGn zlLQ0n&DFeyay+y8qvrL8*KIahU@i+Pz90^K)^>Mf@E+Ck*~I?u&q(qM%X6|;*Y6xT z^4;+3YltaF>#5rl_$iUyCmYNhqEs5da>e`2Ab8T2yM%qdYH(J5Xpuk|p}r?m_mdd~!`2X<$qVr9L|Kt;p)9qqP8%t)8MGZc-6DC;$FU={gMcgW zqy;BCxQ=l9mI!$>4cs}5=qv?hW(&N z^S1JKq818FK7^WTy3Jz z;^*|Yi#Gx5(#R`B8uy2(y+qS^lCIA7|2Bxp3;!Y`imVUy_Av~hnd;ztFmjE z*C^=s==p0>xY4@QxVKybCzGF+dH)LSRLh{9>Rx^odMi`cH^p5ND4~`|NBUU-GYB;H zp9s&WgPIcgq)`JG4Wdqxw7F+Fp{)@Pri{+=sqZ^6^ZvRGo5J8FGP1GMl?rWiQ}1alcx)jlds()%sHZbD-^f zL4T#c4PvfSgViI+;Im7{vIn#t_g!rd?y7AIQ~WUY%fz!AyrI@V)H~t6+L_&n3U()k zq3E`m4=4rBrB#B_Ec>cm3Gz0|G%vWw_OKeeOXC&0r0m0&~yx2C})j3zGw!SAF=o!hoUZASKM+c!n?jrT#Wvh)T z?%qIw0`WXAM@#~>WCcparoKz(@|JOu%WByk~5`;rMAuajHuSN z-yIGwESa7MU2rsKN2+;;l!sy3?7&_NcGIAd=M;(;@pk z*EpHWxr-_YFO?@;07(@$>##=E9xr=pCcSlqy1bSnwXn#T8}@VA+R3Aj#U8l$OM%>)MET3L1}Z-3ZIfRGWc-iLL>yhrP9JK^?WNVFO4!#@k@_ z3{D+9ES$Pd_S6-qPn}Jk+KFXM2O!f$AXBf$8^`hbyPv|DE6vT? z{Pixh!iJT7-!OsCBlEq`hM_W>y;<1(s2RNloD!5u6YZdRn~j472VJ;$0NsKc$YJwR zI7QZit>)bA{m4REcee0Ix_uo4 zS2aF8J`nWB9oKWC<)&G?lP#~wn3#s(8v&gZp5aTUA4%R3Yu=c_BsIr=5e}STv!ryG zk>46mlO}hjs3+%%1ny|T9y&`>B1T&pQhihWk^`^hGJa;BXPoQ+Yyh2dL?Zo*C@2FJ zQICi)l`sSg0Fwxch>U|ufm>pdGxTSqxh48vjH^t@PbyByNzW>-xF$w=Qsps9oLH8i z1FUgS%Y4i|M8so9jZFuMW96cAp?1ON_@71A{8$U4C};-%P?-;Y*Ai<7JMu%$i9Ca& z(|yI~k^?1ad8t20>{r-s33T_g2yhMnpgHb1e9s<~0MIJrOiNv)j+u`5ZC7R#5yNOv z8(K)+C8F{6U<_JB?~1zI@4EF-JW~VUk<+C^?SpNCoT(**{EDnv0`}2wg*~2oXr(M7 z59pdCxp2Qp$YD{9l37C(G!%4XH#?Lwq8JT5BKJ#;y@JCxxe!wg+;oMQD(OazLhET? zWqU$bNn6GM^8bQ2R)hY66IozgbTq@hrI-o3;??a9I|OwU4-5$Fovyf!De z*ex><3BHdc}$WA!tdGmot~ZOT}VxVH2EgB7A)8TLm>sKuT{?UJ$>kw z6TG1B>u+Qe>m2&tV)l*a^!>#f2}i-ZrCc^7nYMF}*_cW|bGZLTz^~z4_IGsnbe6TH z4LlxvH1KUV??2C3%O3(h_>JYCzJum@biUvn88^JfyVo1C?>0Bg6Yzjx=R?8U6j%8k zJ|fuH##Nx@NoX(PYlDvCw{j`IUyMiZ;5~e4K}lIo)=sp_9VtSQ;$j9^BC%-?v+e@^ z)D>$7#$$5^Z;8U`pJO8(c~rSS2)2ERj!ID(GKAS0@{vpfTaoyaIBc;BFVK;OmR!gP zr0~~liT@RMq2R5%T---O>WO|-^0y0%1#ka69q+SdoJ}(CFNAu$;HCOcSf@Pog8j2_ z(+R=Zn|BnFiP8bBpL**CUAr!~#rCrr@0B$#)Si%Ck1j8!yu0AXY+zp$fu4C9EkBjL zKgF8G)anC=f|@Db$8)iE(Em;^(cEl0+bP>B-TitVq`R znAn901H;I&z`B5o6z@)fo-*q{+t~C-aNdn8gX6D2Un=?fg104H=wZ%QClZit6NvYc z{$}B30bR)bMt?J*xqk#58$USs?VxSHZC=r+TUnJNe7Ix4V^r&hp3a)c_OjNrJ~U4j zHeNW6HjVMRvr}119SWA(#k4~(`zaTlfCBLN=;ly)rw7-HGl4V}UF*2N<=lRaVv&!@29`7~3KP z((Am7gPRAn6Q9`wxZEz5B_;$0m(C_wCW-wdZYjQs_`1Y02?fJTpyh{lqrh<-IQ3H4 zN9Kd55U76g<{DR|1SJI}hFprPOw36tNXkx=$@7achQ(;@Ur4xHScz6>sCfIJgDw=m z1wgYNn1)XRYrUY;6au=(k1V|^9S22h|SwX2URqygG`91M{3cW}0<{D7pCZWef z*5E>jNIWy*j3|VPjEag6ooAiMa&XPf&XeU!^Q@{E_SXi-XsG;=B^{y;SaKozgY1^K zge$Vf_oq*<_DlL6!P^@zTKE}>wf{N7+Aq{LK-267T6E(aq(%i3nQSKSa6{|iK>O8% z?t=F0!MO5{n<(}+Z}5cjU@pB|nRA0wX#gcI^k$p`T@Yo^YROI`v>uNlyVDD)IzQ5ICdtG zY&JRyuaA=08A|i;>Lk3oce0e8D%IbmteV(7SxWMDr>gFPwpge`plzTFwDYo{udIp! zXp#0=X$1)Uifn*=rNLqugQ3mMv;vJQKU^p*x?a?l+r-AtiwJRfdN~A|o5Aqj(%VTO z&DZo-dXR-^qC)!C0``qp^j8bmEQH0Lf=1fa*wcfa`nFgW&*(+L!@Rb$O6$oB^wm9e ztFn&vl`=tnR*$Tq6!lz8d+xDV>R{aF$yE0?J+8h(@fH+J>8l>mS?1aMQ>|N}yj2%8 z4AvNH3l0Wz0ewRDWpvKDoYUz}6*7h;Y|IG$VqyZl(+Y#Ms+GgaPsldRyNTcWYPW#8 z%UwJ68RDBmhZs6Gf?d(%emsdSjDSE0P>S7hC{&E9-e3R$W zs2oH`>GSK1*Bv;T8(!s;9jZ`_Jgxhg`ZJ2|-8Q0MVsyKTaUR+C{OA`TE@1P<<~Ff6 z)JKi(Trk60EHWpDOA5$b)HLpiwF2o>bxzz0c9)Hh`IyO|!vON}KGyEea%p7wXg33m)jNBaqGu z`dfX-{dFJ+d9av$V>3t!r*4~ z9bEI7YS<^I*`|_>)0PMe04}#4Lk0tueLwZx>311;J>Wd5^|-sEPSg#tTHjY=z5VSY z`$t*_c}-V3{{ax3Ond$-ZMDvOgAA+F*6H$r9sme$s$yWx{;i7fmhLdXf`80fhkCIh z27rGC#%Rzc?1MK!H6Qd{;oNfoab@dy|AKYCXwh_iOPyZ|K8)g``;ZmPf>J1(Tb_NB zm#>0r@y&dwf6V)nzBm6f0?&hMp{|h3dqv+{@EO6oNQe+{Y}5EDnuQPJEt7{xUYrK- zG~g`+bRmb(DwC;ON}Dl)SHbDT8_;1G42RCrIl$C78*PC+O!MRz+OK%o_h9st;tz%G z_^}?XpZhMgM|J17B@OUiR4897b?AEcR@-8?+E&J+^kj`yF|?JRqnliN&XzmXLbrRx z(fZRMur9^3=v@2!lj)8uf_TZd)fe1Sx-p&$8On*QeJPf0*-XqElf?I+GuRB}bfKcGAkIkq&^?6>Tw$w`?-eFDwI1}< zwIy~Iw95Oxzl}t9csIXPI!(%GL#5qKQspuwqIW4{CP)>9CTJvrAyk+Rd^Q_iEr3@$ zkPWSJSH7=wCsp;T(=5<_B;Br64YYT&wOe2r<_p#hb9pO{v;GGyWUHHO`RVau_N(XW zyT;xzNcsU&1&V|>256Jo0R3Bc+Vd~^eQFHt9qb+sg3R>X$lZYpqgp>fpb*qk(-Af( zuPz-g89`8Gd0Z(Hd^iPVlo=e=VpTp}Y*@51!+0cz2_NwM(S3sA>CY57p<;pFQtni{ z%0`_qyt#8l>ZIMNh~^F5=CBWxY0uuf8i0$gLtV~+&N0o|m1@DZAvE}@Q(Im3zNG+Q z*9G^Dt;q0|!$?P9)yGxcgRZ_{dp{e0XBcVyFHn3M$NBsX4yMj;NZzRm4tk*Ee zTYmEy!FdZVf~`~|91KKEY6J|nl0`T)4aU&P@k8K$Qb#J){^wp_TU}yzervX3{MS1Z zrV8Hp>HqL1@6-CW*vJ)s5|kd0;g;-=R>{!eGV~5qoUDP@zTB*2m)N4Of{AEy8+UG} zfZhttjG_yV=WS0jSClYOE#9|1A>{p8)&9l?sjDbz8^qe595B>Szht8+0WIcE7vR~l zB^2P_)J`Vdluu~C1NLiNOgdZ{{EHE4BzTJ14)zwdwrzb;Wy3ZwgJd9~W;0CFA2u1(~?_0hhjp3?%2DsCiE-cj@QMcBs;##43pJ9SkW zEqYVlT=dWRS~OkW{GYMfjDJc^pQ&RtsJpQBd=|S;No7D>@;MlBQeAgZWB2R+J$b;L zZqbWobrp~0_WFzu0PNV2*KnpMs42HG20F%u0s4M4#QXA-pV3NMbiik|`|%cUrlcqD zCn>-h>*aKwO}>fPp&qdCDxXdt=tB~v>bcO_W?+WSGy)WUJ!mu#A|nE_@I%Rfp+Q~T zpaocrxuT@D=)2(xsz=#Ii|m7Jf}N?gL=GSZQZQy0mivR<_W#5lzjt7TEHW3mXT?y_ z9zaD(W+PeHP}C`JwJl+N(Mqrr>1C-U)K0MA19;Y9;!iY-?#Hu?iTS7u-kHI^GXvh4 zVN9Sv!oBVaI4AE zf%_5_kl$Yp9opfpfi8SKz6Dv5h+ILdxFqI91r;RAp0;E2>NIV4T9H zD@hPqs|&3E`cJk5sLkR5>SHXJo5$r<06EVS^qN+EPOiM@{Srh}H-m`kqoONFiv`nN z*a7WD0H&W}DS6%I8Hcf8`chEinpuFF&rhIX$Z#EidmjiSGzi@M0d$-$+9Twd9EaY_ z$N%OP5P`6H1sivqXF(6P779^@I++lhYUJiNLjh*{zX$Qq*o)DE0W_;OsoZtYc(iul z)!?xatq1Lujo~dN4RKxQ^WjTq=RRNADj ze)mSyJfuq`=SePQ%Bc(bpaivWMgA|lb*p{?jR7%QZoi9rkf^y+^Gn6gR%MJ&mYf{zrGgY8PE*4gJXbZ;B@@KKJ>#V#ChpcFNEl=QG&Oy$N=KQ5TaSym{pw(I>tL` z(YYX7z~z8?^@{9@^a|yL=0m(^gXbOqmt5d031qKW7j!1f-$3R!+p1x?r9|!x z4c@0qIon{Ya}3YgO8{OuYx)%?(M5PQL;c>2=~tS_T#d0N4_{@?NoB8ZOL0?l&%i)? z4~p+MMl*T$+|V7^zQVIb0J2_VK~|_>!}djsFbTV-Mp>I!lr`vvAE2yrVD}(~i7{_u zQP!9*!T@D`OG=B3VE0T~-C1+=1k90S(v;X_*N{1p>6=?3 zFPB&FRZRnvJIK;ogZaCx3Uf-5 zlUI^0+t*}z)A|uW8&n6HeGv(>p~B2#X15PxCnBdit54EL4dYNc6w}^#mi>wg551lR zzRIp=R3~4uh38PPjId`b1n-^WIsoc0zHkrpPrsx`78()2&6-D#>$7jXphxtLi2uXZ zd&fm}ZT-Uo%9)diF^2J&L1z#JjV+c$O^h0`7eqio6p^l|^xk183L+?N=w;|tdapy9 zLKULX7?YS@Zjx&>$sT@tNZz#vyf@GLJpU-{PlCGVoW0jt-?sR`*a(b^Y85HcU*)Ie zS8BhQu!U(N94j7@y51-mqDN5B=*1DU5$^BRdNXe-!9CZt)xOZPsgdVbV%vPJfUF5f9}Y+^PiGkLUYaE z(8DW2=O+=s^P3}eDF09{20_3sEtqTMpj}$0<;;zv(R-rz(A$karG(prJ0guBf0HFd zkEk0SVCxVa4N~XOgrIoagbR1lgHk*a0$Xo(#+ThLOUzHrEfr;_mL~(((3E8;S6EPY zI&Kf&en4}Vvh)-R6Dk><)=CiFD!sLwYZhs^3oZzpPNhgv-J;_5#hJz0Le%Pv zGvkU`mRQ5=P>!$+d@E|&YKF>3Xf!*4pMop3s5}ii%MI69_&2bl<8R^u=x;3#mkkX+ zlb)6XNoo{Ews+-z{YAaE$fp%w$+=D78VeSHV%vPv6sL|n-t~IFVKSvn6yw+Z zm^|~$LkZTsFgxf7|A@?~-wa~XOmFXtwUVTeBkEx!98qtIn23`AyjsAJlb}qQa76Lp zz6^H`CiJg0RNh^9m+un#*UV2GJjmwWHARn1=0j?W3^g^;9&rRW#C%5}Cp)h1O}V|Q*Xwrc|yO5BDTc^*~f?Y32P{fq&B<35g?qvKU;cfUf{63@JgF1(VU za^C`f6Zc~Q$3VoC>yr|K4mYFyUqNz=?FJ5*3$)(rLu&@`8tGB|!4bBoAgwT^kp5mp zfoBL66zmrq0FfD)RB3iePGJr${*|#XTW5mbw95)F@J(PPF zK1Lht#9Ia29fnw|8}07EyIYK~z>pHOZe;# z%CPk})L5D$O|llx8`bkFtr*4lhM|zl^Mh$_^LI?gyVHRvc8EW@(q3 z#5@ql6-E_h#fZa2F%_eQzh?eun!yw94B6pn990-uB+RFk?zNY)#rAZoPqpFn1D$7Iwu;RRDIBVwAT_q#nS9dQ;F@TSAPMGh2hdZq<HLNwG#lkL|_9yywnA2fDJG!&I@S-qa{M1nsTpHu~gSH0e4k*QSU%~msOWu2e-l+ zsPEL!Yla$n6#1;cTz7LQ z-8rHQP~%X#NHk*rx7Cxk@t7$Zdk6o04B2B>_6KL)KW%=k$&@E)9PLz)p)xz-W?aN< zrNAyREkNV}jGkd=wu|^juW-&;T02H&N9ROm(-5`iJ8={Nj&&kQ_3bK#|7s0{`CO|Y zJOO=*|9&Xvy|0nQaHL#1P$4TADxA!d^;c3)b1yjB-up=~iC!Jy zo_HvpX_@I}?m9PH6}a`4^8zaEJFkKi>RS~Tq*B}s8||?E2KISm%ivsOg>u&|3Z3@X{NR{22RL>Z==10fAZp)`XGiQjpPqiZypr@RpVFsR|BS%70@qXYm!qAM!hjhLPLS z{K*{oz<3zJ!y9@-`s;f_<-d%9v*?!bh)M2)vxfYIQ5hpcDgV?~7(%Ydn}pwd zcoiWyD;t1lhIx1k;u&GyrsF@s*X*Op^hr!1-k0M$2&c;nTQO)0=A9I9rw0rzRj)B9 zYZcC-(C&pVP?kO$KSsM5)yc6olx07uJi%5Wm}&vNd&7Og^dip>n>;^g(Y-XqX9`C! zf3lT-Ew`6x|327LAKq8j6De){=ExXYGMY5iI>D`~lAu@AV67K`wGuuLJSNoxaYE^Z zp>7m3^Q2Z3!a!VSZXlIJWFqrmEw|{91oQq0*eWA{`GOhHC62VU43ziNNC-YQ*NII@ zb!PQG&=IV(9n_!N-Q0U>{Y~dq5Xt z;0az}h;xty#AQ%7-LGE?cBH`-%;Zl7y{dOu04@;?iDgjlz@QT?S#2gjrl9!atOgaL zMFmTB1l;2ZaJrsv0k+Fi{AKnWz1a!Qd+-1}&<8TH_6z2r{yzSjTR3C_xY&3MSt>He zOQb1dQE63j$y5mNfYH*z??#L#z7%ocOqnS_)=cw>uI)vZBUL=#zH?tVKvG-40dmL) zc`URjg}?YAhx>}YA^FTN0nw<`vf*5dJt!WjhUS{?(gFH9I>SWU2_3>N)A;CN$Re}f zXJ)d`E;C&0krq*vUtU;ROxvMKU<+y(4zjD!I8VG)&H zE2>Wg2c1flKXK|58w!yNEJxl1$T5y%lK^P9S7HD?+(gV&%HUF|TaHA?ZJe#Tk52*U zRd6olT5$$1Mwk;_0V#kwcl%qOR7@Su=LKExvhcKtst9ffsibk#%oeEXxu?~S*dwJ5 zf6FG0;}}{yb_tGT)5#y@&Jz!1{uA0?4Yl|9^j3An4Sgy<1x`rcj{j@&7!chanEo=) z2T(J&%7M9+ISL&c>=l&)T&CgF%9|8)R(gJ{aZPr;aTIqx@Gt5G1rD zG}kTX4jU#-s)`u27A1qnm+Fg|ZG6r)&PIY;)nx*$NKH^Uyg4gc3!K2k_8M>mLG)Q~l5p&@-QzHXsYn)dT3oyGQr*S)AD=I8H64Adr<>OTjtFBG277 z%Io|Ozu-6mKI5WehX2^2-a|)4~;nii)8++Pr7TMYcj#T!}Q( zR|Y5hrG;hDAKr*|_gZnup|J7tN=Z{kM@=8CdZtVx2hVyh(9mLKT;I~gh?C<4M z=Ehc6(u1=+lfmJ$Ohedyw^P~xm1?2_d9&-UbY9H!Y3bwn`q>4ZdrNjZS;1s^2Wy;`~_9eoY$5Ohw6FQl0We>!cyA6xA=nt9W(5XnvTPQ6N*ri z2(N$NeYAJdS@C4jM*7XfS-EwiPxa5^XMiO=4D?7zeNU7WMZ2{&A9!%`1Kxbbke>WP!dP*R652AYS z0N;Si>Sx(uje~k09#JK5vLi~tld7hc$GvYT@jx34u)|U0poiZfxe^=MVX?8HVb<}9 z=XKNkMMMAxS?Ox3Ls}saJU5{w4p5MfPaX#yt{%Iht-80c&8Cjr%0`0p!t|oFA|N!W zo-laHpAeulxezaT4!?6Z>|m)n(Fy+9{hT5-YWhIUrJNIx+NuTW^r0s3%D(pM=aPC+vtEkB@V6&Kmv>SOg)YyIBnQ0r_i=kbb&= zDZEg4GS#6gpBLTeGvW)(FYO=TvV-B&ne2l}7kWsc8S+sO6q?JznPlTTyUdsOrfBuY zei}2PJFe`4>L!3dS_TBts^?$bs-~|kyU&8>gSVQ3dZw?S%K^9)_!Tvg8(>Mi0Eo!T zU>5obUI0|&WvCq1G}UXGu%@|}0=B_!NMn7?2)}k|>m>cZ%EW1|m(rn%4m5YH^1DQ0lP7;0cc zZaZ{9zw4UAT9pW<}G8$=sf!^XKwuS9xJIJL6Gf|(k5a{S>!@)c)d~9Yv z^2W~Ws_gRI;#`{R{R_*!HWa$~5-L9zg5A(!^ycZmtT>VJUbkElAP)=d!s*VM_|*KB z;?$&!vgL?{Ejv+A9}bcrlU{5xb?op)c4}@)ZgQ@17BAS+-QtR4){Vl*tY~_#C5pGE z>Tx`S@+j|cw{VY08m0!t>4gvhPjA9t|MZk&$UkAqSsZ;22^6O$f0lYqD&$EMwd84E z01oLB$Iw~sXzk3gnSQ3&w$dciX{?GDTJJvO)ke>*o;{$xD-5^Jy$#|ca8sb)W{xuu zLtu{IJoav8aDj75NPbCPd2S`GdN?_)r0xNE8^WRWG!Pf+_;s2f?T3ntp8K|%;_vwl z27WWIF;Hy%<25=m{*6?kxB;OGA>$Hx_{b2P?1SySo_%+_BBe;@-uPtASj!0a+lP{w zPiJ}>f`zetwy?}2ExcS@Qd9&?hcNaBWdT3TCJDqV9*e6GhyTXUoK3MYp^k2ty5+mV zDkiydCGtkQ*FiE&oba!I3mg+tgVKB<#iPIWmZ*4TVR~|2)pAHK$zT_h=T+vwFmW?g zzjtW&fNgqCVR25TbB-)hC1c zXam4eP7^LjJB*k+WA78Tx@H<9H1Y?wdZ+gs7X(JkH6XLYH3P_34u)(lYfbS2GZRr* zwr_GsYe&mRHJ{M9c@UMd%N(jMWO>)N@j?nue(h09qw52BC;R$Mh)fE(l@O#0UQGcI zX;&`3m6p9SC+&77_@cN2B|;EW&?55&dO@zL`6a{FhgSv)9H^+#xqK$i4yOE!OFW3l^Cdlqik%GKU2RX^k z3hriKFAb^lFV{g^WxJ_d@aD+rw#eZ{T?jZ73^8QKO^`l?1JOffRy8uyiSVpFI}9kd zGnYB`iSQP<-VUUtO^L+M5m=8+aFVmR8=;Zkyk6axy**b)&y9)H%IKG+w3_Qn%IH=6 zH8UJ26js9{4&8e(O&}D)!9lOTU_RD2r10zTh!YMDdi^=`v4J5W4=R3>S&cnam?|ER z8i9Mu8kzRz{dHr0lH%T*1Bf>{{D50i3$pxeOcA-Q9XsJ4)Z{w?(`YW3R%m;IA4bmp zJ665m;?@=Xz#pE_rNNBoc+$=|a{xvX2W~u#7z0nS6h2+U0TGm#Kz~q8aq(p)E3-5U z?fIOJ%Z{xmSdN#OX+dRKQ&m+{QrC!&Ok)G|+5u6%xGXpGWR2C}<$Fz4olQ-3=lXbF z03%#>4Q>u=kL;w;g)!{@HQJ}Z`yQk7qhNW?)zn0q`yr8t0xwxRBV@M+8yO>`cd_3I zWW+WU8w@)F?Soti zgR?HHA)}u5;;$^>0O-)g=1x_h3FVt_QKjvr{rO#lv>>mKJQSpcXwL%X#X^tGSxSUsA0ua63oU`z1(O`&ZPuV@F1su&g(Tj zh0MIyu~mPgPSP;00Sd+NTubzihH=k#C&rK|uou6RTLI$fJf{6^Z~1_KXJK1JUthBW zT69W&3T?D+;RaQ*W-cmMG1Wc|SMq$yyLmyyPSq}jQ2SkFYCT%*^WCeudH&^gjaSNM zQ#rT~QaEq!i&zzK0t7tfhAkmlg;VKh;X^RtQw7hl?cU1}wS;5bGrH{;it6Ut3vZ?VN_O&N;LMMr6T zi7JF_I&^7FrFG(03MAwXILH+*m&fq(zA8psG2+q?sTYEGdt+OF%%gGXj4o4bl>c7R zdAdc=l6Rxa|1+Nv9d2tSYc1Nv{Iya3|2DF5F5W=yF+zacMrGa2ybEND5OC=DKkl)7 z<(nVEJtpqWYQI`RnzHp&!KYe>;SM9?c%<=y>DRZQum;>>WFU_;o-_Rh7S!TBKO2pr zV*s;!FNX{c4VmK@)OjVH){20(^3L#~#x`5@h8b`mN4n6mVJ>8VDYwn=$xiF}0l;o} zg4GdHG?jrC#y`}-DpdrZo5A7ptD#-hWAy?%>$qV((GEmOL2wTHW~9^_{g9oF*5sq; zG_;?_XQ>RbZ}wwYdE#d(3>*4lVE|qsxxo{CRO4OiSrDXC-RfOEDl0n7p!k69>w>bI7qNNW|r z!&5SgVK)U_Lgk}glER*t0raNq-u=d*hf;2Q^;0wsH<8#*3{tXo;8E834Z>r_jTZ?q z4OfwQ_Q5;WJtF8EgVh-QYj71ACGCo{=!0ji|N3VUBVe(kzKZ(cDgwGTTtzVHLyRe6 zt&;umi*)^`kB9d>{AGCkXC}K0%%1PtW%k)_HhAD|=f44z(aR^0CFQok_y}HY59wuI zkUz4%bNb`@-@8|Rz8A5bLzJl<;SdiqWzZWK2H}YQgb|*%Zo95b4Ndk*2&#&& zzLlR?n3$QIm0MVm@@*>0`3suz!>d`dTQ@=<97OJ@ofPO@8q(`&SD5mLY`%t5ly7eP>l?w#%ZkUf*uV;p)iMy|KGHe>*VXu%;}m-JH)d=N5LJMv&Wx`m z3(uIbwLnz4Psjo--Oz=W_MnvmWy47lcQBTEf4T>p1Ex^K1MY8o(6K4T<*N0?i(aGl zytb;gj@rBQ*C~wns`z56+XP_@__PNAV_;&djb_F4mD zpqb}cdA7|Sgjm#)SwC=yGY9_|tBc3%&3u8y<(C$6@9tw+FX%KDK@ut907kpUC%UXm z4HNl8W7}0pd`2OQG^27kVt!$VxCFa}`Sb!CtTewizdkcED709w}iT6^>Q(H_$^i<5wR5jGkATm327BsKUGvg7)avrkbuo8U1J&S5WDN zA{fJ*$N|yOF`wg(CofoC&TtVNq9RkmQ$mwl%i3$|?gITQ#;Jzfn?IjLjg)_|FQkxP z&T`DSlIn7q*AUqf)&NyTgMl)wj0v{$a&~dOQte*v-%R5v{1byJDfb}fpsVoF6;d_X zmAU1)bmdQsh2=(2COCtCHMHLF{9{|_xs={A-}<00nCO7OB?}rDqOVcuNIpF0j5=PrT*qQJ^(|A$H1Jv(JG(ePM4m`xtx7I`6_gp*Q-Oi zgX$p{7_h_h*>FY>i=pPw(U2Zk11*JFlHvK~y{TF~H}1zsbz$BmV2qUm3LN_tx-7?+ zr;rUA#WphZ9p?-7Cu#T7hqJ8)cxycIsUW;H20S{w!9AM$IO+Y7#ECl-$@GWV&lvfc zU<*8oEDmFfQ^*3cJC}j;7Fzbph(Y`hcMI?ZWCu(8$kIbt+P7Q4U2{UHI)Iu$ z_qSf%GfpXW^yY-f{N0~(GT|v#6prReVg6z&4UzwA%mVuKp`J#VD|Sao%SL^Rd`EM# zKbb%a#{=^N!9~Noo^P4UnU_is-W4DoYO31*70As6jtdE_w^V4;IkP z-#sA6tmKlhaSY;{%jQ508M^9nOj~euNo;jF$xs@>30Vo6JpvasY*n}l; z$41Q@40*{MOfyRaeRelt3EX@jgnnWX(P#V3F$YWMSpxbXu?gAbjWq*~(x`z7Kc$7g zK80+l)93fu9(2zN&-Y9XZxwfxwpM0^Wrn7QoV)Oz)p4K9uw37?5P8MJp8hUc6p-ql z9AIpSIeXV#$q6Gz%hUQ_CZ_JC9FH*f^QS|?!r}r}1o)c?tC0 z!sMdjmEA3Q2n0@VNJ@D{cu5ncCQL-R1tmEdM!gPy1|X_VTw7QvzR=FQcm=|;T>@); zYFsKx3W~%9FuzTapA+Oh$@?+=W2;m(-g=43lxjgrH^qd#uZ6Es;?K1J^Tv~&{DqGI z_@5^TfRPQ}a2B5*L-g2f zdG%n9G+$CBPZ;cxxsGf9*bkpmPibfDz?7nH@R{`aF!zDQQ)aFTpFMQ_low8^vRB?0CzLcA)BY8a&%xh zTB$S|SKTB+M)AF$@Mh#!3$c|Bkk8oCOR5ym?P3ZN>nP0@nH%-uW-I z1Nb=j0Z>bxy}cHK{C;GfzimL_?T`@nY%^K-fqA~!fa0z$VX30fg_q=t|7-6uqs5?W zPtD+A@EO?vN!lGs-X_?*LP90|@8%JLI-zYM_6zl#2(zJiCbUh&ey*MqE#C8+u>x_1 zZ^^&ucY+q z!u9^AY3|h%37wu=#p9wso-9WTRe!?yw@JZP@n;h8lmHj0jxy*N-hiQ6`g7s@SEfYm zauphtt8CSeDjQ;V1{icxF)$Fk2`LvZaEA{t2R^`7Vk8nWNf^0W{&T9L3myM`uc!;kjg1Ie~sAi(JGd*mZ?3g$t~ICq(dg%j~w z5CN!d1GZAxb0PpjU-=)-0ctug%2)EBVg`D0PUqEfVr>5wc`^^f<__wgp?;gI3fI8a z4jRC>^E?){@58+j57<@E*MHwr-7f4bYES4zAu{A5<^I~Pd;%rt<-LavAF{~;Dx^p_ z-TU#A*013VRTVRJSKF-%-5S8csluk+8A^@_XQts=$HhX|#zvlR`Gxk&Rdk**lR59# zx$~=?Pn~qq^YVes^Ip~VDZBf$>eTGJ;3tt!sLlXQ)CUX(K#BS9b6AT^=rWHa+JH_X zt}R?mYhnk&#wG&QxeF@HuDJWr^F(zu)wyeq)U%w)ZvnpQ+{995`~&KPWITI3g@AB`ED0%-PWe6$k!& zWcW-A43#2d;Sawy_lKu?L(*X{%wEBr9cHiUKmO)PAbf3Q8#?TFI_Cr0&Ka$jZXoyVB%=WpFn7w{a!Is{(>F6*LSPu+qB%E^q|r5D>s}%!*#Dm3*&?< zLgM{y`P>!-CwnIZ7sr=CS)G@dmz*J@OT`%jgUcs99-BPVt^Tv(MbXzb#k}Y^E!C4* z4WRiH0nMi_hZh@l<3?<3dPI(QQdo8t%&#_nByvynO7iP1z5uuy zfxNRxDdrzN#6gWpowwOVpq|O?aw;_uyv^riarS{b%#{9N3Q_Z{atii!%784)v&tXv zcejFt;A2Bn*2<*2rrRfZ5>KJhh^B}tS`y7H^1Pay+CshZ4UO+P9X@Y%!dh4qSQA)E z=OCBkf`USENluA#vAbY1C9XQ1>v8_fnbS@>iA@QO;3ag0&0PdI`Q8&8sES$qlMGBn zrXf_ggv0%9y3@ zmV+5zyztV-OFwxc%^+}$Sp1-^y`{CSy|cTmz5{aL+WrP7Y}TY@U;mKyuS2KhwL|Gr zhe_+6sG%Q*xvXRss%T|IE*bWBJjh;_MKr@+_C+ySZn3z&ERCq{ONLV<%mdeh`&d$oAFTz;vI~y<_Sl7<{V6&OH4KHK9vm*8)LT5W z1f&q}I^wsFtj`WDbV&^@tjw>?Z7jr^~_&m+WZ9jwj-zub=k9N_$lQ@~mlB5r&4beblWdIBc zBOj!e^G2tiJ<-A-^s3s2<;F;d^pX?=rBVy&=a61HSDW{N7ngJZRN?Ykw8KQkJ@?QM z3^(|TE_1}hg(Y&{hLPZ4Ai~N87qmMUdx(gTYg#;4oG+MJ$~Z@y@^_4;Rwfm;Pz_`4!pOdvXqxovgnv zl|HKb#U8+Ey$N(q$)Row@D3A-yENMH+2*%Q|80Qz%9W>7D>n$Z2Q^uU^>680-?JN1 z+C+DO|7Z$<(br5ao;%}c-g2?m74DW|r4hreG*_cr>-c7)9KzEw5ESu^bUAjTX!poj z#lul&>A2iS-ZgrI&_oCo?{06j9Q8~92~W+zyPOaAo0xF$3cv{#{F0zQG33q3LUKTe zrB-sQsf*kPs|ob!jnaVCxsH8#!@1oDO|rv_-9@1tEsfnZ1N6V}ZhX~9d1(|IquuDO z5nM@Slx38QU@Tldic_i7iqwi^=GOj6eqElX%j9krSM?cR$Aj=<$>F;^Z^Rxrnv z1>>NRW35wd#3vb4jx`_#@et4kv7hpNq3gJIo%|?Z@io#W6H7B!dl z6?W39`^pCX@nQ}!d0}?2)CM&I{_Zz8yJd(q+^4V~n1VOLrg%&dC$I0zlSPa`E8o@Q z(^u0KIb1xR2%_EJ2cAwH8^3dp`;k@d|Jlx4z9hzUsl8wqu<(*OYGd+0OcA)}78>AX z{H0%U&}R=omT?f@0%M$oQ|Q)VHWPEyn+_Fm$g@DCO zFi9{Il@KaI9zYixiP__R)p?R%zj6E<!j+T^*UBDph2)YN_A=tCkm=^}%L-M|O zOPEc@_gsHSjQyt3dFBAV0T~X{tkuVc7?tk?WX|2#!WRCe&rxq$u*eHGP0@IZY+8CM zfdbaD4`HME{|$BzoS7zJ_a?x(ZrkMC?(z4c5}=-xP>?ID&tUx>=n{$pBBX{P$lw?1 zfB#}`6LarDofqr~nooV=DMPGNi5r987F+z#oGXTT-=w}X{X-f zwL}(5e4@6)B>b@+0A0)G?MjxRBIfZbvhWA1H3!m2WAZQMolU)(l?+VCNFSk}+7wIJ^0esBj6dk{Ljrp*&(KI+QCN;^a!7W1 zsR+H@7M~qok&usKdM6!5BcR>C3+_E!Gfj|Z!+$Qo>y>9ASyNLF z{je7D;<;5oZ;Kd!=XaZ$dE2AZ*+jgN-g?PXcpnPsQGFw|jF1%6Y8m0E0amAq#J$2X+Y=bS)I`5xs_T>X{fd1GOWH`#R1{L?+TvNMqtfeO zxa+Kh^ONrlFVR^~g3d-jhBQHDH?d3F%lh-w6tEqF{71klbzvT@A#AVTnCUf#2>&aG zd9;=+JY%NU!f9YVj9wYCYt51e3@UnTrL9A^*1x^KLQ?IlG54^^&F97DnhE*)gf%(?C>|?iWyFdlg8Zu76uh#;Y zG@^{n3PONb@BUF4fIh4OVjl+G7 z6~{k1%;Abz2Dvd6{+YrKMcJdAN6-t5)u3!QP$L~<9YWcvW$feuEv|yK4$YuC3QJXK z40;9VYpjJRdxu~#Iyr2s=^x{q9L46T&cLFNtP7C%5laH#V1oZn4ooqU=NQkyli=RS zeT}yJpSEYnLRrd~4X;DTt=ZffRt2^K64qPPFrA}9?7XgA@w}LOA@^by_=>SAPNQ}P>7LvmKz1VxT6uDffV)uaiH|Ea!9&;r z%v8|veg;t%+Xctr++{@LFFo z@4-2hTv=+r)bHgTa(~H3(yKlB(lq(G-f!e~Q0H}*w%AL2+lyPGdmHc(4N>Y(-n!_ER;zE~#4R6)?avXSHMN zHnh17Z*Dtgi#OVmJDhdh&=%X-AORaaJ;jtoRD_j>`q(&MJ>!)bneUeprl@RbEbFZd zEDg#Jx!c9_&vPxf0&>BHdMDFsQyP-m=x-@(l900YhZVhThLoGBmcAe+FRUm$j|QL=@RVH2Bg{3-6$l^;71o^(HV4b|E~I zw2(pInVbb_V>ovG%TQ1ay(6U^UA(;Ka`#6{^D?nza zUqaxR%KQZ#lK#k$B4m!w(>nK|a-g z&tI7@O3JBOj*6bbe0U4q1-(a(MUr!oh!^7-r5?1G1*HpVuoF&0&S?Ge%PJ#fQehnXFE4e6u^d=hEo3tdFL?WWW(u5j* zKT88GRTWTSH)gkR9En`-8rbs<)ZWf0S}D#*D{Nk_EEt9VN31br1pKYpHF!1fFE}u(g{LA?rv0?1Xh7JW+Lqim+I#MA)=nYox?rfT;Ko4T zfH^wGt+H@M^>7yET+6+f;trzF=<<-Z;2O|x#v1|xO%yhXbDGsKf=21TL_OdWOdw2^ zAzE)vvFdFxKiVZ;!6C{T#<-Uqi;JRg+(x`2JE|Z^1hU^lEb(KcRf-OYkUc#G^obhb z`Vb#l2SA;Z0=<`+qH%)jFT(xc!KQBE6 zh0h>errN&7Ec+5bPN5A~2Css!;-JcoiNiZ0@VjT;&5DF@!_e~9ynzg$IlS6&&-3Yu z`_%2c)XKCRI^N#`;>o>X^69%0c|epNOB1~zg9FcSDSiVTc|=7!Mmt2icZcz^+Owy# z*=^9nql-{^IEkTd>jpoXyMBFsP`8PI1O!0J->!sLacS? z)EIF8OoMpCoTYyVhS@I|;wuZ2?RmA+lIP5Q17Hgt7#&r7CcmplRr`cEG`p8%P28Bg zCe!{5uKkF%l9s5R8;#dzwIRcMP{;}s;A+m_EH@!*;F83u&vNkjNXOur@41?cAq%y_ z^Pgv1V3K^PQB8gw4s4}Yd-30|xW|wLo64|fMsi3PepBu+!1+_Mh;Xg*pS?ajtP&s z8Icec@0M`&b_%f}397nT6JKz<@ODm0c4=C6N_kTETo_%0!iBm`d`RxYPd3!hYgRZ> ziIbTT1s*BkI+^0k!t|2NEx9|3_Fdh2@qnpSZg>Gm!F$@8WYza8j?|j9+lumv%5pO4 zvmFliTt9C_uNI@#S!fgda6?Vgx(QQ(NRm330*AkF_9AmckdF>)p;c6zVXRr4El`n_ zzRZ^t+_b@~y*K+m3Hv7U2YRtZ53-Tm7##1tEO+afmW1{=$$Mlz#Xf`DpSIg}=eFAQ z2Y2SQ#`Pkt{tH8hJz)OARN6jJFqGWG9bQ^v2<|zXm<*4sOG(~v#$7KCs|y1=6rrXe z3$r|#tc#ha5}jHzd2x;5vJl8O0YYBNnLCGKt)XqvV6i%(^fQEvzJ!jrkD>->?i$`g zL#&y>L>mRa6F{)S{Gsgc(?F>|s64?y_>%_SOPyZ`Uo3zxl!oLh7JS7$a7d4(+Vv3= zdReW~*#s&b;Lz65QDmg>t^=ly?Er)EfYk78XbrzaBBjnDRiL1>wFIm@Y3}Jm6|8IJ z^IT@gCL>B@2c{ptVnIDauP}r7l`~L5*1-y!tgvB)eTG8*N&&>P3*kxH^Zu#Neb`rS zIo-4evvqi4-Z}LY_IZ&f<{jLa6$uD%#PE~qUvqz{yjW+F?bg=L3ohIL!l&v^6OX$t zX%Xv3`8!l%^R1@Z<;9=K_d>KbVtX;FdJuR(nqzw;wc8c`<%@RdW=0qJq=H6B%mTA5 z?stud`_uA_cI?kSEe`I|ZKg(2YXH_*8G0MeW-uF};$j2*S6rpSVv>qiA}{!HOIU`F zl=VP^GnE4ud-x1Mp60S^<-e)N*dX%rgW#q(`DapvG+u(V72Hx#)=r|8Ou2FOuB@~D zVqR#k%cmY7%-aL}OJnf|DQAbWcu{?Rj{_7CfVZB7cR`kw>6#~!4H#=}5&*aZ-X4n}gI>K1z9HIp;U)q1+PWMQ~dnT=@&($W;?i{wF(C^B?Tg1R$UgcB&R(r>eUjZ(us)eoOG& z*col6_EgQTbW3S5FQV`2EFvu?UqkhybSlOA@hNEs4k` zR8o*c%vU~+%qWabC>VV$VB~ngDGIY-Pe!qZdQAh?G)__6cnt|ERWSIC6XW@dUhr3s;5md=5Rb9? z0gL{0`S(Mhv;VHUP!iIf*&5&5EBj*@F7BN}XZpEIYgot+W|UA(AH8kc3JSos@CooE z)SrXdZ@$cJfKp8VKRqdbEsWCFh?!i69P^Yb^?=2BMtMc}WW|&Qrbo~tXsHMic=eV8 zT{?USUd-?+pBA4=D?eV2Po7&Q%$c(A@6_5jywnY|bYp6Rdx9Hj6r;Qbxs!jr5bU-1 zG>*T2FQ@MZ!3f3G(_;0TAZ<2!cNx~+ZT^7mf#0&ko6Yv6N96>89}}Do!e)8n&lP{r zsyF+UaMfehU4vTFw9MSxbm*H_Yp|?7wk8(-E9ox=wt}Uc>SGDR9W`amvnjTbepSgk zpl(&O31b4zKrnQhYgLK!$BP%tfWA4U4>baD{&?}6889%1<74-Tt9)w6S6VvmIut7J z9U=KZo#o9AeZeh7EwMe5gWnBK%N{DYl}1mM$H50C__+ItOV(M@Wx=Azrk=YG3m(uI zhB0||d1k4X8-UDPAE+ii2o1=KLXtER|B%J*eEoe79jMb$a3auWtdu=%Gx4 ziNLfL)`-Qr&iGZ`&FUYT?G`3#p@DByh~{e}MUN&BSTIlJs;0m8WH*oeHSMn$IvoWl>GTLobZid4XwRX=)pm93_v~?BzrH1$-(|v09s`+=p5xpnx0EKS}-mf|2W=qE^GF zGGYMVK4;_xr$DP9`*!fUEJt!)J}?w5O&)BPhxG^LjY&_-Vids0FKMyw3v3s+-jvix zKO0Iy>+k}3O3L(bnS%S^^hLe7WE1-WQ1g>!rCzNQ;|ewOSm3Y)&cX1Bz&#GxCS*D` z|B1w2fNPv3WRVFD>n4l!GUWtr@JRJ%qE0!<`(6Z{3k?HEV&xeQd8+@Z<2doAEJ8F3 zdKLRPZ8ifSKp~7Leh|^EoPy0c-&ooIIbS@^J4j z(a=*`-~a}Fco^_2^!mba^x<(f4neOoDk})PKp%d{R{Q%zfxpjHXnNDUAf3CbIzc2} zS&*Kb3lPWaY#adD;z}_m&f|t_j2r!~>3zy~kMoC?XEUM-0@L9BWx<=*+Im-70xTvC zQmODelVnAr;#u~eFV%wMlc zXU0Eehwl$s6YxIW@lMJC)8#I)SE7WvH?BwDjEbj)5w{XUR*C|Xyb^-5<8z^B%f6j; zN0d&B^HVzqmXAe#V*Rad(f1{Yo%fYPDd}Ps+sl6#s*;1e6XJ~=!Ss>meB^$;A$02(N5}j-=Lqzz=5f`_qJTuiw!-b1Dk3Q~IZh zqJyX?=I$wW8WZ(j6-6#lQK0_^63!j(ntW}@zNv5WGdtY)%H9voWk-rZ*&=VLA1)fK z@~m>r^Ka?|LqWT`%N0q5yf26gjYPILk^S+c?C6(lbYf*S^H2qv|AV{px>iS0Z&X;WNeS1AT zF$<-@=w>_$DAdBQ(CBuy>NU6+RV&A9(WuL$lFy!9tfXJ+Ft)tBK>x4Hle-D5{nVPm zJ^#Hx;hbFiQj0;}NTS6ck0mBr4DwDAErxocX9y-qU@2H54H+4fTaTp3GDj@`*-D|U zau_c-4s#covjFV;_&pS zyspCgSx}rEI`ZGHE2=)KCafW>N(YG~Owq-%lbM%*5aDOzX5-|*KkyaD>>_*3b%4L| zGTpFRPni$6Y_E<4_OS9t=5649q9wampG3JM>pw+{NAThw+1N;x%owq`g5=ecVCigL$>^yp+BUEEk&=$xhPcG+sb4) zXM?x@0I-58gWE%E>5ror@s(n$3^z#uFQU@B1HkWxLExR#a4ydU48H=aoVuL>4__b4 z1lhaUxp?)v^XjTA`to2}pqQy-!mfIr4{#X@q~_@E7D4 z(0Tu6E*{wU-n*8fsI0&gVP1M(MqVb3FDSR5BtV-AN(w4+%C8iK3bqI;8ep)D8(}PO zM#l07h5^Pf+abUQ;GIM4GwQWLSUa;ra8{{eV$Mcdh1t^B+6tYZBE2Jg!u)8w&l2${ zd#kgTj=5)s7x<(^e96)V97H{46=(r8~Fm{DdFrWdD` z0ROKWaPiDmQD;go&5e6HyHT~DIVNZ0*JJSJ6M%?i-Y5wZMbQ&TO9TUSW;t4eKc~WP z#72ZW#V20WO$!#e!=WgzOGqao=~VIE+B}9;BdzED-jnW9CZ$oSIY|BX7Fvi z2HjSdT%JP@q@F|f%Jz=&y^B>+up@Tj$8 zGs&01VmkiL9f)IT)52?q(L3xeQLNlHN=v!DVQd#Ww}m`kq0ul~UX@bo=qH5Sbya{Q=o@hDiUgT1 zUf{98?T6rhM}9{qqG^~k=(u8|lbD?uDfUZ?AVY&Rapt@EZ&z=3HFPktIF}V!;*%E9 z)=}4AA;4vu%!2x5-xYN-@&QBG#`9ScWy@_yCnXkui}frBzZA zFbV{m`OvH_#GLn~dD}nOuW4a(=519<-aBlun*BNlJG}MM#Z3+AYQ9i&B(Wx3nrcW_ za~{(Da1ePY&M1D7x+$g!1@_0@+V+Uf8gS$5mRWUsw>JKNbiD_76Ia?ctPngJMB)&O zAu^URArK&eP(n#4!F1EH!I)+n8{B*ElH8lzZAq3~Io~n1;wDNe z$2#X?s$Fv)FQVFyu@%R!IZ>G!$D$UX%e4ee2LlWF^}j=dVLAOBF!w1W7e(Pu{MW2K z$JEd%g6^`a24#1JYn4NecTE$|r`WQ=wk%VXGMV@}b5<*n<%%eLAaLzF=cVBgLL%%c zt*FcEY`ocQUf^8c#B-}Q?YrI}S0zs+d;z}8YPWyuaqzh_=m_C~Stg%gJ@ZUKwr5H( z+A=WqeF@TKI`?JhLs&;|3ycE;$?%BqxFEgYoAJq3;7nEgC>Z6blBeV@-$P?Sa6+M%2MP~|0jbHN#sG=B5SOT6$ zX6G5tqb6GSJuolM>&a@jJHmO7fN}08P)Tasf`M+%@H;hhbB$Xt(9Icsr?z3&KYDta z{YRt?UCq`*lFk>T{(XJhk@J7CHj7x`bEnE1cJ9@A)>GCW(2?6J?i(5D>@S`;H4@ox z+x`8(wMp>J>gN8qtF@PIr#DhK5F)_?!KMi(QVLtel@v!{J4b+GC$JWz-EUs*#e^$l zbIDF7TD4~Kq8kO{0iFCKpja_S0o1QofO5a7i~9b`X5#W@>fOFyiEdM>?|Z&c4Tt+m z@*R zw)v#G5-SYe44<0A3U#q5j7=?pe8MJ6Y4=mok{ zg`)x6=))IAk#oP1{uszW(pl&6cJ*3HY?gQ}*&OtXkBEx&^7KOK)dt=*?=61&g{i_c zzeHhqQdM$g3V9qaHAPDy-AX$s%>)q&tq))1aYVN9Vk-mi81PC68i=bE7i5qH=_y&& zD}QGHu88SbFVMQj22bYK&~hT}wA55#VeM${c8!11nG>Mr<*jTCFlcJ1ZY%6Imh*xf zyq(?M!YacXB3nsxq>tcITbJRJM09IRXU|G?Bv|Ua^tF7KQ1IVb17U(Msp3$lGn*oJ z<8~3aTDC7d)R%}q6mP&eT95^3qSe5YgLxS+I9yWy_z#mj--?bRKsWLQ|F99NyX0aji$y+}AT!W7l2PXA03TTbgS7 zphrKy47oGson`_K=N+I7%)nnq_~I!$f9hqRBn(dNz5%;MM9z$v0$NKQk_8*86<&1tAX#Gw_N!}`ivIJQ(~Q?qC?26y>n{*z z7OUPiA;gnfXfv|lzx^S+_Xk-7(x;86^N-o09d7%NS;|BTm}EcdFMBRSEb{_9n*^8BsU{-9#a5FdqMuf1(PAp^Uv8!;;JbGVy;T zA}ux)f$sniLDAR@<-7LAwo@jVq9PxOsJ;%I4hL#HYMpbv8@hR3h33`PML?!2Nhu*` zKmvaCgAX>YJ}D0^@RkVj<@t)dEYceFLYzP^yUeG|uOu)fM_wjVeB7|{&bgVK3LaMr z9_kTDA!@B}YOZc?uhsj*B;_tu8xi}z@b+!*R$Bo3cwf9*MmA00SI5nzT zV}?cjI`Gg2y*rEAqf~b%&Wt1NarEZYsVQ!Aa}&LJuAaI`7x%N()AO~I3H{vwTir*m zr%dSv@L^^pb70G8rEn~L7R9o;YgyZXx35p(6#61cVXNaRXm%||lA%$Gfh0q-Yq<_R87df!8*Ux49Q$%)Xgmml7KUm@e8IpkX5_(e)!^4d z4Wrx#X1zaX_>SHmx|xDPy;!Au2D4n`k^@~5lbq23eC;}~1s{K4t!pBqvmv8wAr^9m zAwwQ|0t@i(^h~%Bz$}dfUki8oDphV%X_jfL}#X{9LV$S4D&l! zy^Ov2Z%3!)m_lTh!%t18=SIu(N zZ_ojtz||0PH$zPcCYlR1rO#&Th0HfiO$jIJl66p24uK?4!+g$%qHnFW~%O#N8vHv|J^U@;xRq!*>Qn0X}e zdW(b?-5zqAG4fjsuju&%XedF4nAj2X01PYs9~POe#UKV%<|4On#p=r@J*g>^yo(DW~-i0et=%NGl37WOX`bS z3OX9Bo39nwR1WgoYArh~8*|!uT$A^JEeP3W1FKDlKmMdtt1rL=AdebY1AqJ{rCM`= z;Kr~P(M4!Eb@IMi$Od_LIIxI(;yuNw!65PrBI^sq8QBKez#=MNNegEt*xGJGUksIv z*bhD&v~C<33LN=i`0~JImCo~_{1LC-ijI)s%2DwcN}qI?@EhlTZh9X5zyOq%x#!bv zK|DrytvA?;!fwovHNC)lLjjMv10Hp2&>dgch(0CHV`?EIBj##P3WFa!5W$daSvuoP z$K-JNKt_i0+e|BC~=N!h-YKd>t?QL=)F2707cL^X+c>twZd*hJ5H+tczXrcj!yX z(jnWK{O~0OiqTJMXx~~dozWQmB%`C$9_{&Z)V_bV+jgjS;$pvev)=LYf^QYmma-$LLxcGoJ8|N5OtDcZLeuXS3n* z8JK7XNkv6vdG$F>BvA2eaxK!G`=I(O2(Ab!C!Y;p)t8iKl%}?{mv#Yyp#F7$uK5{1(cu0N z!K#I2Zrg^9MB?oq8^Fq4pzIGeG7Q&Oc${b~mc!>iKlG~0kfe#|oi-l?%_RXMR zLHH`>yQB*&NcLLDl59b6-4FCfT=-v6;9u8`ub+^9MUh>}w0UsOtg)I~w2Ls(e zssC~dg~!1_H`-`%fB9%sr~h!ykW<$<1NZlP6n!;nH+*?e=bQGT9^a;-hN$-9w(c3D zF=ODPeRRFOy$|tK+<}A>y>q^_9#~Nj+`=$X?+(moUtNs&AMVvhYWVlgz53s%wWvz- zhBMi=y@fpg8mkc}ICD0v@Va*E$}L-YL_vTgthS+fxTKeyKk|SIJ>$L2!?YE^S2(dG zP(iGhYnaGr2wUP4QP!*_N zfzk8!Z-jbQ|6ijpQ&_!$^)_Oi26hPR3}US#E}f;|+piN1;vL2y3PwAqQM>~zlicWV z3RF<6UN6HN@1EW-kH`~BL^(w~wIhmyiZKQx^p4xvCGF)yc|Ep}JQ1U{fhfAIeFMN5a$qw!h30Zv=`Q@Xc?&%Vd^7lde0q{OE|Cjd()%1dC1&mWOdE7VOQ!>458U5C7w({=- z7ajzNUNi?T$q+y_>TxKk8G<%g|7S0#$DyQVoB|;ArKPdC*`uhX4e2)BY-#`oLPL{h zVG9tm7Y$tRZ|sT~yfW})&}8WDfZ1SASIEG1_|uJH?sLvs7Ba4(Bu-N61u<`tpiuXj6RcKRsDbop}z=JDXyWHzY{5*^j+W6e^^y+fw zm6R$wW$-vh=o3_?d!}8g7bDTm3#t|rk%Qq>mYdQx-4h%u!}5J={fkKq0~&#YmxHHo z2SB^UdCkDBqPt@#nVZ5k#S?5A!}Eh{gNvCTt8^;7A#v@@=ZT6untaMh{7o33z5ez- zzHkwRB{|J7^;W-yKcs#kL_vaJfq-1Sp#PPVAq-vC#L4*h2_tJ=X;@BRdT0TR!}lSK z96_y^qs%T)zU;i82GhN&n-bb{2r7i z&u%auyc7>V05vhN-i1NI%un5~ugJ^<;V~emo2Zn46yIb&k}G48hK?r3PoA)rh5=ti zR0*k02Wilt{B&{D5Pk_gM#l`lgf09MFvd}q$B3izMb)BW-IMS;{A9V2KwDa-Csw-%$b5c-dUlifpn-aFa~FZekj{%< zeYig6C%mt(QB{NC;iA>cME~()vVeoZT-P4Pp=Z&IjtEjyxSAiP$hU(ZhPB|jv_8O* z)c4k#3G{v=^@BCITfUE8r@p_*e9%DsaB~x(ePIge58DnQ)7M7ndYffe!vylNw+-uVcG+zzS1 zk8gkR^C((0y7u|%FOP$}4dg0pomhuRPN|QxKP>V2ZdESgf_D9DWh2^{ zZs3>f!X#2+Dmud3((P^V{WT9ytudepHh0T1bd-q1%fhdT1Y~$*cwDfaz&1YVmO(~9 zs)yLWDW*}Jmrx)Ej;12FFl{mw9WLUii;)!-aLdOU+~eQX&#zI|W!I7dV5GTiH^WQz zeW(a42#|(A_BGe67r#Ic>EoN(mUlAE`hG4iw8>}Gw}YJDINuI8{GZuqT>)BE zs2eke-}S2)#i4byJp)9e=XRwueriMD(q=fQuyj2$h&yiond+y;v4CkljX z_j3j;gKu?-;Mm8SD7RKjENJ`uO|&x1^kR2#Gox=*)0;Z{rnAMg#o(S-b*97}{`mZ{y{f z&z8ey%fY2$AO4L}WERTMsk_mc(S@;Dc)JN42t3q)c8=kl=o))8Ycj8=n4JFte81Ltj3f)mQ)(^s~gSSMGG@fgvq8&g41JB_K_@fX}9+4iAUL@Xj2I8i|+(SJ? zo+Ms>3KU+pr>XM^;P$y!GVd1{_CCNo!5pwpX5FYF{H(F{ooZ|c_Gtow2CLF zvhQ;OxKB3U#<%;4Lr2`F&}Vz_X9O}>{5hLzy3~+w^fiYY`C2d9_cmUk{$ipVeP%vM z-^c6zg+7_yqhHZj(^uAUsDS5n#qzMd1!)Xk5&xQco6TJwZ^%ErmZQE*F99jYuPpva zcjo%OqK|KfK!eg=W&14w2O_!Q`o6*|Am&~luI~|E0Wt3SaD89l( z(E|q<9~a7{r}NLX-RgxjzajNvLaJOyYiJw91%#|Vqba@RWIEVEi!4H{$zw!yNl=kP zwM&sM`YDbI@Nj}EpbrYO{Ic>Q;HNI##G1cgM*W8OtRs#s{tX{^he$wdExOH8j$Gr{zHJnJG)~emUHHVanpm0i;!l^z+b1 z|LOg5R06x`|LI+$e5d67+K(*upFg$lD%r)>4jcIg^k!eSMGT}O;(+J;lT3Q2A)7iKZNKl80jz@`8iK=DDU@$IFt-H?ln4=!s&o(^ zR7nZ=Jg~Q6{1b0D1jO!-FjzgujT~fO3O#w*bQQXV;-IU$M8w6$#)XG_#YgK2V-sWs z+J^Avwl}vmx3xF4wkd{@)~F6TbGLT_bv$smTXOFp(!M^;ou5H_zNS2HT3x^9F=EH- zEUj%XZYD>PDVd$*n%H>)x}7pnM{pJSV=R^Jlneask!)UAsecQUxW9x@`JMD6SF zo}j?1$}VB2mvgA|0tk)0O!sJmQ{-+^6A8U}$HsXXb z@ZKuY`XOXYU0r;RUV%6(IWtq4o7o{l#2k+^&uJ#O zTO;Q7w9W5`?CvozVjRz^we4x?nws#OLc8uAy36ev5Pb`-$HEJ~ zyM4`iy?Hr(IbX^K$S(c-OzK5}eyVBmVXKv2>i48-aaWhJ;_4%Bhxd{d>6Fw$YLaNn z_#RaX1_aGyNg5?{l$)ozw<>rMML{hgl^}PVTR}y-3+x2WBsaAmgcj;-^fd@BwhGP! zYgiSGrLD6#5#rhdQ`{5$s$;9f+2Z^Jd8#}sIXNjUDI;4@;FA%ImO}N4xJBr#8vM}^ ze(U!`q%^m%G)st+tZ0=xJRB;Gb;A0?RTzQzKDJl;-LX_)=c)h$i{b2@r z4?6vChZz1RU=LaYtU=U)ny?|e;h`4ZjrkM`JdFeCDh>)f$=3d40ELW>3=gPsZu@pm zsdQkndvx|FIy^C(JA{O{!)|l`ETxyC4=K@M&%@?ceNMdYw!F_}pF$zOtaU}!rHrF- zmP1lri*PoecU6BZ!(Rh~;yy4ahLM$LGdU1Pl{%D}%bdCjc!DzLHW#>IYvxu{k)A?( zApk?z{enJZ_aNc!p(k9dq~Ax!so=A2JDo0%c=9Ux3cgYR6PI_R zIfBYCOFN!m(U-=HZ3&$c^^ikih6@Rti!K@5AYX=Mu1cl~9E&Yvu3%~_EOBf0tRhS4 z6>sZvuPSj9TtE{*e@zppiBPiuJ~VRda6oB_^GSa^6`pk9!bILX2wBPhiy zA+SBBjk#M1IVo~Qafysi{mfCH2D#(b82YkGeR+uBURX`Pg4#gUs3^!Pk(cZN&5TE& z4McaVi`Bza&i?N!&>Kx?NuzGqI4142V5~~7MNp1CJdF72J#6^<=L0SUTZu^RPx_H| zZ&zZAgcpu(%evzo-G0zlfEX_ja|DB;Ca1v9on=n49cx}-MfWzz=jz?dDN z4TyObD(5dv=b&XMpB?2N;S=FU;)7}{g8SqM-AqItj&KSMB_kpt;)Hr4pZEk1gVIbv ziE{(g8`phH-O8+GZn{YcDZxqp;$YqSSlE~n@|cZDo|Bu=k%owQKaRSs3+~t<`cbd? z5qz~`8SQ}5a2t`4Em3CV!A2Q_Zlis~{O*es{iCt^M?(9PZf1?--goPsfV<#-``BG% zGUMNVeQc)f`HX1#_?O&22k1-4j0!sCy2HV&-+@;(So~eqBuo*O)kjmQCMkyEtDPyp zbtsMNjAXO~-3e5th1?|BrcKI=tO*(jZXx?(D7j;nWvXj4kQ_<^nnA#;ZkeMfY{PcP z2Y`yOgawSkaD2iRTjL#gU$&^kGn3g@bo$ZyLbSH-aP=oSmj|19Zc`>m>&nxlPkCtD zeRu)zmJ$NpVskG+SBA6bJ`VTEA^Hu7aorkn+CP8{3kw5gSfHgi>AFFBP^yv4uo+QR29;rvY#eLX0dtj_$idJSvMrj*2Cu3lN0>iGR0MSS)i73n1#X2l5_jM> z0rFGlQAEyPp+(e*r#O_2_lDwA8}WOq&zW%X^yz z@(}f=PJ;XT1{S2T$6`xDq6C#_!NGK1v~QS8xI4KIucJ`uCGdj!ad96iHDzn>A2ypr zK|_Noze#ET1atH~8_#KPYMvRlnKGI=h7Lj$!b*_LsknZ1y-y+iGVFr?Uf-)-Fk3c!>R~Vk^bjKrpP0VHG4~!j4r?BK=f`ZommPIS5X15iB&V!qf1j z)|)I4us6XlMOu(qA}u)#&)P#^BXl8A=llefB`cFc1~~+(#FQyl;!PG>TqBT){RYl$ zI9u6<&wQshoY#WhOE(Be@y-m+gfyYpaI-)?b6YTm5(F5a4=2y-M?H!8LHtXPv{TlY zX*SH;?SwZt;blH?^GA5MJOcw^bOOJHG)U^44D&Y<#wwn4E(IlT6w#8}me!J<@Im6X zNvFv|ZMa zd1Zw6i8JQ8V74DAoPUqF;o=qm@iFk5OM|nW)4+zLY{U@6y;2Ii^uvkTl={@VH1QVk zj>J7A{W`stzuk`mPnKLgO6#c9I>T%(n-uZ6XF2p)&QQPWY!Pgr?z1{*#q7{={b;V7I*Pu7N!a#zimzS5!Q(+x;Q44-R9>t?caxrZMaOLG&{WJg_fMJ* zK}V_3V}5%au7VVxx;N*mj5}nFh?2lt6=w-!2ysjJG@SnJNeo@UIN26lbR!m?Un}$c z3gP)Rf^}M{XEQuM%mwZftZlmx09*{z`2_dZBlL3SB?>(o9K^76V}tcV?BbJdzVPG( zHpVu@=Oz@26_AgRUzBlQip=+N;I`5RDzyO|_A87u8}VTxvmmorRstEgz~V-!Ct-wv zIZzv=f)C#k(Yy#1QpaZ@pkcxv7i`7*>>&3X=)#6yrXcExny{D*YiRu{Th6l zn*ULWmVh|&W{#8oej?{HeHUiEtjCVE$X zJm<|TH7-4xcN+V;@H&2JLpl)326n)+>K#YXYQhCCzk-(s*#}q$y9P$5`AO}Pg2V~D z{EX^Uq^M``_oRT~LSBINfJa_`{@3y$T_gnx9QXBPBOEowMp3ZaMNPn~&;20=k1&-P z{R*3ZLx)ozQv(GB1LS6A6&6eHOJ8s~_%9s5;c}Lt5jYdC^VyF%+6Vj1#{c?jP6RO) z`b_T8FI`^ka+tIc$hY84+IC>kMxy`*=#(E6hOQ^BmZl5b%zC2}b)|V1k zsjE|1g%T>=B-PN8*}NOW!D>afJRLS~#o1I_(2zz}2M$BsK)(UT+c`IKP13DN>~#bY z_(KA(4aTQ2WF)Q178VA`z-?&lA`t+X@06kaDd>F?fIjY#P!WIMSq@G|TT%CIXl64K z!y3INyt;JNyXN^+u6_P>scSXRaO7)Q;pf6#LxHjg%TuT)a2F>!^R?KVin#LlYyh^Q z*O3>N$nIyN8?GGu#Sr>kg@1=_UB{jU9!*X76w&l>pDaStMd^ZPQZ~U|c8i4`5mBbm zR?!Z^67Obzc*tgSD8+ph{!Ykg9Ky6PA<)A7`x$4LKFXn`Qzh-_y~((VlaDs*qg``7 zlqc4;z;B)XNFgkC&k#&}4@vcIKA@^?~uI*2{=K^v~ltx)+bg`Lz7h2qb-l;@<-NQ;3yzxBV zHWpg#FPmxtvB7cyH%t>*EI46E3Mo^q>Urm!LH3|E|auI5Q#dwSWkB1Uz z=|PBWcLzDBGpW5#g`$Q>ROPcb`lq`6??|DXrDh=A0d1Sk?HFk3pY)kB`kMR0Q@Rv( zy|AOO>s{+{;nnnJf0c0$x`!Zto}a_u8M_?Fa19@zWES3m^w^v zxx>aeKvB-KEHshWPUZ82HJ&OjAV0pcjNT=tBHTl4gwSb!wFZMm13iU}aZBjgkRWJ< zBI2OYc6r%ogM6VXwgwvQyo9V2Syn-b9i45}yPW^6X!TAnA>d!zL z(>z_vaDowQXt_s+7pU!*u;w??MtpJ$hih@3|N3JN+FmvHEr;HFnli%Ouw!+@j@3PZ zx(W57myM(mZ3KRQk? z(17&cv1HS#RGw%s`l$tvkJ6n4FjT-n@1y;v|` zM9FPsR}vfn?0Utu?QSO~bs_hdg zznhMxcFrSCyqcQwK4lAOO}|e;nW|n&)9g?qk+X22!xZQVT*OI$?1UuyibHB+s-eoy z6U&q2GG&guCG!{cZhiH?>k00v^tm@$=ig|gqhY>TMdURckw*oG`iA-IqAILS-Bf?l zz@|R|Z0nOc8k|D+w>^A(y}iB-?p#xSOIi1KU89hDYkFsV9G$)M!`*)HW94@Lh~ArP zrY_H2?gpzWxmq3D^vANxA z=OeHurPi6_@VP9Ymx3eTQV#x(kY&qqrTP0(d6E7C50N*Cp5iz8tKQ+DHG?HxXi0lx zdu?lDLqn?$I&u%$-A7jUvL77yh1=7M-lh$x5G#+%4zB%Rc2a!Qkfos973{MqMM~Xe_yoyM))E}lLa38!nOL&5Ow+at{Np11#)N!?e zF?z_}avbZM;5XeAA-NusK=f8MS~vUCV^CP$->3iMN9f(7Xr%*R%YlQJ7h!$XR`5sr zYEZpy8Z@``9V#jy%rgvRlm2Qh!Sz@@w~h!qzOZDhIYPJiL>CBcO7Rv4RmE1tX94)2 zNQU*2D{GPc@gk5jzrK;+>Mol*&^~{lmf*f&NRJ{r{FEr}*TU)RDOz|_EhP3VZls0Q z0N?ean4!VJf&M87Wz|6+PC-2EFa1_cRx_m?BduLiVbjOI;)+Cc3~V*whW;PhfkvKv z8G>?qvc66KOt+TVtJ9exHH`N;alaxsYU)>e2)Om}S1L zzJNN|qgqPcJxO=7abz5YH?c7(#BX7(wW}1OLQhE$iQYtz!uCE|xe|TArVZ&=sYnm- z2XO(Yc+F~c9#Zr7gm5s1J$UQe^y`D_*8#`luSLDEn-~*KpKIaGOr08X3Wv*3jq?ECf1XYy{|!WjnlWt?Mm zM+aS*0VK1gv%jrlDr)A~mt6cS9Sip(_`KUrH`Bp|!e22&Rz}Ykayp^LWNw6R9e?qk z90q!+(==LjX;hBA@>-g6GeDc*^#z*Xd6-vIsmIWZ$%rW}`YqZCHd+B}v;x>@1yDRC zPapvs55i_!2&i2Q{VFK!J`iXc;e~VCUYrPHw!k#cfRzn^4?r0Yc?@zmw`KRh^U;K( zeg(GJtr7IHLG?1YQOlQsD?%H-Ow>I(AOpbJR_Lq?B29H;8Fd)#WLx9+PT)1WKaexy z*N~>p`r)b>AnnZ1TiC7pJ2vH>>uu%vj9&cX%4fG+dA)3DWkyv-6^Zoj5ZWINq)i~h z;l%L>SV;quCcVsBB4^P~NaUO?ZtZItp7xqP{x#R-cNSd?cP`|-I}}O1FtY2<{|4?H z*Tk7s5(!Hw6_yet_CmV@l~Os=?m(4p+XU@ST_E3~oTE0L)1&ZBn04Pgif`^a@%jA9 z*-^Cf{Pf&gcP9^_n`|6_izr~Ty6@6IwHW>_+ywzusjNAQE~HLi|4BCT@1?N+IPxcO z_rjem1!Uj{xV2k_XEYc?I8*^4j^adHgN#66Rs__=)@lm!EGWneN@Nct$P(a%=Wvd_ zwoHWZAjYut7Kbr)XdAe2NueH+lpaAM4%3?q_fK|5~9Krv1J4a2=y#nFmhFQ0AE=z8a_~P(Se!M`k{?pL%!`K z_Zn;*I34ao;-^bfbj9!f9vKaDV6Q6$a`+ zulAVTnYo9K^;)!AOyzfux7~h;bu44iVhz?2sKGkczR&9Z8|x@zu#VK@u{Qt#6))6` zO)uZSh}uAvUkh3PcfHsIC-+OBLx}aLoP%g_IojNe);8&irtvBpEp!;YF%5CQ#y9l+ z^k?xJWCek^i-B}!wH;J8H&|&csjbOvhb24_Uh0RJTJ2MaO1-4QyvD5VOcnVDHdbHP zhjqY(-*3O~;ed-klbu~`ILDeRV4w>asqhxBUVttL?$X5?=;F}8=+rMTMUhW?sAgb_ z;_xDigNX6RlTCf2Jx`{E(_AE=&oFD|jQ2jL>)p=0s_xt`GpEQ#cxf&qAB?-+C*ie6 z-W94=bwyKhOWB14n;tnpZy~CnX0jc&WV`IEDNY@rGAIx10DKeg0*v702nDZE*AU#s zH|d>F3a<+@4q`BZ&{&wEEKCL9B}njZjcL(L1+r2U;Mpd<3%)?i3}yESeR5cR5?%nd z4*CmbD2{{_W++map`2xAC>_Ue$HEMy18@SPC-dQGJAapgqrJBc5pDG?b}Sx){KNELb& zJIR;|3@VZ`IEmHvX_WsBd$U_E!%n=$s){}(xV2f(Aykg(FNJ6&_==V5in>6HmyB0C zXaR%=ZfMU;5g!AG6j_6hl0RjJSnNP^|0@A@B>IsEw+QzUgppCO!Qb>SwgiqCn;B!pCzr6Je)7);D2Erf2Gvq9LW|LiCl#uG7^|u zOqUT4g-#~f>PH&lJM35i^i zEy+0mXG(z3Gt`$vGx&A>n*;~SKA$?;htS66oWOB;{|KN=)m@`gr~7sOH8VKz!)zD- zohI-uCRua_ETV{mz&r=AkQFKFa;@){KS}#cw=9IOeT}1mj-ow79Yf)qoe5jb-)X?% z@)j+lIsIx5G(Ma#L;jTu5S;7In)gCt3OBIv`Y3$U2yXy4w=#gZ$@^%V6osR0E0a+p zs%GOxBMK5z)`Q-=Up-AJpjSjkhk?CR9G8RFTJq3Yv}YXe`TZn*$hHXx35i%G36wb{ z2f)y5C#C#Z4SVzf z2{!qV%9~InuB=%3GdeMYPXOIsotOeGs=$tEQ6*L^;D%+RDL9?B2+jlrz>66MFJ`c< zI0?F}fE0JJe`9Q8yi#0{s7O&Tr_*Pd$O2BMnIY7u!i{j~ziQ0_4@n01mK9_|i$&s% z%m~-{G$&$bH2LwC006$!wz?tcY(gMKX)vqR| zDn45i-zv{eN#g+~3-);ro;RTtg=k&=sZx{5%Vx%BEsUI{f*jYhfXT|yw!V?>bHmr~ zIFpbiQ<5n^Tyn1EGT<=F$)U$>e-tC?=5Y@8Clc!t;D00M!_OK2BCIKml=4J1N zfC8G}T(m2=n(ovBP(X!OyH^c4??*qNj#=spF~?=&yC!m>5`3qjoU3Lk@-^Vg2l&}* zL=^rY%2*H_5f&aBA|``v;*+dlNv64p{hIl2`EW9`l|?BJQqip!FF5#j^$|+_!4!u?*k^3xF4NpuXTNHe5ij)#vrm1E@4CiMlWLN+SUh z7rN+bpBR|ze5ksiaw{oo{vA%By2jeN+9n-je5Y>$ZJI?(??y~JeEJE}x;ezX^I|pH z4-PH9cD9$DT!tNacJLXuVxDEqd8U!SQJG+ED933IFal~9;`I?2N6dRx;>IeGh z%wBLIiWbJL657WlSTgCnevr;PR2P?CU{H{rDz8|H^w{&KKuAy;Q0-OZbJpp&_c4Jq zB+~=nB7r_S)`<2#n}Wn|?)qgS>uh0+mWjSpAyH&x8A*AL7mQ3c+yuL0uZ*C+!j{rn z*yv6Y9+q~tH=I3`z?aSMW*-#P{B=QRl(8a#^-~X=T%)Lg0p9hpM zjEsnihzo_;x;W9zKve8q>jNgMtlwD^aHQC(Z`88s8+8=?o4x@ooo-1#I#tH;A1z=e zCZ}jtbJaiD;z%IrA1Spj(&m~ z&dx&&Z9J=qVJ5X%YiT zTBFWJdo^yd^h6{Kya$CXhW8W!&I)>37D*OjDbsHD0<~zK`;l2(3 zW%(WscPae&jj4qZnAx!L@BnkKp7o6kKpq>~UfW^WVmV&XiryLb7<-YgPxnI=5NQ(p zq0kiOkl(U;{4@Gw-=wH?OTz&?%s4K#Y|Y@sHitqROI9Rk1iR3LWdeE_z?9PSKy{}S zMh}XAu6kar8#ZR_r-lAGHEe`BP2k3y_0ytzOw}-I%QvMGqBGJJvZ~x4I(4Hm!;4~a zKDx!r)kpY6rT(wOM6Uk3sLf>F6k@?b1d-r1`z6`&d# z=h#f?{4rSAgx(tr8yKEKku%&!l{7tfg+h@8ce6A8Cyrzr(Yq;iAWSC%*?66Lmg?Nr zi(gkBo-F2h-@S;ITzLw1Iti4^hRf^EoyZC=_mhSW-&P@3H4F{Z&+RDTX`kKhW}N^e z^yPj9GW(JUDosQDX#(O8bTeT+WDPjY!00yBwhm61+JJsIKdaK<{LHhExYjVETkmFo zRf_55fcBqNVizF(xIoM1GJ#wOT(W@IewvIvg%W>z=$Q}(uVrTG0(&t~3{vKqv6F_! z0z-5l7?en$%Qb(2^znoLxqY0~kV4ybM8EV4BUz+Jm|YkUGWPYN*QbUChgJ4d#Q^pT zr!x9=KAT3HroX&f-Gh$b37c|#q=*@l0%>IdsP54K)c_qe zCQ`XD8A-K;!i)i`ePJ@9Ih_Eg269tqxhDfumqR9?5zc|rA_p!oX9DFA_}Rh**6Psf z7XWFdkq2fI0ManqU;t@|g4c{X;lyAtk_A9|l%rk?=f(0j>HRA8epn)lBS25v2DPfJ zkg1hEUVx*a6jQUFQ`{3b!p#Fu245$4%zrrjCHMk-34s5bwh&JTo)KshK94ue|IC3e zuCigiBqC+j_ah(v(XE)==6kT*Ok#heSiHgCuM#8O~cI26%?9GSE6$u zJ@nn~VcOxxxGg*d6C4N=xKX$wU#9)}ggu8V7sX&wosXUo{FO^M+*d`(V1?ZV(%kAV7${AT8EkMD8?)vqWJ4h< z==@W(urtBEwSk3~tT5_=Lwbn_FADvbL@h+bbqH1vlEGnN(ZPEDFcPo;KPbjXydkC` zt~jYADK{;Ib)#hIKq_aaF@c{K6!q~F0@*qGHbb%jVpm0B6%Qm{JzoQT>K zfwjW$t1w|)!%{&%;YLKi6SX($9Etsi)akUZQom)|1-mh%{}<~Gn23I?KOQsWGF%F6 zL*THlGOxRA%AsH9o5}2+>aLjy*XiTv(1}U3X4J9r^BLO_uG1#@m(W9qF8J*BV|s|} z1#Tzz)?r=fA~MdW?iXM0VJLWE4?>5?;xtNnReCb{7EtgaYXlR5cBXxh-%^;RGOIMA zETB6W>Pj(8OkLl(w(gt}Wd3<%2(t2cYESm|67L^Z<)Q6R3Vf-oLE1nkNrJ9PLavVr z_qBAudhs55reWy~20$me0Z{|V9Mp%?*vj(UYEbT=5+V+_$Gi)yeE3f*gyCS+t``g} zAsYjUf2yn4k0GNKBo-!SrOEShD-w{F82$2y12jfyxwJC90+zt%fGvr6XbnDbR3HCD z=l|VnK!6lCX7OrhHa>?(`Lfvvhtlr<(rl32_{D~WiSqyII39wDf(rLRN&Lzt5v-ji zyoYE6!stg#xAC84?Z+w>E%~ZDcH@8d#P9OhhnvYu#5XZguqY9BVIi7`KsJ{nhypz|8w=Nbmpr!|a&B=b>|LI)cX{?uz&GU{UOf)MAGqZs z4)?2N@bZP??V|rcA)UmDb^xjY3K>`%TN9THjZk)~3;L&p#bKm3dJiHIfEpF-_(|K@Oix`pcIDye46fioqx zy-hsV8k0fGW)dBnH=u;)J$HJUsum{L(9LldU*@wz0gwQqVBGPzqcKOo2M^jKm;h_R zB>AEvKD`YS*`h*UX+ZaM)i?P+)mqnGueP{$^t|Z_Td6?lnjSD-Kh)QChb(l+b&`7w zH1Pt;-Rhkh+aA^YqC_0)3=Xy>(tp-Mr4VMY+z`_Cl4ixX#nYs_`Ydz_MBpK>Q!Xa< zO?FKVwRT>-)QaSaWY7zoT1)?dP9eAX0g-v$E#hgnU`C>5-8B3_KNpGI~A#>~0Qs21-POe{j_A!imQ~I(t>>y|8+gunZwZ2YMv_GF{HW-fP21 zceHviW>l=IpRj;?XRhi8lcFhPJN<07upMok5is|xxr;@o!95ch`|j{F9rWa7_h&tY zmyi2)IIA>DGZO+v2^{R2nNa-#G>4!65Pu~*E;m^KjDhBpyyknQZIok zdSx98b-@)~<*$g~a2+qRumn$1?_=A4u#F}t_5CSiiEQ!Gg)DDIsRS0GX Iq^dSv zl5bETNoBIU!`QeSdr`=C8?4Q}kV=-9R+EhC6kwhaloFz<<9YR;`|9fWB`03KbD>?H z9rg?5we+@4*G?ZV;Mwjm{>aZK&?h7$C^{`b;s8txW{@8-6ASBcaQeLFmE1o0^915E zZ{rtF(N5$^x$VBT>(a%nuyQ|{sCT62dBrnwP(U?b=s2LfI$Xo^>9~5|^8X|3y~Cos z(y;N-$js~P#u&yo84zqS#+bxtVv3rA9kHUKSV08oy$lR(r~-o20fydt??|6vr~_D2 zG(9Gp#AG+wWOIf+gZbU>V0QQWzCV6ft}EaqCi0&1p7T8SbC)58ynEhcHGZ(QA8~J` z&U1DNORd%utNqk!#*vJ zvFcB&bWdZ|S8wGZt>E#h>*}M}Iko0C@xmnEoM)w(t*0CJJ)mK)@B11UKK!4Cs23Uh zc4iYP6tpXP3fc-Uj*GefhP~fjr}Oixb!PMk*=?@s7`4Fz$r!zYQ#53T_)g<_Y?zLj z6ixYQbMY`jaa?*^4a2ToS^PDQ4bj@F$8P|h1x}6*uzm)amLxsnERAFR;1w7Kalh$* z@1ZK+4sX^R-*YCxVN>qqJOa#OIzf`^!gAgl@(UTD2?GBJ5NoBieOhHb>iN@{GNZUdMx- zFeKn`eJ2DK9r}3Im7(vXn)FQ+WH#I_^C$W2w^6M?r4!&GFWgP8;~5|Plk}r~UAW}^ zULx#nU- z5sA0~2@zHO){kzQ(tq%?3PI&BWaz(i3t8P_?IVF~AFO!)FMvNZ6(a&Bt`R(<<=mKF zkzHH}TF0-Tb<8bjoPD)>?$s7;4Y8v${Nl#XlQOz|ILpPxg;tF;%-TIAyY@KBBl|Vzfb()RkXVzw7wu_*S zsY|Bv4tZC4*WOHV)R~AgGI(ej?A6oQ`{z7Wkx1UmI#I0O_r*KyY5;7itcRfY-?+2ZETwwzcXq`o}4M|t-t-#l*?rI z2j+K(%cv;)W1h*f*M)N%krWhe>wC=8wbw^n)d2U!E{L=$z)-)GYL(#Bix;v!YA~{m zX^KJDlyf1)6DGUS6~P_Bbxc<*Ti{*joZ;7uhPp&j7XU{h+ub}`KgilZyc)AP)(IGO zG+r9na+%xJWapJ>Vchz1$OABHEN_-V5tlEx0-s-XqMkB>n0nr zRzj(t7i=Q_QFzg@=X1}MlfU$lzcdg-^Fp!r%Z?k*ZO{ z(+Rf{?=WWkT}ivI?OQ<;?vUle`@6Q&6GzAiBpx3k&iq(uq!-5*K@q=o4^JCC#b(g0 z1$134eZt<#_RLxPw5pVzqMJY@G6)66GG@ zY3s>btn7&2+uqiLus0qk_#%6psmTP8k-G1KaW9ksF~Bj5>XYGdop(IVp&M{gLy$_^ z!PKO~WuwF<(*qQAS%puBKX{tUP;16`M_k4niZ0QW@kKd{WaQyIcmWP`BvJ)H5`_@J z6?zgtK+-xeVG8u<4`$AlnczRzap=#FV_E~1&H%OXE-Rj%{cw<9hpuD;*Oi1@&~R@d z35U9Cn{I+F$f?OBJ|8l1iYv7R#`O3h0}%79af&YGO;Kx&pscL4tOSW$ug|RJ58dtg ztogx_BC%*u9MA5>6PqpGVPeV3hf^<+_2#FLABb@=Z$AKqIONjPm2|^CvW0)*_~COF z_GD0)9a8L`9#UOcT~vvnA4h?mza%7b1@}k9vE-2w%q%(BO`huN>gepe-qO|4-KW0Z z*K7C(FP&2>?ve3a?_8fE^ltDi=Uclug;X1P^-DIu&hhj`k1=;~dv#r3aVIm8g4t=7 zb&}f@jKylhdPAC+e+26Y{ze-#U)Xhi{s?X zZtIxa)(QSNqwT-|@F71_k(ndU1AAx~l)p53?+i&lP1DZ74M#VlCaKqHYA63e`j3hm zg?87)=7CfwT_2g4AWopM2BeVhbKXE?vYvcTMbL4&LC5LV!s^_*v~K(*+lS6glK`Ml z4I0}?Auj3ZGXuosW(bpB=laUUk=K20dh~$|dGuBSTXdpeN6OiO5^+pV;B7xtURk;(HteAHTW+V6 z-r|~}k}ol2DUW6|P60B#fG&X(=|v1M=sELI_@rO%=0tjdoXnycU4l#bg)9%S55g*IHyY&Z0$Xm9Rhk(lD?Oiml)9Z1YvP;;d4%= zXE_H455!t(+OB4muPo0@D{M9<<^r;4;<`p(Vs>=*rrB^1OW2VI`9b)F2&5ji+g z(Fa-JzjA%%vNc*}RL5ZIZ-4fOGZ&6wTHigsFmH)kNQ2D~1! zA~d()N_>vJy|kpNS8Q54#cpSp71`j!Vlhp8bnpE=(83%8vQJ?tiKeDQv2uufVq{ipAlSyf@yq9RiDv*cqU# zrPo;b`OG6p_AvH}Yl;{R>tc#hL5k!ozG43rT@%vFVLM+Swvujykb9#5QG|=wJPUHZ z*MPn=N)8HI<*}nqQ3l!(+;e6($W@K5(PCWpZ$5cXyM~3vb^&=^Ko(Fh7O7u!fsX;% zDiA$i#-pc62YR>zSfK-0p#xI{K4~#StgaX$s0BRi4J=W_ydIdNDTm?y;o$ESoPCU_ zZVj(Ku0F9au?XX^?7U(<{C#2WJs~X`C21;}hHG_E8IQm(maatcmI;QE1-&SZMaCOAMc#j-{SV%=Fh`e~PLCfQv1@B;uWhSus%r&F<97a( zr-r;SNtioz5S%}8Y5-8B8BxKKSniSGkm#ey633TDG$Sqbj_LQ&Y`%AaQ<^WQFfW!g zNO0tRkDATZ@sf)l*cqY)GN!SzrnMMos-PO87MylSm%2 z&N{n#)L)<}+SQ@{y080;^>%mgvGJFdN-M)KH)|Q-*oTXFt7J`4EpZ*p0p!v1imqKu zyClC1yuY76l0GEw$hz1n-s(p;h0v#?Qe^3YN#TP@>TAQPj1BcXMLZ7)sAqWcapIb5 za8MBbsMs_t;r!LJNom2E{>f5C(|$FhVkLZ>WEC0diY8+WRo=@^?4|F^4AkHT=GYZn zE_Q|Li?{Ho-~ONjVL7s5zl^ZqoUwx8Qs&6uq_bxEa7pR6nJc9!0hys$%#kb6fj-MG z*%me1uNjKa z{GMso>Az_RGifz0Iuv9(+csOoHXd&@Q=A%48eG~uiXX69>q;*Lz zUE7Q*h*@KNUhb19=?ea^=hLPi z%YS3$E@*fEDf)jNsB0_o)8k~&{DR$LEEP(p1mHz=rr*yxyD+Mo;tjkehC8 zG^MR0q)bK2un&3NJOa#~zW5)W$8%hbbLb#F#1RS(fl#o0z)npzjpnKgC;V0A!#>J^ zA$uSZ*29MPCw`dtUUPf8qRyp1xM#~3^zEF&j=Y%zK zHcemCi4eco1HXe5UP?lzo>^0$^0>G|>OpivpeneXftaWm#a)(1GobN3QLd3PZu0c_ zhXd_BB-JO_xIVrCH0Bac@&v*lC(yUs4yT{htLQglWKKor)cUs59K#4R6Xtbzm2s-J zrK>r_H2zH7r8rkG!CED#8||Ud6**sAWg<`t=gQ~{?7?%Up(leZgKZfi;fHSwvkMMo zq9P-&1}_h?xe7Hh;2f~qu7llHnpla+P;PETY3@iaS;Zt}Bo#vS;>@r7-hfCDV7IV< zM}Hz`*&nvP%qOpX%6`A~6+V59xU)ZO<39L={eBxh@P_|9{am}0d`@5g^57fXB#7*u zkgMA#E}&~W3v!D8K!Y-25GK|SHUAi$>;czWQ+l1eHt92Y;7d_UZxMM>`wL4Ho2i0N zf)^3RYxw3U!R7HS{;S{4{cJipXUIld2EQ3(4U78yW_^;pLB$v8BSxsU(=awnynu|5 z_|B-Q$U&wK<+e?(iOY--XakHsY%)CCa>Wr90h}6YE-4qeOy~448#zBg&y$7Z{R!O* z2jTX=P@vsEyI3zHKaDj{Fu8rq^qsNq1y*-}6)gAzO>#WyjSE5Y?`P#T1h z0OzvE#UF(93&1pg<;9WTRrGhI z&}v*98pQO*@P`{D?)Kz#_H|X))U+$dzWz9fr!6M$vyq4V-}L1wvC^UZui*79S{%Tu zh|`mZ|ARgDuV_q}m-@{EFM7jiJPK}}oZ*IIETYD*8x!BTr?o3s61NE!Mntw~{+qqz zC6;R8451Z>_Qvzbzp2ZA0F)by4B9dvD8C9rof9#CnFr~i(ne4&;1iC8UB!X)Hn^)2 zxqb9Gp2IT6jQm|i|BmOdHiD<$(}(nY;jF6n0j21yb!t{oo?8vaTe6w&f5PpYgS}&c zL%w4!IwG=>-T#w7axuVO;uaq-?vV`n^m`pSx6|bvi6SJ=GhO^UU!ri9xUT%A{#F3uiZhE2Cp!F>xQFl&TV`qj0JVD)?+kfnHMKHFjQkYL$gYHkJ6uh)T_KmqU6UltaBE^_ z`N}dyYH^D(c){;X(|7(wmQB%7!9Z{SaL4E^tGnjir#gTj^}Rp!{!n3~ShRMWr}d#% zI3TkQ+FuUIT(pD7f&TBP;%2}vJgK>^4!MzxT~t2hr8N9uiX5JKc(bu*u)CXM@P8&c zGr&WR`y;mORN3B)3tX&Lm-n=HKSRDoDtM@+&lVr65g>XhAbM)1I`;9T8o4F&fOfeywoI+Ei8_Fd%7RKBYFS$Z9iB zddzdXw-qXdz1j^o_M4XUl}{DHYIKx-&gytm*U8~aGh~*t3{7HT#CsTCciKzZ8sv)Z z=b6(W6pBG86oWJ*h))Ml%0nYigt_BjLF4%d z&n`=cipPB9kZInG0oguw&@}sFgO_xN*!8a*{(cK*PPdu9H3bM@-8fzM)z@RM|6L$@ z+FpjS2PcU0#Pj#W^G6-*$)`=gR8K6zAO#4;X4%1DDF`a_5`zDQRrMEKUP(pv2br9Z zP(QU;^(Uie!73SP#KE}dF}Avs!&cuLKiW_1#sbwBXBt%fGe?x3GZml#I**;YY51k; ztHJiZ)5?3Q!7Xr-W${4!uFcrZaF5WCVs@P8+*7Z#R z`73Nun9pepUlxBQGSV|4(QQS#G|l5$a3{#q`EsTpD>c8-c(xgFa=;={W-&!Y+xSU8 z8r&CkkpiKhjTbPHsgQWGOFj50X2MtTTH9>GnVA4_7%wVV{L zmXLptNiOb!8N?X*Kt(^mPG4ZVmMBQ~M$5VI&}MsF(ru4(I0c6Do_CJ%fnsveNRdjT`BqkdcbPO7-MAqLQ3 zzj6cUo{V|m-w32LZUC*9`*Qmm1L!({0s8&T4WOaf$Nm;s1oCuaS{_TE+((`ikY&%@ zq))=}ahY}*yTy}k@TX6Or$OkGB<)QeOc_jLUZFb=^wTGISiqI@)B&=K8Vcx6NyhG< zv&qT=A}%mWn5OFj4TzAuJ846n8PyyiPhOv-X8#n7%2k=2@UQUF<*@tUlE5FOFp~XR zh&D7@raeQxGFf^aPS4oUP1w=1|HqiX5h*g9N_c)uP=YZ5Br%WovzEKypllGTbV5Am zMKZXCV?Q6`H+{MzJNEH0@=kw^h8Sub@O;BHsmd|U{3z@R&BMt9Ue(%#YT z@#$cV-rOPwdpUMz$ac32pu_AO?DMD}z<&P7s4Ni|J?Dd7Xhfp?3gd+P* zJlzmAWa-kE$Wnn+dy@5}tIpCjhhEJIukg(bhuE+<3jtWInww-uLs!G5_6JiULN@}W zkc!CCN73`YblAlGatT?h*<)HTP&A#dHbcR2EMQyUG3I4{;Yi+uVw~yH3D}p&l81cV zUl&osmtY9DWanVHhL~#3;2|YwVnO`1(R2ale?mhR<0-wTf+%LJt9LqnQgq@snKqkj zV7D^Qi!*USDvCTh2ps<^jH$p%uVDT`$s(X|KJ|cfBYlnWih%2 zEnzEct1L4e#^C_h?4j~#XUIR1hUXLMgqKj1v)c@yES{Ynk3AA9rL&;pUFE0>Ay6d- zH9Zzg<)EA|t&rcrHN#=Cv!j0w=%3gv zD?k^U91j5pMnnHfO!1iiHUE@X>mM@r3;QNGqd!HS(tyHUq|6&pj4%^)j-`w7;bOk- z$wkx@-<#sSR+6LB5$d6_6DW29Ml1WXr_ISxYJvv~JC047Uj}VEY-h@PN4f^4rSp5Q z1Wz_*U+}wbn4cId{G!jRzBlLdwA)72$&UxzkF|dNM*0U^a4gjU)4dq*0&s~nLSpR- z#LvC80DI3%&oW^Svu=m14j;G6m6c;7sjiRSX}ZOH6wcN=G+XDo3^s{_>Mr%TfY)+S zYXKtP^XP!2n2Jhq(MUB!3dtH?%enh%?ILOk zSbM{FJjHvAGt-l{1XVHksKB$(A>C&fbd1t~CP@XfK(JOvAwMgKIuPX<8KDP!B9@2y zUA^YPNpK`##&rqR3Hboe@{_Z(3-YsCGrot2NB0P_s7=h$**7}p-T>0HY{(2Pu{EvZ zmo^_t&(D@u%g2n8(m5714dJI`Cud(%FytOT%OTgf0M2v^PsG^~;-V$~%RN1ED!o?rmUmXy)SoI4`*=J0+DXES z!^&h34Mg1;)WtWcrg?7kRITzBjnZN?>9b*Sh5*7fha* zaV^kMOd9ast+_13WoSP_B-KLcSP9n}dw6{F9-tW0`6WH*Cu}t0@%+_c(s+I>L}cx@BzPw%k?M4gvpL)YPMh8wvU~ z;4>pk8bjEq6Vf+APpW*y*+XzVQZiMsCR?Y_3#9OLBha_eHo9$2Fs6kU@@^QL8R&+F zv_s?xT@XLwN?g>VX`ypsMlhHkNX)Lenh8f_d1^tev9<%`WV#J;DWaEn=Gr2f&zdY& z3iTYVCf;t7w~j#)#oKAZ0HRoo06WeFV)v zrwf6iX?`33$BkhPY(`dTf+s|u{9Ah+XN>GO)V6sLDI+_E?ZFz3n= z-xA9;x8`mO2V50&SBdZqDF`;geZl0Ny{ znUXau4Ymu*J{RDpzK;`l0c zmrP_t_ax(JLbK` z=j0F&2fd>YQ*i~A9@oj}Sx(KMc-W$oacIXS(2TK92LnK?#I@u?CfCVC&KtYvewgL6 zS$+zyOdl+?GmoC7uaEyoKHe}%UdPi9s|swALJhGdB}LmsbJ1z&*@CzLd^E&|Pg~IG zVI)ukMCBJO8INVy$M z?r9IPhqV0hxtpdtI5d|@Q>NTZK~%E*F#Ln!E?%*X1(qH`{|%t(MM2oRmtS9fAUC`y zAS3h(Ro6`EOzqpf`(_+6atljyvfgPq`GqHN;TM3_ne%Mv#xnjtVYEv_1n{u0OhZ<< zShC%9o9*$Os7h&eWI=g;U2Y?zdYy*H^ZUD-;H_8o6=P2mb!u7^tGA_5MCj9zJjc% zFuF2IH~1LbgOZRwe~-L7q1$kfy!DQWV1sb8fIA@F)wQa`;Y1vfHp2r->6qb2|ICne z&;8yrLlcADQ{@~MePu#4;a-HUdpjF(Bw&m0slfno$zcB1nPUL_e9ho3!7&UHIkO;O zo_|Uu^d}|Qh8)EJ`>bL9q|D+d2I1wt9US84kF7`Jv~Lp--@hT^G2N-d#CWq{=o?nF z+K>0q=J(*nuUpOrpYpeHy?i;xKF3~>Tb^H&2b6gYUCa&zDJ6aWryVRj-wezOQ~0Gz ze=7T+VYs)$zXjQngWY1+qKn1niW)o12JR#u1u}=6e}99PnnMa_zP^S@6%_t^9hUZX6Q5f_+(i7EXUMG6hjHfTQT&_sKa6N zZf@brL*WjLX`tRI5~f99LX_#<5R1yw@1&DU*mb{Q5-%$n)w3Z+^a!klfII1*=T7=S z3mS->6o{R)bI4gu+|-d1sjAXxS7nceYk&=JjU7?XbVp8PbPr6NQx9nxAi8RwjF}KQ zSOuEA^}SGc185iKC>-~4E!lCHMrv1+$b)ppDq_bMoydtJksPtuGT|UL?B6!#0RXP+mCRt@5@#%(5K&(M-+Oeg^`6f z#~#!^7_?SeR9tMoE_S`X_qOH0H3f9jqwwBJDoiXaFdqMXjAjc)Zx1}I1j-^Z*j;zv z8+xqj{k~4|nSYcPRFsxh7}>UjN&S2!5+je{qWrRwq6(u+&Edfkzd(tR)|qZ&MY}To z$9x3FBYZssaKn&%W8!EpMANVDD>#7_ntsk^Q_lu?fTrK@X%mAdaZW^=nEqY3Yltk; zjH~TN+f?Nv-pbZt_kQ)nai!tRc*JDs%>Id^%H}EmiLY+7_mP)w#7>KTXbIFNJbtiV z9Qm4iu(@h@sXWG9`*6K-LbMg=vF6o5;9f_8K?!vb=jq^B0(B6lU4=Tx4>ndOmYL0Y zBNLCtl2z}}cPtQ^v1e~aG z%nwd)Hx(He^1j*2-+q*ZFv#q4`qW!UbwD=Fr}U|<+`lSo);`O=L!UyW-=v@xyR_rcFL5ijm3!9Yh@~K~@FeQilt~e=|GpMb|0=CS}@V3azY0n55RMEjvl7t^;11FTOlvkos=0s#*AH2lP z@>a=qlRkd@xj1{w@+t!B(6-TC+5}v5GMo+j>0u5W0E)j~AQD>hbU%~<8+Z=r%&(K| zbL7ci`6%=Q*+fBhPANurchcR{Jg@nI(IlI4BnvKmb}CSuXP;-Iuzm4@7=MZ_+Jn?{ zZY%r-f3I&_6Ze9 z`I$f~Q;KSgMZ7gUa7&hKYbU(+2FQpTo0{5+`!4jUy5+s8U2)xceTK;M&A(kgaX|wj zNX?yesEWlRUAnEdezu#vANGsakbil*_;|_lrSHfY-IBRn*3Z%5tb-S*tW7ob-DS

z+gap@6D(+i8p zQVVm_YsH{5B70@wQZv%}HG3Wd!3%<@N1vKTAPxNzV4jQs#0?HNUQLH4S)Q6xX-pOg z`gh$r_@Q%VPC>CE^Kj9zmUGu}_cgZHSv840yv{n z$#vxiO^$spOv%b#S(umED*x`$aZ}x+2DIJ_nLky89uc9sEI5qXmI@M`o&p^?%^SV&C+LvqyFHbPGf8pphzc;^sVt8P}i z4Ya8I`)ZV-16BRfkrJi9%BIus{s>v1`E@M$51v5AD>6l&Hr&}W89pI8y0D46=4fdU z8*xr*8F)6KT}*ce8GJ;xtLb)h7f&DOk*%;l**ame_yyqy{E4&?`Sna!wfJQ}x;2Oz zMR7@@Nf4@99hNDRMw3Ulw0Y~J#QGq82ieH)SkQfzWZ&BYYA2EJ*Y4~K^B(f3QWk%y z&=~2gh#N~5>|ARK%R5%Y3V6Tne2!0d!U~)$*u{N%oBegybEw{m%mD_zVzT1Q{H6E3 z{?hx~==|x7qPF6xQ}X$4AvOd3uDgl{-D890*&L z3=kF$!yZsFbJH9@OmjXb2iSv`O+8*Z^YV!^IkEzT7hJ#Iey{oqhK`SuVL`2J&4oh8 zp-Qpafc1^D!wgwGL6?$6!~j)87rjjuuQR3AS|`|ft+u8s_HND&E^EK{qiqUKV&|heVY}1ykO~L%G%2) ztfsiuc%Ej3`8K%mj-0KLezG9dpV;WBa-fjqD5jV!ps<=8C3jTx4vLDuELcSf@rd8y zbE5?@S6JCvP+JsMwkWK&wg2AH(9zV^)Y#Zw*lyR;*WK9vS%-74A+f!kGzv!E!};TZ z@@|QA_j%;t6zlGC;oN!e5m#|XReNWB19Kyp<<5c9KY=cs1BkWz?>TT}wuE)FK5^lQ z8!|Y50pd`#wuf@VzC@ftRG0 zbKMGjaWp+(WV*NL*N=~oL);Mp@NWJH`P-wvJU&90d1C?&y^EMXLLTr-8q%UuqOZjm zeP5!guY+3P1r&s`Rk3cdA<;20a3+u?GU0Al!D|3G2ym<+z9At`Z_1dJnWspvPM%Q<@PXz9X6|AJmr5 z+Ta}iTeo2D;M@yrkh80;lP|VLQ&mMrQ8QL#q3rH8L@CN`6Fp#88DA06f!svcHx_v0 zUrO_aCq;C5U^Bc7YqbWfBDFk?h>Do_tc1dB<2AtsKi?6wy!Fvx!t+Yc$t%jvv`cr) zcFix%D$gk`I8!MO4)YH54e`&H7K9cfwy7Is&}y0P6hFBtv9ip#S8Fz)i+n+_Q}*JX z9V>F8EB!J<+q+ALAqu*;lkXA|E`zimf4+>!Y~(#NBHbc0`n8OuKWKjzL|8?5N5EDI zwuT9wiSE}@0mmkWa- zH2pAHU1{(>0-vvhuaG4;UcHzEC72(23(-Pzo~q?495>43sk!yW4+IeI#SN?pGPpqm zLsal7P;&FFbhQ;(y`8^g&%ik)37D`{kEv`%+m#p8LwyARNMEB*0P<5_9QuCr==5hQ z8+d%>^pLlvqBWvLiNR=_-kPvd3ma=|AK)xV-CU3$v zHix&?#Mx4)r}}_MV7PR>iL*!un%E-p8$b5QmGiOA;nm*Pee3kD-6P79kq}%u0V9SV z4%`?_zL79=cc{li{Gw2_{~m9aM_x2BbP?(<5(V3Jyzkv&Yh{zzuh;s+b4tdWL?d}{ z`3^ocB+HTMwtN?tm$#g~L6`64SL4$6wy@cM=0Z+n1)LW8dV4>p{*XCL{aNq5Hv3PW z&yB1M$dV2A^?zJ_4`%I1C(#jNvg95zD;CL2jMpJnVG(F%s9p_0yw$_ICZ}Nfqt;cj zjh5~27M@mDi6h-3JR|%De8ffIh-9nJ6p5oW?zdTnyvIfymAvI^-4%cdU;gJD_OWPZT;4VCy)e_aX-k7ZHJbCb%I7e)oa5;Z zu!-PM;*bs|Cxgv*FR;zME=P)Q^$dkw4 zg?Qm8Vr+oj@+_c4r4f8tiL_E$4iGAKFr_ZJ;acOFRB`m`kPWs6vZ9NE)1r#&bK5g} z6h4`LX+h;>VmE;kU2uRdK5$GSD+rK>bauAitpA9iZ7MP)D0L{e$?|RK6^9ld{uqk4 zK0#JgerQHiZdGPuT1&1^j!$|}0|NFcyjy%~uT>}2B-Jv1BOvwJt@5E>2dyRq33OjL zjin3eUU_s;V44gg?!$Q`FOeK(I{7jkT=~RO4<H_Xd}U%T21eOw*||mKa?}DGw3jnVs#LTJwZP(4%ZMlGK51HW-(W3Q z3A|}R-$W(wCMp5o46lyWAUJ+hb>$D7@LP3G+x6xim6wu0WB3r( zmCI%=*&@55r_-zrPg1rH;?8aix~NbfZjPS7J1eup{!CoE$GEe}y8; z;bpeuQn6LK^Dv^G8+-?RTNv_PH5+`vb-(YWUL;&MSF6jAdi|-cj*YSm+7fI93zBSA z&V!6$ri>%%aBiG!8RCwpnTUZERTx0n(m1zE}Jh zdj2pu$ajDL@L4MM$O_AamkU|ba8uRrb>}M|9V1Wrq!r{Rw=50q{G?V940AgT0XEYP-!I4$DdSH3DC+z4~rs+b2 zjHu>mUQJvL_^O3~*s}5pE5R}0Ec(kwNvjHkTomGIJQ9yWa1%cROnZ6Np=@!i7qm=n z%w}E5Bb|^`@#BtPv59l$0@s4-p_S;md&{P?rlX;yp|PO_K1Zku_us^JxYG>K|JKlL zQGWrsI{OK0XS20(> zm?>*6t*t6;WOj``kdjr)@kp=bq?e{=F-~;-RsZD&6AdQGnO=HkNKjr-T3k@x=gm?5 zyE?^A82z5C$S6!NPARcS69@0UU}f%{8Lg0IMd0_o&Oa)@Q0(57o}XHnj6Y)7XROV! zO^3ESX3OAX6$ZLwel>)cCXez=Z<3?@s?v(`A{Rx|r*$K0z8+5&PAYJ$RM^Xx03QGwB@FTSJ; zSts4e27&e@>Wh;N+LNf@I`2-~cGPxOwbixOv=U2%*BcVSt%uVEBkm(4Z6x+a!JVBC zMU6l4Na8pfp9{tE~%^R&2N0PSV(?~W)0S?iiwWlHmldw zWDo9#L?u-hUyH#mR3XWVtb+XP=Bx)CuNm42-JM!6s8`9f4?A@a`JyFjcvad!%y$L_ znK@bc^1}Dh#WB)w-v|kV5z#b#g4Mq6qI;cxXHkvzbvINFcX&EX2v{5X90tMy`%A~| zPeHYw7bFksEV_II49^MH4lbyC|+a6!JqM#s4ezItPn=PuJC;RAzPCo4->)CWk zx=*SQ+M3;rvMqq%rx6fmU zcqto3T!t=``VLZDnpBW74_vx~c zl$3iu)x9c142gf0XcePnzhvtq z^**&e4vsb+=kVg^_@svPm-f`PcT{_qd*}N#Vik+L%G}G_2I|L)?z(ge*WV|9Rnfmr zAEh>&)N$b=-ZAJUrFQ-fUal@>PNlAe%)45Q0sYcEQ$WykMn$o75s$t}+s5O=gsaD{FOr~Ey8b0P~snE2=u zL<}brb4EpL_Dxa0d@6yc)3lY*b3+N>bfm z!u%#2;Rt>~ed@l>jzYCem7|IoNmW)22XaG>yIAI~zMEq+$dVXJ{oGo-LK9la+3Rt?< z|GIx8lc#+ed>*DLg?fO`79YSt@&kQYCAW{ zs!6k@fHo5zJ0{4C$d;yt4mT9{r}r>DXZhAyvYr*AHc*g4 ztng0Y-XpgIr_v@{$3C8Zc*nWk`KG5HXdQU2mq+%iSwCloGcKNJmg{Qjy2~4w+i9%a zCEYg3a}wr_m609cwaoV%lPJ$V#S2U#7{GEst7wqN$hb?>3v?py4uv>19B6=yiLa@> znzy_#F+U|c3x_aICM(F3H)VXuB&A=?!@K<>xNj@u6lMF9t_sKst7*#_Oal#mC4C!j>)OSgDa>)xHU(Z0r?d!%bYXXXD!fEC;V3X}z3XQ`Re zST+NduZ?kAMtny!Dqr{|=)Lz+{-^Qhi<6@R*#$D=3l zj-SLkeo_OYSDJ!%9R4*t(g<*IbJRhMgCb*M62hp5>0|k(oRNVnP@9l?H=>(}(A68VJ{8&BH##!*g{jq~HJL7VjGTb2zeGV7~?%b4jrb4;I<=L2w$UBob)w}&GuDzWMb6I<7bs;R%BEl}ClAKx`tOOMni_ts|NEC@{TPliSxsOwD1X#c z*UElRo_<9@p9U~Mo_?J^%_r*?eMz2vLjZ!+6;||gin%#;LapzFRdLb~CqAvhs$^g^ zJi!TTWmuHiQ^GL#Z9xObtxE9x+50DDe}39IhO^gzx0BpgA%O}jvXmhedQ83kQww+^ z${aXM9R%{=VLOm$0@TIIh|xS;Ez$5>SvZkT>=UQI&;uORn9A(X3c-7Y++KbL-m+yu;?fsxFGXfLy+ zew7{t-p*(3F4?aTynv6qc2i zmS5};hXnciN&;*?7SrarE;j3+-j=3@yijd)Mg3k#%fr0|k(Yx`246CIUh-1VUK|t8 z$M9yWv}1axrRg!$vg|R`vIu&pd8h^6Wf*W?s}}Z8p6&N7>L0ZQMK@v~tQXC4I1CW} zsFv}1;*}@TVD{&NZ{4fiCki@LZO!9;6Z%=_Gz1z~c7|``Y)^P&O~A*bjq6h}4sa*0#u z*`8uWKg)KH+4}6LqQH#M-pRVVC3l(c__0pW&XF#G z3V%g_+}lQ68decl6cp(NRgG^zfme}Fj+>phJg~;6$j{~QrL(rK3R#|iMkpgU{qe(( z$O<$)%)d-+?+QZb3gAB5cPkY69nfQ^bX+*78pLf&60CA$)03?(%dY_BA7E|?TuaCN=*HhKr z>#D3Ak>G8zRU3Yz#{2Z^$c>5G?$P$9iRe*L?UA2hk`FNkM1el120W20Qt(x_$-Xaz zx{TTNY?dU`C!K>QEXlKc%dl(@#pN^)Bl^ryt@p?(eokd>ZEnqpJn_$J`Yu1RGNe{o z#;m8$K^wxCxk+q-T$oo)W2%F?ke4g+e_`oK9TXD_cda)iJLYF^bO$+(Pxf%1++=^< zgZW&pPRN49_Jo|gnMYpAGfGravtabUZ>Z@TqfiyK>sHWq^3D+x!Aap>f%dj`f)y1t zrI>8}4i%v+&g4+PP{gF9n3%qXuRO+N^^3x-fACtk7r;`+Q=J+l{z?~h{eY*ctIuAk z8nEk^5%EEeYM~kTD+rwUOm%$hgEprrpR%Cw)|r}a^1_VIq{w2dd~kLFc@aT1-3Y3Q zf+@HWQb9R|O4?=c7NJn#+}Ua$Dwi8#wY+gpwv0c@dVOW~a&?xy`@-BE!kWg$cqhv@g?q=h{7Jb$xHevH`F zq=Z}xNeVY=%Z_gKAMx!nf(&*i&t8@;#h5yLNhWMfF z1objsAle|qobPLHP8fwaP23tGV0Gx@^I$Ox7hQDQWl6aZfEGjgkU=hTyvxtkoZ$c^+J9hILhJq`i0b#YWp|quQ#b z=IVxX0MK0>Y+PNWJ-*#e4Vn33Vof}$fL~Q#*;3qWe_wdOG_z1%m{DkvDRzGMzzdsq zilC(nGuH!aXm!WQP^_rH9QB>NOvD!=`uy z8ON6tUCmwIP?}y+8_(n%x_ni@fTq#G$7pR=!UE9eL>9*1pDygj;xIkt^Rn z-Y3d;#q)f!{h#b_YhN`@DM>6XHWtN1%CreB`ng2gLNonbf~xd+QEMY*`nkj^ti=C4 zm%!yM8L?N_C~Z5uRlP$ehd2vF>){cloyw3Hs+5|P2@fS%#t|yZ>Ol_2ogIR}NAHuM zgq6W{pUW~@ym3=el8LdMP^X?XX5XbR9VaiDyz_}rbZnK(WOueuWL9i$@-B>mKO;A= z$^54UqE$!D_2G`RqvOWJFI4;UpnG?qm6Xq2m$7UrT|Ic7O+Qcqi zmMHTG*y>{Y~+gvz5~mGLCc zlL{^DW&DA!Wplh%?OZ z^!N}t{)rMN+_5KkORQAX>O(=&Tpio*IC{FlxwA{`Tx;23-N=xo+I1YVAmg2x{z3IJPO4Qh z-rXZO7b9i79rWP8orQOC9xsgK@pf!J{tZ-GI~VbImb!JM0>2Ia4&AJEhEUlY?tr4F zj~eo3jlch|a3TZSMX#JN;4PKuXKM?VE___^{lD6Y?E~bTI#OLUELByhJ@5uz##q&m zSSS-UkxF7YRop|?XdnVm?quDq|T zlOf0Z^^%I3-Llo#-uEAG@BdhnVPN3Q$>GpR%v+Pe9!^gK&w4y7x;I9CQ_;ye_O#gQQU8;MVwIpT& z>5lX7QA2A*SfOu5=+E7%@oG(%)9oYGZq*vGPr=3+yNc8bkVf&pJF%1pNH?+8pqZ>5 zrmMM#k&S}(PmdNwcm!Pxb~Q5NBfX*}u=-?>SmB%Pp6<=OVoBcGM_)KbUU1+ixEkO* z${G4`xLobnA0h8jT^YF6by<0?YM|4#R@R?6bXA!!Y^WS|Y?R@`{>40}eZph3?DtjV z+1=^U9-U(ZlXw?p@-R!t{=SDO;EReAkbGznk3RZYlZe%#4ly0Inye_gPHdj(mDlXVx^?}l2nlz{LJ-FtM zrhCr5=P8aZ0LKTdSNhk$D2ucP=1|^bFgHS*^be}qv(3B9p9Czuj_Rg{G8hy0g6@a~ z+J^n6kRQQzVldV)(5hW9$_)63@o7Qno;M+234_3-r#4+wvOy-wE`mY2%>=p!ted`o zi09V?)W`y31|!AbYz&J=MuDlyr@k!w^Kht>H?!kbz+krMBU02x_t2=QMM)nMp|$Ui?d1nH3p z8?pX-lmlAVMf~vXVhqa>z3?ZW{*Fo63H&1c9iuMdXM7vVE@9Bn?$r+SKgu7c%L>{B zb(;QfY2}G5f4=h6PA{k{=ox+*Ne24Z{E847Ch#c`Mp6^j4~{oEHg<603L=Xliu?QL zAGFLkm2=`@iRk_%)Wr04zGmm?v+G07BlJ0Ce&!8}_dNk-FCZswq4uNRN#%$iT79}B zpst_eEyZi6FO?TcI9P)6D6BsKGrr5Kve71dN{LRPcOW{@$Ij#Ez?g<(hW1|s*0eRN z)KDO$O#xx0!Y8VEqY z9NHAHFp*2ImOaB{EEQ@o8O8oBT1-Z*X*7dLj551MX-#pHOh2*3Hq15Bi?1(5tElRj zMy@ofx{kvubd&k#lWcRy=5w>Dh#2?4(AaVxPHA3!aXGZ;m~T=IF`c1`=+YKWNP(lw zE1$%dG{>lM+xw@Qu5~tZVhVZ1TH5(B0v!=CGVnq zsyezSs1wo!PiC#GMp3CCZ$6+x4KH2TX1T*t948GB#MKto7uU&17|a}ULSel`Gq)j3 z8e2ca@sVQP3G;H9oP*1d7=%3bG2Hx96ChtBusFh@gOea+yK(gHm`2YG9RLqlL2gz9 zqAmbZYMGrjf}H377S7O6lyQCrq%W&3m1c6IC2X(;kaj_=4^{Xfh;{kat#qXb7M>xh z0WPU$VjTj`%))Ax>(J1Eie6)zdt?dfxAn^4l~L<1^zw+sgW;*FF7)P9_@wC_cJFIu z0PUc~DW}OZ=~4M5hH(~SyOv_SOPkRF^=OlJ75;x29ZX@?0|%>T2~_RC*TvU_bcaFJ z?hW-Zx+Ux?2n1r4#PWz1s4}BHH>rd`o>NR z#;m4IY90GJI3C?6=FbB*%>WmS6#v>SE15Ji-GS2`C2)>d*NtG&7dYQv&AGPGg{%9j*j4!M1N zK!j@)kb14wBq-GfP@5 zQ3+I$op(D1M!bdwX3BfeTQk>b^oW*L&t7#obwPa@f-{ph`S=fPOd-)uEMg*O7;R-U zTFpM(0wYf3FVx*F$C#M;3w3+TN#<4bK83y1Zy_(DQ=_30Ey8z1T8)Ho3g|MfiY$K) z!wncvbbb#EK0r8Mtj+I1y=e*rsNpt0M_6h39|gjzu;`jEQsknI?WqOnWf=g~X|j@! zb^LVDT0&A#SXfXvb>_oeO;_4GInlDP($I?X;!1gW$+b=nFCri)hdjP5P`!I$P-i_Sy27KuR{?~>SOmw( z+Yx06Rq+B<;n#4S>}8hQi*9JE;*RS&pX*m+mX_lfIFXhrCa~#=zgRp$#rXw0MEH`{ zhPhes6(MchO8v{BMO=xWI9Pv^C9*Zer!3K_bC`Qcou$Iwx6m0SJ~N3Nl&>;XpX#tp zSP(;N45^IE8CNsypn?97S({rWDihnaa_mCi4?hx@6DJJMjH}54_5*r;T-OTe?k6xi zD%T9XAk}$~zz+>!Secg#g$Qy8hV|qd6nWW_x^+*8Cq=14TVr(Y66~$Z_yCNMZ7Q(J zb8ruE3B48}<`)MG_;r$cc}*#4EiLzNSl=bB{4RGxOb)d3^T~55z`VRZI!4yS-_X_8 z+*>|;xquVt8R8w_pIm>VC8e80n}?a~Gz9?Prgo?I53f@@AVY)ICt2n&P_xu4sDy*E zObkqbff_)}(vs^wN5-Mab%E-TaiSFLW|0KQMepU)^E6ZA=z$790PCp-sU#gBAExCE z(e{5@SZJDFjii@Fpn}A!&?;c6$_f-PXrxl3IYcf&L@xnrc(^X>|+ipB7Jo3T}K|vkf1mQ0qImm5E~10ic2%rk`)##l)}t zN@X&v9>}C#QfCyMshE7~pwDWkR&SKP^8U)_`afM?Jg+=x1Lfnqc6j zytWgs|7;U_!Cokplou437u(dibhgO5s+ubtyEp+MPF}9T;Z?jkpq>M@bD?)$H^LLR zpta1)wnwj-gpzDNV~sdY0?>pCp#rK+TE&ZsO9I8~FwKn>2Ldetd&?OHzPPvr6wd~# zT{d9cZKjb86APi*r6AaC04deVOfgTfzu}o|zBCdA!@3aG07IkJ&ex{Y0_>X%ga?f) z03H?5!fPJa?)=aZ%^3& z+%~MVQS~UDl-H^!RBlk`uvO{ZtMgVBxAr#ojs&YNjrEO8j8Cd^CMQSPHD@pI)jjHD z$`VZ~2^dLPMX@o1}8XUC^uVP;c~?LBsY&=6r98FEgmfG zsmkMv_(Hz*wGSLm!N`S}n;kn_@maUBi_DD?#AL@@JdYXYb_9SHa&tgdB_AAPJgobE zY9j<=>D`-w3v{L%us;%$^x%Trj^Lkiq=<`2o#D9Ee)cte8220;goOb-f`Q} zVATqiNrU|-p~fxmA&v^XApM6q~7Cxw5GqU54|{8*8|{)$p zocBBDYG06~h!Q2X^f!K}m?xVPDY;{rnaF(*a9SFJM!21TaSyZEXbsjiH#CENDEyMS zp*n(o@uIomRoGn7{EkA^zY~w1Zl-wW{Y;%53KB~qg^3+~jUSdQ0P$cd-x??*(TiC3 z5d8~Y1Q?qand(UT6})2u?J3P4C|Lam!7f@Fr6C zaL1R;HLcanbxpO+4Q(!Y{MNcw*VdXgm)44wrsjrLy+@NRw=Hi64E<|LIG-~4WGHFj z^quVcN6^~WZ?PFChm4WwA~1TIFRp?*lt%hg0gLkmPFVqf1bn?Rstsz3zws!Af4Pe= z00eRr3B4gv4#eJ<$ zVFh$;%Bv~W>Lzpwj}WCTIw^q4c|o%VX5z>+25dzc1MtOR7&>D!i4kLb5ow5MPaS*= zeFV&D?!fnpYG6D<`wG{fN3^7o#+L{{0R_06xluY3c zpvLYXa1q8-tZDE8U;BQ4PTfGsX9c%Nl$=2|o7V3YS*jq2RCzsgrt`~}HyL9gml9tT z9Jyhm%H!PZOS&IDN)E+S3JckhY#U_}C%G^DUJ&ebg?3U&mtqb?*$zYW+Asb@pdyQt z0Z^mk`9u`}Mo6@QD*#3Sq8Cj)iIptP?BWO1KKv3CBN;*{=ez`6|5WrcVuBVh1W?>! zJ~Z8jbsxg|KHfx~z`6@iKqHw@W5ae-438Vk<&x-y%RpUyIWsmd&M00~e4|JpPZzDv zE6mIkY!GGV=4Rz*=VjJrRK{dQ3;4pcz_hT`gj8T=R5ewVwv~o#O)% zfg!?)Azy4llVFA|zzkb}8Mc@PS3%Dr)1ebgm;i9Dd1633A4yp;T0$_|@7{y~1$yC@ zWEVhrhfX=4v0l>Nw__ajs$lOH3b7j*a+HI*uNdaRcvxN)4bZOL{Ucwa*KXH9O$v1} zQ!MF8!1(I0b|9^VJM>X^Zzl|>cXs!9sB-_y*vDs#8Fo7UmvIQ-z`umH`!+s9^T1Fk z?7Y^MH^A$3>Pa0u#QRF+`YL z*BKn^H0uQ-Vrax|WrT<4C5z*Q3Hr$nU!8(qmc-!juk|~V>;(pcf&y`2ULjfU$h6Y5Qv?3v?sY+ftsIyGE^#Pxmg%!C^){{C zidLihnwRkXADM2xr@Sm-`K~xj7&~5aucg1$p@xGfCbB@fRGcMuPuNv}A1of0e+DTF z%2FTHj#vm73`m^leHL)akUG&DEsQlA8P6b#Xp*>tgMR;I9N;qw4cl7U%13N_JyoK< z^kHs`YioQ9j1?>IJfzbB0HV>t|U2kmIv8}k*Rps5%*OA;C)!s3Br8TuLUg_K+ z>+zl}>O$)#_~Ud*mBAka2z6{Y{H2=Qp#-=e0PlmCLgRxXA;3hz$Ohgyx{5R|2NHQ( z#6`A*<^y`ogTXknY%6wFuRzYk>{DGT`CR1A@XI}6962!53TMe`A(d+>IAOaEiXGia zw-Z!!bX{l<-ru!D%t>sD8jAwf-B1RIQZs66VAsyVWCE3EftKf??L}ytM8BVKyrF|4 z@qL^JpW*CsCq^JZ{PjCuWq#F0^*P%xKR_=~{jTt33)SapLkrvsninVEVzCppFjyAl zxJ>(F@Y4g%*4m%$bFpE5)k+PxSToTVc#x8L0b8vfjd*=5(JO&gTtJtn17#b_`7qqwJ2b?g{4<4;DNYjgt}q z>^jkaSRLAi4!{XXbLgt2MUDqFFQIBV65UP~CQej-fHbHmM+|sy z;;Uj#ymNqqTL4VgF_y!@twGKM8m=|+!>0&9bUsM&?ffoZ^A;ycqI2U*B(l#XOtYkzN1(b#q3yQLfvI|Ld(c(LFM->795reT3*wIm=76K5dR3OMNgkgkdji-jcQnO_&FmLpx%DWIX=2zvx)U`HhG(C$=A!;;*yJIzM zHL9GxaqC~BbHg*^PsSYQfEDhWnJ;Ii=TXdj#BH{}dU2I{k!teovM&kff&qgH`*wH0 zGj&j7bC}>hhaTP{o~A%^=QJ|GB;KxRn^s4Bqxp44{VOoB0cgs4Siek3(Dp-oGlaa4 zSzR?5c_ka<;%pgUv03AH5sA*gct&HSdQ}K@!jQ(tF02AHjD&y~w`d;{V9DK~jh~3m zC!UImOpH%Rje^nmfXJvcrwscnQ4|zIcs(gyG`xEz5dRjJSLS{uLJ>|Z{5v3Mu>+z} zjKRK47X6?=Y(PIJIxv?wrl-!MV)_Fn6}c4hmS zBL^=P#Z?66Mc*HrzCZkRn^U8c!gF|#6WHq1W7AVPS~e-20V-@`wH5mJUylyR83_T= ze|z*K`q3l*dh|=i24{m<_z(K&SbP-2ypFdfTgC$HJzNlAdvlK7fWPB9@OMC`1!!($d=)_3*g{#6qOb%YRHj^pzf^LktD&uuGnzGgV{~q$cMO1jf1mnl_8aH zux2|l02RGllq+vsr+%GK0|7mC+_2#>l*t2N&eTVLvmyJ$CS$n!^Tg5x;AwWVcrF=P z_8an|a)UE?ovEE^ikp?0((EE>S*7ry2=U<3FVNRw8Egb!qi_8=C8DM?CQZ@7W1~qW zMM~aq?cnT?Vk`>CGw|Xp zOd@D@B8_?{fiC02|1$^AHN{$cUY|^VXQ=rK9irHqS2C=KsB~T`&j_D2MMs$7Cu|R6 z;!I(zG(;GMUYJvUR`#*#?DXF1D;)zIzupV?Ehk|OcJdN4>t!=8IpW5*McT6eTW4a5+WRY+plDBx773rLj$E{B=xKH>ElBF^L|D z5Fd`|-t%K}1Ej63NtS$2A6Nnf_0e84;osD!9CGpkiM&+sTzyWd(E3|9RF!>Rqiz3X zY=`~M0>k0q;qgJinBJ$#{-LpMdt{8aDQyPZ2I@MT&^y3IeQqXThJC9gdvQA)a*JQT zQ)y6hCnvbfrqvk~>Me~MSkfA>qzUSeD14v52FdumIi`&A6BN+zCvPDm5%NH9tb;Lk zw6Or~DEp<}vzN2Y8=nfquSb*r#x@i_M&Q+n_{bUjl94!}BqTSMqydiZpmq7k3USsI z6PfOhL^}3_SwYN*k$4(Nm)i5?A7^i1Q!j6f$%+uf3&{(qH{yA*SJLCxho)r8Hw?l! zcmDP6*Z4Y9S56T-6Lh{oHN=g;B>$v{4n8NpCSRG|?Iq$|Pt{>>O5o!i4~e${q;OYVzJ& zhOV(ZKGZMaw+Z9&L$U#iBE@*E`r={>1!HV^<>KnRs#B2S1jK`ikZ*!^w1Q{2oNywB zN5*R*n1Jv;Q_I!>q6fAH$e_yUVLfSSrSOpuCDD{gZM5uZgs^k8>7-@9XieiAXL(`< zs|oCidq1{0@bA1Nl$`AM&N_D0rP2ExFzQDl~P_@@1!1gep-AM(b@KQgIgtEl}kSNr2Q zg*`|@J}8F>li zzQX9z$)Y>rdn7JW-9eks@6gje&!=ZVGCOXzXTVC@9y9^*&slhH_ZjL}d~&lCrqTfu zs|G8mN0+B0Z4guiD4JFLiKvLA zgv8Y7jOaACjBDV&0eFnpp4y&XqLouov7%D+Scno}fW4j;6C)dPuk*ByCR@gVj$YR` zWBtAy9K{39cg1T{KSNKw@h=W*^#aBuT8{=Esqbek?#+(aLq` zUGxGJj!tFs3Sy073uV{kA_ZujRGK9eZxF+nYPOt&9pk{4I?vXM*R@K^BuY{fiL`Y z8s`vSK$A}qBKOVdfI?E{*edYqergE)^-E>0ohWIRQr1UPr_MGEaDWL#;0h*zuN@a4f(A&RgV z&d)@fYuDv6SMN)1S6!S*-Q%;l6L)X@G5*_3{P=ehdUL98=bntt&HXh0^KEvxS{<>N zPt}<=?GEiVy)17&+BSbL*9fg~0u!F2#qDl3<@6dU^mFHcvOGX3!lt(jDKS^Aq^I_DDa6 zBP|Ij@fL+vDLB{x7d(U`Er=5&HPA;oP-kCjS>UccQg=AgIyll&?UCAr*@igyH(l#- zX$%VFe2xmRkO1O}YA$RzJJaCW`v_^6EIper>%tF96TM`tM3w(7a|T21?X>&FKJT@6%Oj#6vv+TH%W-p zPl!s4i%%6q3j#CafMek?mY9^$Rv!Q>qK%wxs`HvZ`>sK+Car5w~l z_)I<0J|prHmtI^fS=652*Od(Y6szp`EWyZcJN6V?#}6wNs;eTaXZ%y9Hv$GS_CG9`}oJ zuUvma{2w@=hv%95csiPE>P(Q%;0)>l>AdzG+BJc9{bUM)`SdTrNMRQJ3QqWh5G9HE zImz|Sb=@_CHFnitx9X}n(I6O)-rA8$5R4No7*H}0jB}4$6E}qO8A<@b@TE~&!Cb+n z6xu};b0vWyUNvpEXuDuTkC2_9@ zRU6vkR!qOgt#WDbmBH#2lsrV?c_BiWd3lAN0|lU1Gbtz3#CGo$D-uoNH-Llc$#>}O z3HsC(QLP?e zwrWS{tFArKrK^7Je^>nmoM@zi2cE$0x!IBi2%np+uqCtA0#_Z(mZkQptB;RS2wFAW zMknw#0_m>2jkcX6fRq>7@7s=@GTfww&Z{|k{hkgCaNftk7twBPK$r$x3~-JlQCc4T z&21m*?3+LjaOg#}2fs|1`kMwiMUuLJw5UH?uN>|2>Kz)K9-IL<2G;yXb4j<|=FyH> zv)j@0zuZn9b-2a;S;bKA{D_+5-gj&AZS3c`R2w&%!ob)|BF1}aNbc~FX#yP?r0|g$ zNKfW5EbAROYh{RN7|H`@h}(d^xBVdYed zUhF=Ea`0=!nUbh4j3nqK*i)aB%09~7z{?7PB}dZYMslKm@Ehq7>z$Lb6dTyDtYXv< zp9`N9%_|&RIW|Dzb10siMe*nm!6rr+IBW)0PiS6ZCX~fg$CaJpamq|}G|RAxX+TI> zf{!qZ@D9BmdBcc)j=V6TJCGT&hh8=QIFzy>E_j*X%{E4b{PI9i40==5^*Hlm66I`U z9xsWwZ&dUJe6|^B67z-7|$^2<6|LohF+23rq z5t__RiXicn1;D=8neX(%d&0#rFv%8$E?558H}L5GPQXiA1l;{7dQo{t^^NN87&KOju zE`h>cK}U`O1U`<46@ z5Nf^rVtC0$c`*fnaN4=G+303jT45@TkYS&2&Tq&~iQVoXfIcwTlgav-VDDXxGLa)r zzl*A&MYY-q_n?EQWQ3^+0*o$rX@Czx7?FYf!JKTzJBbV}%$#uP6K;o3h!Ve{(CO`v z!swzcV>3fDy~_THeiakK74wgp-R`B#UzkVx=5CI%-j_=t)?Nw1*2 zm(CjEvzm3Z)@GsHvQ%+;Nk#$54${N^Jm6KOl~xHK_f_IdY0Q@fOk371ne?6k=RV5{ zl+`zI6MY|&vP(9U5|2ecNoUHOIytuC`yvm=lD7%=hSk8by9>vtkbDIH%8W0I1wB2= z;|O`e(A+Rm^CGT=c5V_5I)vXhk|fte7Q~UCpuJ-FSG(6`-^VMTEAYD$d11Nk^lV(A z97(0HNHk*x5MqZf;+2=T1{TJa1c`XRHXzpYC-)%+zlS`CM~4vm4ASw=E0&cP6<|gg zUOliMV(6wZ{L@EF9DzQhF3{*3a0qNp2HJZ8mi$r71Vow_EDUuI-$BPmhZd4Xk?!dC zlX^dlv@6|u=llEs$!@RlU=7-IN!35je)QIVUWR>2-XTxCJkX?%%c-8O|E=;dsd+z^ zsyfiB@< zBI^2|$t=5co13}edES}fz-l)QkgC_25&KSFDmEzxFypA_j<=VJNjUv2^#dP=f9hUqBALqzZ14_ zdJZ;8W0mYjU-3~5P(ur|#93|7$lSh};d9RNyoHk_zA`L7u2NA|Q&C64o+^8OCF3kk zvO!6w@V_hh>P>2BB_a#!`6M%xH)?=7csH2YL@$H-qk(nVmF+$S%Msm`Uzj8^+gfczx zAouISak*C;$3E&v^a(yW0=E2fKDq(yc{fH-+ZbMC@=JMDJo%{zjyOsjE&_+J4l1!w zS;1~c@avEiSA~H?_!PY>M$zc~b+zBUL3l>SL_?Ve9$pkz5KND$tyo~(hjlqKyl;#^ z`}!!nZv+^e=Qn{@HUqEBSUG|YfX!O5jK5$5e!&d|!w&T3n;(Nd>3QQdEhiQtuDO`eD5fEL}_m-aAB~$XC$*%NW*Q_X0glac*@simoET!N>~Nv31v^V4EcTIR8}Io@_}EUw!Fd z68au1Zf`_9v>nF|p*E%F73OtCe04(UiD-@>LJ*oAPGXE(nE0(E?6Due`5Xc13$!C2 zC7_+_N*-g9@ZsKwf`|YfK@^eW0T$&)^d$vrM;k5h-jSt;v4Dp`u_Is#EG!KF95lrD za6Oe8o*JG4-FNw`47}pPznEdiUCa-;!}@nvZp;teJs*~SNB+S~ekaKd0KtI{768ua z>L?AMAPC9;fOBXr+$@C=v4TRGTsSH~8~@3ofAwBw?wF45mDZ`&dYC7wg$n2!<~`$G ztsR}6T>yScM-!JmOq)Zy=y*c!fzO@raW?+l7~qTdQ7-BSt%Tpgd??4MlwVhaZ6E#0PR~>4-!mee7cG0=NHSnZUp-up@Mi>PE96CF9EqpGxD)V z6lX*m7__1^D3Qg({1;)d6d35yfq|@emKI!~AO-@18`qQkg6Y8ET5_grL&^V%3iu?< zbTfXHBLVsczR`u`gU`bn#q+SnfevBfyC`(b0Wg_yyd#7)j@tJ+;XOxq58Z%QM;yW3 zu_smwTcx{{@N*Ho8 zdJvXbRk`GDY;6k3nEm;q7ti#k?i`IA8J>)r)_bgK8uIR$>%BJAGdMEdu9_Gh8)MhR zKVCeh2Fql!3$`T?cO^2}4KF-k1dMmo|A_nLf<*9t@lC4w&*cwE^hz4laJpf8zU8oj z6QlI_%43$qd1@mHe+!kAE`YU92ujcJGx|>otF7{8o@|3OF9*iNffj^?zQyZ_ir;jQ z{%JolMfrql4{;F3?n@mqY4zMX_55U^ad$8Wruv2^|lsQ5oS|0CfXwD!Z% zqrY8-^Y`cRqT=EFt)HL&<)!nFc4 z@Z6JIlSw{HKc zc8*=;_Qq4N`}9Q$W$Sl1;6y||RQZJQbBf2!!_DLepb+kOUlW0%Au@G`$kY@!P|}?0 zEF^`Q@Z~VWlqv!ip?n-0%YiA+gf$H)tOJMDpvql@xq|=LNQ#WWJdH^Ur@%>Ph2_NN zk`5`!$vy#C2QU{MlOS=)25V%!od1IWttfaPb8q07M{kXOKY{E-@mB%vRhq=W{3bNi z2YF@ClyP*it&T7shk3!B?9Vw0j7kwrV2#b!c8=kQuytyfnDMs2S7exrX zz^lOJ0cIlOb({I#cwgPp19_h{L@{;Xq`j>SHCHs%^qk0pW#mTm%V}m@N=j6kQB;~` z`k9+TNENf9$WMCHbE`L033ChU*Uh7kx`?NyD2Bo;@X9QLZLsX+jo7^bMRz~FY>xsw zne#9-$?`mEh#g%hw9fs4A##M-N|yUkLv#UNY;fOVsC%>>S$Ea7g%0Mlx*)byk6%+m zXH#cKXG^awE!Md^Gd@$vvtRU8&YMnGP2Qb9uTm)4|NL>AItn;Hnxp!;7134ooZv!h zxf?9DF?RCtVf8)A74K<*p|h=WIuzLJ3Sv8fr>Q@I=aWu03qW!!m1b$+SZ?Oyh&WCg z;&x(g6Dk77V*K)(gqb4tOAy>T2nZ&mMYm|dX`?H95S+o9NS1(jDEy{yM&{DQv5h_) zeKa0$a@ouLH=`JED12C%>eCR+M0tE^g3LIYBfW|rrRlh0ArtRT!tsak5jwHp<;Ox| zaXJs>p~LH%@0=ifBhq-OfW*C3E(#O4-2{OxLvk1yaxF73B3SJ2Abflsr&BKIjwc>) zMg!jXi34&uOziUA?Q0fM<;)qA->JG?NuDDTF^A=vA&3)3X2p`q`g8#Vg1I^QW$V5s zdaY-#eaHp8I*C|hm48)nt*Tzqs;H~5DdmJlxCi+}@W5Ba6_e%2t?MXIdL^t-?UlKfk5Fy$rtn zf`JZ${`OWF>%pt(ZyRPrplcZqy~=Q}PPne~ zfnh@TS(+hEpH&Fl?gdBWueX`EN#&fN0v$1w&EM9p zt6m#N`^Pk|n4x{JuB!JI%tE;%!@msm$>)aJo=%}k=@ja3Zt_zw)XTs~YYp{}=<89u z^)ExsU&&`kAc+!-z*4`^Os3|iDT>w^hl5w7O*QQ*eYm_xaFv}}Q$06&ddXFK(XLYX zSI+cJ6}ZZ9xJPWg7mOKOv^mq?Z8ek!pV{j3cacD){>SJtlzs~>EFjmB6^qG>xm2@t z&k4n~(Hc%jo6VeC9{`p%P#JGzZp+z@*F|y!$C1872TxYc;vn?$`dg)K$Vj{a=+FzJ zi^x~6$GdXZ!-&R3R2#ejajO&OaY#V&7b)Lo{G_jGc)Fje;#G%NbI)5FyYCMM7?Xh9 zsK-?wx6h7sd$f9#`S*2m0;`-G?HdME?Q<0molC+D@g9S%f3VPgCikkJqlcTH+)wH+ zAy27O3{DKObT2$W87dTW=b`o~)Mocr7#uyz8W?H2UGd0S&WTCYdBadB$%Q{RGC0jY zGy6SftSf^XhB$$;P4o5@WC79OS5#D3oL{0}9$XPn#H)j4Y?)8BpTT==EC72CyBgr= z=@w$3$7iW;J-x}ona2T7>;m2n5Mvwg%6-MLW!$`&@8-Yx?KU*jHF`v3^7SaAh%}2d zvzvKgV$aO5kJ6NxHTh(%I8V{PPMrtIE^rt-5xSALk;l^MXH21Mv%k&$KDPoo$8S<) zyz=leuG!U-ZilreB#8RB^27EiRgHgze{slwfvJ+!>oIk;Jh*X?6C``hz`cOg&;%QV zepUVkx-YS8g`Bucp*Aj_B-(*aLfQKMc>u03N9GU|*qz;V`iKv(n1+dBR8><0y-IR3 z;U)Cgb?)E^D}6ws3>LD3}pSF@^1U2 zs>ZK80G_0s6H(?@?Oio6+6vOTCJ!^*@;_PC{KV|`7`G`4Zla&^jfHC!q!bG3#Q=|9 zR9GMp%SuDb0tzAua!+|@fqjIQpjJyru z<=ZFluATd(@$yJ<%-7%DMJ5esH#y6+#e&yxHr%QgisKa#;uy3O>Hm&OdXSWCVuowB zgyAi|9{gx@h{XeNq-z_*VFJGlE}apUW>(%T&Mhu)>nTP%73!-`c>rn3EFLZ#5s#6& z2fjce`jP?h+ATDC$L7|#+w6$N%*9#?7y{3hIjUety31_*N+&srf2G*L0SBEAUp*1E z5c@d(dz22S{L?z@P@EE=!;VOLeEsW`@5tlKtZmuv33lT3(VScp9rmwiU6B~SShP{G zL;uu){bzPK%VJ8n1-uViZmWRI7B3q*yO7eJ+SFZwK8VTjlg1Pzz7cK7%^;mKJxBe2 zn0Y+>uf`wAr`fnyd-2QR;-7FkqP;F~@vA;;Te|p?kU{$5_j$}%S7_B&x@o}?P?=5w z9w|OUOWDjEAVJ?I?9S~1DbrV67^AA3GI)){(q8S-U_mS`-DZ&PUurE4mWCF=GR$-7 z(ic7#E_{i>rsXVwJO_%!B(lq;;w*x%x_APxexx8tQdlU>mn%Zc0t+HR@1maT9jR+J zhjLZOLK20Jp1Y-hGf$jEE;H9*>~YTS7Wql7BerQ zC^{z@v`~8v3OEPQ0owiqmgmYq3jsce6K5G}=()@E;3+Hp07 zZW$w8JH!Gmkp+Ajz}FmaV@C+FG@m~`{WrV)zw3LqZE~c>kG9+W?VRv3e>mcPt#7&} z_ceUOkUj91Z)*CP*`u}U+LPWwTlHtB8u08??P$XuV&kec>?cO?3EHq0kylqAKHRvdtq%=qdSw7PSE4M^6RBWmvDm%XF3%MpD*$&Hn*pZX7MSRsC)h{v8rUGFF@8;m z_;&6{XcwtzeR}BG5u1Z+Jo3ynezfFJz%@$fBaWbtxT=4&?fET!&EUm@tmc0*d%PxW z%R}o20lR1Gv?GAo=c98_p2x@Od#OFdcL@74yH6T>!M)@PV^!6Y!@VQq>ss%4{@}@_ zdkNn0|2afYa1ehTVyxCd*@PwHC4f@3@Z=2XnoB65a_oYTslY_>z!!3hoqp!JVpcO9VHVE(h9X#BEWI@v4B68_c`dqXIJuiVvS*|Fl zEUweWAkj@toES-PX$Zt1bT<#0cz5p3q8C*CvkqNo&q6bFsOx8u+sxeD=ksjzcyWaq zmI8w@7Y741br*#(K*D1D9>iL<3$Q9<3~jA}zsA%&5YbQ^*sNuqR zsiat5sA%wMbeD%T_HaVWoSWV1a-{+pjLOlvA2yFJ&3_D6FL@GmO_)spYxMk#L|Mh47RnMYIz-H#~!ScFkE$`7$e3UjC=goo8;w{4AFK3nu@y7q- z%)n?A#+AS&r=1yXc{ej@XGTYrTBC6aEN|=y+L_T&CGE`QRYi5hv@@dv?HEZ&S*RTB zFdbFu9zdrqj^7<^Pz8;me@$EkiNsB+br;xN{^FX&^U%Fz2HSW#I=i{n`!;c#NsXtb zgf^F9W!y_wPrDz}I{nSWOjNgZAwEb zS~)Pa5Ro1GxZ<}kd<~&?ng`RC!V(h+ z$8F2$clVn~_!V4nY=xqtqO3;lRpDLdbI$IlhjCbd;)7|AGHoy~iONkZEXXewOX+Yb z|5-S-6wHHW)5i|MyoVRb4&g&Qe`*w;qEG&mIjjoP@%$c>=V64ha$*3&2s)nk1fTa; z7(ttBI*i!J47aAk2#Dv4L!p@D!&e8u=G-}a(j1+H(G+vKQ}25biX!Y!mhuNGheTj0{-Eu=LMt*!#NQGDu8a;GX_1|qMn$T)nO|p8(f%_t z=^(Pi33O#bIW<4^@hyiDcI%Tx&LW`Hwm*@1mf7b+Y2O9!pZFO)d=o!7gFa@mtDoEg zH`x9J?FXpoYEevGz+iB*KK9eNQ)kd>SX9~*h*#jXrulKwpxh|*+J|*$Z5}cs=W!kL zcwzXM5xey}ad&$D`oW?)^h)-I$n3zpScofEq^3mkfO#}&JufA*bOZ1myf;HU?S^3i zZJ5ZtWPSQC)w*A~@Rw@!0%Jv)f0YlokvmWN=}__U|3bw^TD8WXtJWKIs8~oe_|j@6 zmg$#-DuSNHiT+iAOWB$>PP`Oy72-t5caDtWBlj;n%hu>1aj!N=l+Zz9)xbu9=K*V;I7iyD4xLB#bJ)I{ zP<4!rbtoMTbn02xnU#aJA6L$kPpbj7y^IGLH`f9*e#tcEM%{mA8vhEA{Iv3W`kX^sKly^Y{YGMxUUs@Y?{Y@LE z=%D^}B2eG~LA^HrqUH5Gc!KO2w9(5g1oE#C;CuXA1p3?#O27_2Q>s|Vu9uXGcJ}Dz zjI}DF;QPs6!5aEa>LK1F@}B;A{_F%|%(4sT)i)QLDGdkhdk4FnK_-OVTL<4eVE5-s ztoh#gPy4egA=#BddB_uGaGxia=vX$RRuP- zXvrCYjfKRjWufd5$v(+&meVY|>u7IO3x?K11%?*9QLyZt+b-G~M?xWZBN#t~(E>$i zX`uL7c2yk&T0>`~kX`XFhuL|86yA9r!W!t*u(X-gzSGAc;?gQj<=9|1ErcL}*RBf( zpD9f)C{2gfBpNa!t_EtACX_ab=e0rzxM&9vmkgYd0y3+jS_zamgmh*VC*eL50&Uq` zfQMn0s~VZ{t440O5KAmYlCF3M_@n*S$ zBzp}X>*1@^8MWIC<`9a3vN`$KX(=b#EMQ;AC7AuP#zFXfO2eA&MM(~`YJ$MyxPXG+ zorEtF_}JngC3erV%l07g`E%-nx1hm$y)kw$2380@eQNqwmV4@1mJ0=^s)aT>GN7+N zWVv9zAO$1YZ!w-{x^#&U8`5sC=||dt*_J(z4W8w?l4rTD7;^qna8lZ=3}S=-U#|N+ z(FL8*R-EYAfVN>kPOnXL!G zoWO=v7S(~HfkYQRL98V7Z?OT|(u2GN=>v*kq%#~fn$fL?Zp}wWnFz4R5S~t4>-Q-`9XLAzc^eNgAAL{oA-YFihOVW8{)|h^!8D- z(NkDhRFa?nj`YpOJ)N>5nXE|O>o??46$-0cz6$e(D^=N|Y*AL8PY&q!fQC{ram7Xo zT?=BmxmP{^7Y<>c#r&Hq}tdsP55?!e1>K4ap)jF+O3 zB%ijMLgE3ZL3W3H(Rs0n>WWbMRdE}7IR0{rO2;D7K@(2(M@|LQh1K_Ryk%$0Z3@Y6 zPzn|7>lx@A60L}>h^s~gC7kOMxH2@ZBdI5)Umu`>IYXJ&0voXd)=A)GnNRxzs+xdl5rR1-h0|}&Uqf4&`hWWQ^E%X z(!%~S;&P|hS-M(WSoTBu+K?o`fHMfq8`pfiS3B6{-*H;oQ`KHq-Ar7R@25O%?VT)K zgDQe+!y8aT3FrE-64|#73q2#+W4dqls%SA23{y=q%-2QIf9Ai^bIkHBi|kd-*dBH} z?4KD{=#diksO017>1IN^XV77IwX~=b2m{szCxazq5yx*txqGj+u`F)0TibW1qqVjh zc!FJ3Pmi<7EsWlQ*V!l?m(m0&s(TNzk(LB&(Yc=@+R&D@|Ln;H%Mw=%_jao8DP((t zhf>tobeMQ^>Y=!B_;=~AL+)_?u_MTC2y}N+NAHZ1ZI-jKXokWo@mZF%tx;SG0ooIW zr!8e`un()@%hMS#?x9xUE-IKQUjx;Ox3z<{tLQ;E1CEP zG5}2;W~}=)yHFcqFB2XA9dWUrC_+QGAP;T9HzbQbizxAq;wHpYgJuDItT=W8A_!MeT*n>JV0}z4W{-iT&K3 zqVOuuZl60Ux;_OCC1x3Lx2w;CQwTqxgm-HL+#ll_?H1)h$OCcWLfLf5fqtt{>pNe0@qOO0YyNdM5e<6K3E5T>~@t_aD zgPzAk;rw5xPjoT!k5ECEk?ZN^gd%b)iW#ki^y$wsQgILes7e0p*DTW*NBLs3cf__) zGGHV$>FJd?j4GpGDLUNeT7^9Oer&K4lS_(ZX0x@xGu4My(3*0Kqww2eSSKY-)a2_i z7uK^!23T_@u&Q1XSn6NuYiN4j{;W?%xWGLnWTt$gMJ%p&ta8eCZ|i^^4d+_>+D^%x z>EdVhfO%q7W~v|s{<#^)Y5tbk?AU8o=u<7^_~sv&vI9I{18tSytq*liEMzoR=Z~J% zaLM51faB<`kOGb58gmLqcA3ur&n*hbSq+b2CN$2}#iv#_VKvkt)eV1A&k;rj+By)#3y zy;1_Fs{h?L+()GPC;KP)YaGM>I=;yzBP7!^CE%-uUnlS1OFkW`6X-_-M})=(tq%61 zb#P(4$s<01h*i+d)#j&`7p`q+&OmQ_fb?uRrxG(e@f&>?`FXiznVE)troVdpQd3b= zSq@yOXsPt6*hKt5{CE&r^@?C)J;eQOg8WQ0J2%@#`D;6AI$q6*E(^(wtnIqas z>&8;Gmul6rt_&4%LOUJqIrbC9z}}5&4nysDc?qid2!2HfzBmG3^x{0Ftm`xc20P+PmVxcfm1b|Z#e2TW0Q+nu>0(SY| z0I+%kd%mccFY&=DR8=Sc5fhG#pjxr{*rjpA9E0*j@9A-vjLRFYnYGzt@m2x7QT}Iuy2s`u{RpDRdS>$bJYvpDElaM*SsUaX3S%au?I3!Lx zGO^#qhV8oa!nDHFLOnjm`;7Sk4GjYM=;Lus7MWdxat!ek^d@EkOPv6#Y8II@KL8xe z=p&c}D+)=AAjB_H(VIW7>Kejx{j+OWDc6#(C0-MCHTTy5x7VA@NY@DWFc0s1w*t>> z4+lPHE0cI|At9F*herRw4@6toN;BIZS)X$xV z;GW~364q5N>TRNd9p4n!{RT&M3{)~A@}ZU2*wD~h4Zd(0GsxJ-!rjh4-#<4%KT=wIvCI$SRJjX!OP)7o9XWq$(sSIfis#U2&tEhB8 zzITcb4AfkJE3)MYoN_FaT~*f4tsj;>M{7PL_v6K1AXRxH zGXkV^cy_nqtk$Fjr@AEs5mLCF;;a=Z;-E2Q41~68xC*u)5GP z!gy|p>RSL!h1Jseg96?=w*c>@(C}W%ptu6s>wm^68m#%`6;wM=e%jJ~Jykk~D1XUp zcaWGA&(}Kd&DhXa1~~+D*Ixs2bO3`euITIih1c*5d>6Xw+hBMF@ETffNm2eEQk4I6 z<=GE#jiUVbh4No}9*p}tTm$jT*zu0w73w#Lt-#kokZUH7p3AtZB-2yPd|K3$8ZyqY3ff=kq-&GZj5)8aX z33e`^1T+OZ*{4Gzd3-XfYm6Hy}z6%jjgGJfh{y(>AG|tIsF?< z&<-#GoooP1pg>L)4cK|ChQ`+Dx>V8Vg3E%sRB>65aYhKdOZ>sW<{Pj&6124UvtD~~ z#lY2eQQy7ZY02e2WfVAsOeC&jm4hZd(}S0~J0|U=XIx779O4vYVxHp z4N=C`f^rmV%T^<87XB97QW@@<4ry*M1g$5FheUzd^^InJluM9Vh@;9b)fFZ{&31Vm zes^{~g$HC8&;WktkzUy6Z{}gPwEx$+II|XCm%kEK+P910B zTgaHYuTKBQUFx1X{Tp|wN9xC!bCf((0rM358JK9Kj!*vXDO@$37HXq3weI?7Dr#=6~!gK#(Hvsa$uYV{uVUW1DE)^@gPL1Z565Lps>8zrlPN< zRzX+z>J_)OzBQmPu2mVuO@2Pbn{Z7fFMaa;>{ZGV?Qpv?oopiK57#Goev+*_vF&E_|rf04dWTcj) z_J9g(T+Z!P*q@o0o|v8h3bBkx6bHn(CM_&{DNd;#?Q6yR>X3FT*6u!Qiq$NUnj;H+ zjhR$+SZzpcu({hMf348;aDiu1*u#SH^1kLlmwuN!KD9j@$0Fl0eGsNyw7#akv{fJ^ zKJCM8p!=I%npSo(i{o|F?39jK>NQSQR6$5uR7Gi7HE6M;lguz{KPPt&n>?Fb2S6B> zFUI?D8)eUO_p=YM49v;r_+{DWI%TEtQ~AmKsCW*FUWqO7PS8D00tYJ-Mn8I>@rWs_uF!AS<#kfY0mB6%{vERfbgu7lnQ3xWQ{@I1s-3rG}za z#b{^G&*Q|giTkYIw9p5}=v~+Y&ne2v+yM(}WqaxhGJro#Aod&c01gchBg)k&HOx(g zze-X%fwl|fuq8aOSi-^?^7pBpsQw6X_y#ZhuTadq27AvwiJ@p6A8ji_Z?}H#_taM$ z2=Ihi&UwKZfw;V@rncGPCpK)ykn0v_XJln$W?snPn3(OlidVR1L`og9vU7X zTYi;^6x7pVpq^Htsi$|jDu|~Udj`yD$LbGM(aWCc9>6M>k&2dd3@y_d8dGRyKVwZP zxT4i9bX`Nr1IxUN^RjXkT+!kE@@Rde!@O*x=6u$V&&%;jj_AyzxuW@zX$r3BeHLc} z(jqco(tE7zX=h*C0#~%|g$?KTdZdSE`X&P{PG8a|)h`DBK@=UlI?R_QiiWA(d7|ii znkc$}CW>yTayM4&_tPGY9Z0dWx(|;Z@4NV>UYop?Xqy zmHl+}lm}T?c4!iKmH#bqDqe%f;8h|G1I?Wk;4v+o_)zZzKA;Et;P0`l&Yw7Y7QzC= z@X($Lv9N{qC_X9w&svLwu;6XsYUh{dm*+1abRpY@T5FliV&L=1)B)w^gTIgH44_>j z+EQ3re1q&{puSltmE)7^mI+*CS@>5brf%R9`uHvE#+4#66&awlt5BybU;ult!mE5* zd@70eXqF}Aqn94Dd@$P_ueo}XAX^!!P)8*P*7R3DFZ~L3p$BB!SUL6r+f={GUQT3z z-96tXqLT%G;?RH|Qf8v}R$^tfOVcb*yv7V~ynKovI~edY2Jpig`f5KddqK#VQW=+3 zZeD7h?c7nz@#!}H)Ml7~S>*rno(>$Lzl9Mpr2_F5N4!RRN01jMGrY(>HB3;PQ<_zt zy)%Dr`BAH#`fBQCIpO)<{NT}!-m#{~b;sMz5113_xy7(Ks?}wB&+dyhw6q+p%Rzex z;Up-oW%lqzseObgblrYrhGTXBX1Kra{b|sb5#K>cw_Ru|dH|cE>n;wPb|Xe75MOQO zF?if&WwdB2b>=@KsA+^OZbvE5pt8LBq2ZZ%FpT%;&R3Fwe!?Es>veVB)7x*S7(L*D zdP`nS;j2L|Hl9JTtlE;QvQij=S-yM*TT_iOEm3VzY9WUskKG_h)=h8(eo0}!q@hKE zuPeXud+sw3SkuZe$vK;QN+2oiuBmBpC~)E8Iwp9PwA3`OR1Okl<%VR3q=%_=S9sK2 z>^7`bx$2d}6GY^MtKiD;@2b9S{HjaEwB94cJ=85s1@FApl-pQvr$oh|($w~>qkptY zV07^Hkl57lG@nFXR#IUCT9$O#&pg;S#wy%7$}=`4jPDiC%YoncDTxT@g+>NP+WMON zTZE=Z=KG~Y)@L>5)fa%oACuHw?&5C~bTv#XOEXiCPi(^RlpbDf2*9`P1?>x$>3|uO z77m0A1C4_OK0o5bt#fI$&I_o9;UR&B)Cn*Mwg|6)b=87%sOc6g_^KG8H&`|ow9gwl z!@CIrJginU*fd1{pMiz@&e?u#iE~<(ATKlRe3kZy{WyUrA>K2@#9cs_lO6#b^iV?1 z!Rwf(qIPMXS(6RA;IQ}e;88YWeY)!TY+N5ktgyVmqL5tZMs}O_b+ou3Gt6%3?yB#I zN{dcK*SfBD-W^f?b?QGO71FT07o#7K@T5)SWS#lcYzIoENWBfZSRi8a9B#*1!LlIV zHz9QzXflW-IT6_)ODm6K4`xgA z#Ko@?*!Jili>!H4(7+n<9SoL+rsn46=VanHi|~qS%pA$d;v`KS_vl(7#eyZosvgJ!z zQ6)jOK)T^QM^UB_^jR0WDE#k{pOLl=EBYo-Uq;<>zwHzUI+}s70n=G?J1u{0L0WQF z#VXi4#R+&V(740@k6FWgfr_ZE!q(!3@?N)IpWXms*}3~@2^1J{;7ie4j!q_nqS+ zQz$^_FR-cV^5<-1`V9W{z&S3o+!%95CMeBe)(!kt7+zzvksyY{d=UpZ6 ziZj2!1htaMcFogSVYCabt}kyY?I7g55#WOaK5sANgt@Hn8#j3l?5lD7z%+MY>lkXF z>*|t>l!CfdvTWSK%Bst(&j1m^j%i%V`tI(a`u9wH5~ot% zp(Q(5q(117pT^_Vmk0MS(T>lk&kugc#5<4&_2nV@gHNf?4}A!H3+(<+d!F=vEbW}} zmnc6SN*$^lDjF7y{CjY6;)PWAC-V0SG$o-LoVzW`-8))2Zk1-uR#gN(dXuuXJ8ftN z(rcg|9o}`Z)Pq#HjdsNH5ho(8zgnDY7{$) zdnGaOU&9gQCz1?lp)_BbI{4+pfI|{I({= zSmcIVJakbVzlT4D0mF~+*1N!_SIU46{d+IrDB)@B!)esZAHi<-g59ctWZ#R!yO?O_ z6YBZleN4O)IZ`i<&>uXeo*&uATy|J$)q*w-JRUpW`&jHYUN7+<6HAmokt7cm31)rRp3z;lSEjIG)w!?Y8p==3sNdz``LTj5e$`II!+^ z0tsN6LB4eeeK~;Yq1+W%S4r~UV(-%Bzi${&m3`l>~xkYyo9 zG146%e{Y}FL!VG~M(V~F++ZONP}hl?`+coLM1>Y*bbhnmPPYtR7L4q^tp8<5`jDtF zq%^nS^%wAA&y284-;@w^rXFpXx`$XGnJcTI%KU4ba(rr=Il-llRbH@TR_*|w)y{tw zRO(a(pezmpRg*fa4&7p_mR62elZDt8#I4pQP+k^Bde#n5)^;#M|&)E;?EV@Lf5iikf^s`V<`78{Zpj zKdEWNL*GwJ&cNF}gj1;R(Hq+p_J0^+&hvwC z?(05N{EF8hH|qHz`h!muzb?B!9ym}XzSdhOiWXN5gh-NwEs_+W^234nfqY5kK)Uo^ zzw_|OK*R)3JTY!3AsGc#vbxzI>Z^6TnfmBM3OgW6AYgfEM18YvC-d_BCwB13-1~EG zRE=Y+Nr`J)E61bCs>2@M_=X#lvyF}c9A_9bio#nO2ZbHLT3@B5kLp2p3ZB<-4Q!za z967ruV35##9J^0JtLbAF{*5M+V;tGYMe2{Ck9%Yb7pudoLX4yIN1wn)N4Ie07BUTL z`=0ME?Kr6S*{fLY*7xD%w$X3*OL)8b z6axarH1)~hqmV*9p`IKDq6;qkDW4pnKX^<%IdYh}?8LoulG&w_k{Nq|$TG$*jKOl= z!1OPZq>D)QNcIzTZDRm_+wM%i8>i_>9-@+AML}KxOFQK*bym4vZFQXB5{D*t=%6%- zWUFP-)M*6zWyOJb7^Vt$tO2284}K_eM+ZvKXGy4o_=OoI_l(3RJsgP8sHmGEs{^jy zPBd8y6gf_De(kZHw<;4W6Z6yZs%m@F(8)Ae4H1`qyXy8T(oH14IjFiLj*XyvJn@h@ zkkXghnhqnCB5ipf72@h);p6O^1621~z>jj+=9rDXcey?&<^HS$R6HQn8g*+;v?naf zL!z4Qoft$kSH{7@d;vd6)#x+!)!M5yrB&t4Zj2~Nz1h1>oc*OK{xJ` zL84#K?f>g1myxnmYVO6%eRMsipJ|BXTo@O1-=T*;8XFWad2 z89tEUBY*u2k&OTEKLe_nMnTg75-I!y*<%+*WgBP6$G^khzD#j%?R7AdoLt1OZ__1dikXOdAIc7O!wrV zvAUUI;TRC)rLoQ%?J_&&l^&GkksS1};`6=&L)c z;T_xr)t5CiX@V=fgABzxsG#3LAwN-2xeB-w`>mwrHEwl19M58l+AHuVDw@j_j{@EA8zcc^TIj+?pi7Gw(H=oCn$Diq96_8B@>kcd;CxG6Yprt>c2Vry=v6_WUt#40 zKS%3XX77FU{$cZ+@B*K7Aam2-_n`Ow=fdv@TsV!2S>3y*_D-6l=N1*`WU6%@{mT4H z0(KdnWBrH7WemFjk@0f$<{1{b4|esitroI9!NP2y{33&DKs->Bi!MH)5a+Nu@D3nX z3N1TAe*~_Da|Ay6Shkw_$RBSD!~ars{EDr9Nym*JoCP;AR5w1-JqjEl=`0DS?$h}E5$x=6TZ103|f^Ch$F!p z;3kqqX|z3x)O2WjbaB7x?c^`upj8*}3Dzk)4F?mSasy6J*+9)e4eVXf6%*WqH*Jxr z^Ec517MVv|Bz+U_u-a@pEXzFVyEuM@PF40*rIl4}6-~5xdW@khW9Xep^yb7ZiDW_x zt|D|;XBe4Db;lBi|2^`}*!fY?-vAZ~sS?*}xQy01PDrr}eHmonTQW5p3Qr9;p;-G- zG>-S7chH4N3~e-!xlo3A;CT539L>ZhqH){@SOd!Da^G~o;r47ALu45`l8$x|XfJ+; zNv>LncLHh%oQ}Z%&OEmS?Zj_h-~tnKbNwpPE_PgAM4>Gx7H?t6U1mF}qyBh97-mHS z?F_J`bj1r3dJ_o^dN=KTmc?a_OPAnEGToDcq;;bso%EIPS(l6snw^F#$$~4Hs`ywm zBMFDdG5MpXj(!nkC?P=YVmvI*Z{-U}M!cci*55T**5UyyhXZKe;!p8Lot< zrbk97W1Q>Eo zWFf)>t+aXQ*4E6?E8JhUuc$n?ssPlC=Z*j|!xaQ1LTZcOcgB>*hOlg2UO;*{BJ$9d z#+U7cQ7H>EE@4e;e84|DJR5RAw6PHFx`S9k#JP;NxuY!ssX2gWWZ;cCczYp;+?M8W zTr!n_yoz)|+pa!#hPS!6sadOJL)GR|6;#q8U)u=`Wyxp>|9PfCGY4<=!IeDh8=K5a z4TuXTzWym8@#R`r1V1Bj8wGA*v}iOSe@u!PgSL{6F=G)g4fX{_FM_D)xX;+ru`ec_ zr^o}*JlPkr7Zi%b3>?YS!qaRS6ZX;N>S!gUB?|@G@*m~(3$71BOE9=TPY5kF#T3*& zq?dsjejCVxknT#D6% z9)UZnmT-Ktk9L4N6a3}QO6J|!9)&yGAi+Dqc2b0?E$L`E`x12F=CcdGT<~V+72fR3 zU*1gePj5!Y6-9p0qKGRC-i&0m+jd%(xz~4tH**AUR#sKnQr-X>T?1wklr%UpBprCz zXDAsISpfNA1ntedCQ;0k=LC5)Q}zs$D^Sb{9K%GGOHj;7_~I;jm%=l$x6ll0u0`Gp zj;_WUL~!Y&S2#K}jkZzR-?Kc=*<3KcniU0@OJs3LQB^@LG0C*ue&NHDx|z{Mu!!|& za^}ZY#3ZJeu9(o@qINDjsyHYO#&4TWfBbL|L7z*75Hln3F`i~xcRvg<<$bdEsKfqvQy5+fmzV;V z*cC5I5Yo;McA55o^E-1%S8<8p{A$NWyXKvrE;v8+1?M+i@rh_e(&2XBu*ypjkB)VM z^UL9Q)A6Vc;!z3h{CsjhR9xV@61@H)Q=BYJYXRqHxlVT>9{D&0(D8_Ney8ou&O5)Z zGVy}*Q)Gy=^P@AwwAYzI-rD^6ctk=xYKI%F?dJGHJaVWmt*UILou9fGfeewAcyK6v zFa(mVpdsBs^6LjEmMZnCvCr{qt>pw4I@NfU5HfwZG(k|lpCHi0QQy-bC?h=E59mWT zh|tF0M$kdK0uE^1Z{eorz=mCpb|%M3Q~mTt)2v7UT0%!^dmEcu!CNuCEgWrZ9SuaB zGCV@%1sD1Y0?t|;_4o*2@Jw&`b_N|t9g;a4`+WM_?dIm>XBA`=T+HNLJ-qV(bRsf% z0>6~d&l{#Y`=RtxVR{BxZU^T4_qTb`~evIl?u}ogi1@py$*Hf4nLX zFNsPDN%f5n?MUcO?4sq4&uD`}yN%I#Lj#X&p1_wMBCHt_w}=S*Cd@|@tIDY2aJt-I)x(-%nsXLr?H1#XF#OWy?s2->VSp!z&%^MLsn_KbL zYf>TL0emf9fUi?>YFAlud#(GfRJzo_eqy0RrCn7;Wp(3xGUzpdwoQQC$Epdy$<-uQ zExD2qNJFCOj8J3pEHbM{F4!K~pPK+e8s2}G&rVR=9q}12yg4idNcG}F&^A8ODnUw( zFuAsgDljh4NVApz#cQ+QgfDFKXc9aeVn&sim1**ABqbbPlgFrMCm~}rQ2{!3AG#a% zcyX$PcRs0r{hLo=q_-5zKgUbqd7TtV=QlulB=?zYL6de9vRqaW7rUHe0?Q2#ZNsZx z$=P@#{HrBs0&j$Raq<-~E^U9oMej26xFD&wiD2M&(c8?fg5HvrViOUE{`=UupLe+A z1x!|!3=P?7I-T4KZkbLe|K@{Vr<1h*-3RF;B=-N4PR{$^zonD&<_pWeJ`3sOY6;%_ zI-Nu!_TREe@V_c_HmPp+H~%}IO~&4g#0}hqtYyBmTQJ;rSnW1{x!!8Ks`9GJw(>?g zkqjCP%|>kLFXGVb7s4k(4@uXcS>M$GpJHX?GpsX&jtvZo;=~>eb|a)|>hjMN`6PYL zihR*UZY`Wd^2%->Ir=t?sQf zs7Oq9Ydro#o7Qwd@gIHP6l9=P`o$>sFJ}ux5alQ zbkQO9s2&t8R+lvwQq$xhaqaMMJ9ua1OqRa!QPZ=E>rDz8uec|Y479mT8&`T)4M4SU zs{N`WH5DlRz-o0Fpi@&=`w;9Kw1(7lodjmtNYH_N#Oh7%PHW`LpR&yq9vae9XMg&R zwed-ohQm1rbKg=!4*K9rB}KUqt+?QHZAyVb6V$V(OTd*L-%__<^nHHW? z{qQgwI(q~|u2Bx-IkKbua(|fJ-*pYIxV&o(siw@xx?UQ>k5t_Nv|I4K(nHeSAU7o6 zQWB}|-2pQ}B8GfJG<7@xuL;F0I9{435so(=j#mR8)Pw1BYr~6%2JVpJdn5;nY9vy? z1hH}kOW*XU`8n_O5V}SitGL%A>F;!ZXjn~W{*b-2oBw&dEzW%wMZOKa9AClyL0dT9 z%@Vw8KKQk!qYdoyavqe$URJ&?etC-6canA1QOm*Hzf7OgSt_a$RY9+vMo0c;J%2x3 zlGS40VqNSBWmrJIYlSo9{1r{5U_BRSUi}voM5}p>&Kaxzso}d~6f0K#O4{D+)@wY@ zo7Cwo5e6Vadk8Ilh#aQL#8{E)(Q9mZ29Sa0m}Y9lTY=o)&8D!i;4b2ojGL&cLb73i zV;!R}Nr_ZD&4{B#A^V9gkd8IVJ(pxDOygW@4H^&ZB}7S7safgyRBMp{{H52hJ9H1f z37|p^JU{X_?)Tvo35%WzV8)KcW*|oZu%gWa)I%+6!a0Jb{PA1}GbLgQ=3!&sNBh#_2RZm$q|$wKXc!;5 ziw>b#`4&8jj;n(-;{-Z-*WnLfm1D$khbAN_Orre7#AJKasQc8#{{EqossBu%B{O9H zE~GWPkIFaC)lRZ$2N~_=V5wgxL0(VKmXA?cx~V5_nT~>#c~9hnPzf=W2pgSwr<2Tw zvN_i}0%l;Na{^}H%}Ouwt%036vdL@M6WUNmovAQgk7KT8gYKNF-r~msARWIYdqnXp z{LckjH-vF=8;S<=dkJ)QfRgu3$og1l$yd~zkQE$rJ$RnDPbZ!hO-T&;^j24VWd>2f z&nW0vHQNh1RXZtCdmGwv7m|F7Cy}R2rPm@pDKu4H;)5?W{N{2m6F8 zP0?!+1{25v{UrCsKOyUL*jO7GznbBmVlBhPZ2GxO4_%*5?MHS~&SK?{rR_~#<6#rJ zy_KR0Ag*~pQlsd-Sv^W%nt49XvI$;EbNDE{67tsO6!{BK4NX2Bd!;Xl({T+R$eLdI zfe6keS%~B4WJS(f&SDjc^3kr?Z2MS!qeg5cPMDn z=`KfSjxn)WWFnl+{*-?_9nDCk2k7t+K70=yMl$(UEJMfPNRE&H@yA_gl`iMbpUl1f zuckj0D?gF8HGA}!j77k~lnbkdWUD90LR@s_kvHHXp1^{SYgyl`B%u7G7NX9&TvPHo^VSXNI0m1mUmH_H+d{fV??X{9V! z)`&YC-1h(+z`?z?EXBdmRuyF)2nSak&dF`cqpb?rq0N*RUUe0}d;Xy6($(yWmeSrr zAa_k>l8M)NQS#jpc)O=F5gx_68NAxx_;%9ewHbjauJQiDo4vQH5~>pOQ}fE}I?~Xd zbeUZl8+S5udb0b{p^x20%gEqZ7;HTTKPP$vde3h#am4(IZv*A%({SV_3UlSgD$Mmz zI{Hvyu4m~pKQx37-2-!#$v457AA>XhrbpP>(~h>b|GR$%BErRK#G1*!hb-@ruS952 zrcLF06xpVE!61GVAYFcdHukIx^>nxL_i7B_6c&}&mekU_ocJeTjpkdY8YVc;z`0jN z3L}~T4vV4kZSxINobKjwqHFww{?PMX6-2ez^jk1eqxTE7|A!8 z{8y<*zEg-^cH);ks1nq(sv~N6HPN$TQc!Yq))Y!G*#4A6X+bX%B9J@)tjI#x1*N} zR^m12ePn86DO_PgZYIHN$@4S~MGPn&HH=Dx2 z&#REPQ+Ua=qwjrfz3pJV#yMw`EPK*8*V}@|z`f(3I4aD*3w*iAhf@f?JO|d~Z028_ zdYEMu?Z6#a zGq<=nC*#EAF|_guB0&;v3frOmNNozMp+mr^jt^;|LwG+n(?F+~*7z+oyz;n4R8`2G z;0gjaf+QPBU0VnffDy)0zvI|-Ozc1h49E}AZg_zgq7s0YY(Km};H}@wNAG3C4RNqK z9WeF{<9&Al&YhL7fPk?N0tOr>J~D+i-90^J4JSG_eL;+zr|jTJ2ih7uyG=(U$1Zgj z_tr~=Gw9|$(!k177L!Y9$SkH+T1%6s9c4)_s$2>pk6|lL>1cR8KpS((hoI2=+i*pe}lOjcz@(&3|j8=;X@!r zw)1}S_2Dbb9p?g%l9H7g&=AM&=Ym6?=AzTO(obP# z^WeyT9(vD|-2LLg(+M&%O;sg@En70HK^=mbi4q*njLTG-X5pDblrGR|xI1dQ;$^|qOHqIn2YiaoN#)?8%1(Yu@%;RpLw&1*I2*o2x8K{$ zC5a`ud3nY86@+~A%6!O}()`;&Mxn^BA~2s=rZxKGe9F}E2jeVAu!Avy3^=4tL4)Y+ z;as?U30!_p+~}q5%3go8NLM;M`BL)3gx?5x%R3Nl{nM!aym0XAOBsqQ7bbCN#~!Un znQ|c_CR!?PI(Q*vDzeVkPjToKaH8w|`g~ySGPY5DGI)oP=&yinX?WyqldQBnPal3H!vw}P59sb&YB#-n^;QC-(kM_54 z!6fwajiOSU*3xTyOL>RTBQ#;it>~&8%oka_y2(Dr1*u2Xi=AxPVmG9Iz=qpk>d0D6 z{)g-eg_LkC?5-Z5-_~LryN!zj7T(qpXn}&o=_@R5B~UiMk%IQ8qh0Co3l=xq!M+RT zJu6p%mvsnU*6Gnd{+Lum*^axGU~qBMk$fQVw4ZVkTeKb;)<^PHfA`cvP6J zkD;e~k2|NVuBbP=l}M8k7L=jKqVGc)mr7dv_iO<{w)Es)@0iRH0X~U7A;C2`94H`;H$JO<&8_QC_?3cV9Ua z_@8j}Mhsd(pv)mCx+oLHdXBb!{fu zzlbLrWb*O;T)d$QFEZ7*taJWKUU;EjMyRN%tEaV}*mtMLczpGANe9}Iu{J2hOAw;U z`HpSr(DU4V?buH}h+U7~Cgw(=v7TnEL>^6J(6h;BvyfU=TssHbg(>wS%}%V@0bDE| z^0UBvr+o!$nIkP0c{+)q&%zXl!jS)pW1!WT#H#^nhi(gSOp|-LGiV9L^Yph0_N)ov z2ucd-@@f>}9Dgv3)+>zRuDEv!t$>`f&j2YqqQ#rruSz3^^}F&rf_?)-P#)DAYaB-> zrKC=j@vP-+Hr3-KGA^>~Zszzj8FZTf**?U4`76AH%7SRD%_SJ3zC#??hR;WS#=YL0`~}-M#i&8&6}Ax!!KX5u zlLhW5TSSCID_95!zoIRFMz1*;x2wCQyYT_+I+qNfWqrM! z%IMf=`E(t`uF0uiCj9P_ezovNwosTqh4?JAy_zDUR^WaX@6{8w>22fqwBU4~gy5N5 z?Q!L4L^(fAP`3(+S@PDodez;ZvB{+cEinIB)!;REr*iG#_+1#Dx0&a#a4PZ*vB6gJ;KJxp3L z(bL;UU}_q0i5{m(voha?9*$4WN4-Xsz_8Yb&1aZnse|cVAhG1Dg+IryD0~KWnBr+G zG;Jz>&+NZ%cggp3qo*zSX*U`|XIS#zY(Z#x8!a&1_XCBlZNt}?%T5o!s$GbJ@c3*; z+lIx17UCxZUr8;*%6Annu+CE&F{0m<*BSKNW%1a!J^-plUyu1cOoc+Fvu`X@}jd$UDJdx6&^#8(v`N zpD$41unXZ$bwM`N?5l9tqIrj{c$9g73wR3q?PI_57+S^Lp z$ce4UUxnXxRYLmmJzPYN$^m*Mk;+-vJg^A}42fO?;NT`kMsSv6axft~DBDkw*Y+f< zxwx6wT?Ji@V_LxFd@9F(bWP?U5NP!Ysu3%BZBUXIKTLJwO1A%vL=flP&JtFPw+fqx zxkoT4s(7<#8@yhHugA9Z>)o`7G5>tkA`>D1qCQy8_%Q`-Nk{Ad@AoTPc)t^!t+0JJ z4c&9g){>t3j;>zY9?!o_WbM7QDYW;l?R2mpqt$YV{K7~LsgF_K_Li2W zJ`y{QsG_O8yqS2BN@Y1@n8v$KLRwlG-WdwJOH?>j=2T&t;Wm-S39EAJ@C3kq3($@@ z4H{QEk5qD8t1osN*MJ{Ui@`~=j90VV3uR$1*5spgw+mL^D=2tIlhb;phNcncZp1{| zyU$%_n?=(~b}UH#)YhTP=FF&9Pr2c@>~5RHrG=*j(NaT;)-4}xzMTr{>!~T(<*R^P zjd6(RN2Hu$M&*Z;gypM;aQI>TpwwVOz7mtu9|J6@yL{H%h$;WRl_LLhP0d#4jG>yN zAi69tBdRgKsjMDKqT{ep<>F=M=P&RS_+(sl=H&AVL-Rv5J&p$+c?3-c@wZQoh0bzl=`#_EwNbz#lh&!N3gmo+S)< zj~12hU*M79PweB8o0J$|p|IVX;I|er>I5Lk3%ar@=w1R9;qT!TCeSz zom~vuRsJo7%HG3eO!UE$7pQC(3x9)vvT5IBBPw&8k?mpG$j(qcuC|s=PN5aym64Ui z7n<_TuNHHiZrjB{XTS@jE&6KXU^}5OJw;Hv3eLNA-=Nw(+jK#GNp{A?GDDHoXk%4p zTN4=8P$BgL%-q~bg1nPOk8vy*mlbVX$_we!afNNM8wJUvCZ@3GY@RdHv_IJ`q8KX>We8?&mbLB?GO` zpp*Q45lZ*ara`>vKH83c$8!8T+H#V$3sOVSRw?)i$8LsEcepjhSI}kulnnqIo%+WN z<_w1pm5P0#-+q6;4+g6a<6<_>mPA6?aOSf%@HUyUvHF=w3f8T|Ate{INyR>vzM1 z(Uy~nUOzwb;R7jR)*=qk$Bd%;{2{?Hiax*V?L;S7GNd)Y>SF6beZ3?;Cpjy(c)rgM z%I&{0`2}h45BP14ni}2gpZgd+roM+QSS=G_`u*8UA ze4m0gzAioX(51(QVZ7lk$b4~b0*(YW^ds2NMs=nFGC48)d?*EO(Xa2$uwQSh*jF?- zJU-wCJ`L<=g=3v(dpzW=kT`%(8y-Q6r+{*UWSoImxO(2Lp_lCWoPftjn$HQk{_fJC zii5(LP6}E=#)Et4OUbhp7$!S+2{Jot4P zC!ou?XLF~ii!%N&4t$Obhp^!^I*Uf}*+JwsjNP8Irc=k$MOnsuoLye{co1Gs&%OJ^ z2M;6+B@QJMN3q34WT6AQ9i!;v8LX|1wD+;_Vo-5Li;r)EckqZp%;WpGKnV_@#Nre5 zM-Snn6A(BS$8BQBe?;HC&TQv%14U-5%uxG#YRis0P3H!G=C%q4=DFa&euKl`3=Zs1 z3(^^|5jPEZ4Ci$4#k{t@4$(mT{OKz@)O)sBNa(tOPTchS(VJ2r9`zr-_<-d5k~~!- zKAU7vU?cotD%+F~xD~y_%wW_jDg`xo84IO?8si+J>u^Z7-5^_d$GPm`Cg9+deiym?X8It<2FHi=#NH?w!;42 zyn&*=TZ1=66@!94lCh0(Q}&K& z^&2D#ix}_V#p_vfDhS$me|y|DiXadML4bBRx9B3Iopv}hY(z9AP+3N_!)Z_B+-M6K z3l$R0i4X<;1{?XokKFyg0d{f~T4Aww2Y0O%SbV+$dN*!m?~0H-gLiMt+!r z`DB*K$>Zu8_F3V1p8Vj6mWK~TPk;@35M{7LhaVjpy{fvD#nx&#F>O2z+*f1Re+1bh z7&0c*7U^OqY=0TqLo0w>k+yLic0v|fEP0E}oB9UF?qs61a6{ih`xLnIm#8tYnZjVU zfx&zPKK<(yq{>I{W~h*xc#139L|4i+Y;fpH(oQkZdT{7pfX%E~aOj6cQ{ByX&3nzo z4MKlFLN9-2B<$~tp1OiYjWH-m2C`yX>7AND#|0!GTgH=gp*w& zs29``bC*$y0=b|)(mE)d=*S&07OJ?kbMsyaqJ$#vr7OoAy5C6Wy>!PSNJz9LKu?zL zH+I0pRkOFE-`MS^fckvr;f#5MV-Vnv=pX@DpTZwKMf)kU9os*^N|!Pt@}b0QD7aHx z3&aqNHf?^ScaNXXAsrcvBz$--fkMd| zI9dLHjgmE45G$5axA0vhX%$NL7(zg2zIe-uZTL5I^OZXH7T&C6%td;D=fYO)W@E1W zXW3p@1Sc0=SB8kah4sHM$O^_f2HFJ%sHSj-O4nbz!*vR<^+Wo4X&{8oc7KJ7rGXG_ z@lS(#(nPxOkcJKq59tgVizfyy51>WjtMdsLU4FD*l|+C$gkbTrL0_fVt?!|DsQ;6G z*ZzQflCc5u1gJ#*v?cmlUI%V9M#4ft5(|w@2C<3JogCMGZ6>-;1uW{%y8e1==s4t6 zt%(ajYzGa*&gWGMces#O(e982VtLyZ2fmIL;12&POJqyhskyLd)|HsF_O-KTVc2)& zCwTEuChkXvL3J&OVTrxb%2O;-#A7t#J@Rd6k2&+h0^d(T7>>N1zj`vK{PX0sp^3my z#3#<)hzWIG{XS4x6foZT1?`n>!Fy@j*v5eA4CKSatK=Z%wkeb0`!i;4Vxp5v(BhwA zP!0bgd!KrU7oA`}#F!$_Fo^=p+6v|GODVM#Eqck62O#iCaMv13;T{)imVY=>m}_|L zNR>#o#tH_2zEHKygT4w+s>r+)pzlv`lXTru+g9CBT|ZTSr=#Icbxl=mWxcff3drrL z-Mu;r?QxKqF?>L>R-l_8xF$<+m8USBm&j6F;8&87jP}9TO!D89n>>Z}gZxJ-%RAF4 z$!{W^bG<0EIt*~vRS{IaPl;2y|9CbBn6+#CK=ww3N40o~>`Q$nia2hi2K81&gj^O! zc{*EoScQy2t2Dp0wgQIt%%>@f|GwQO-&2jAHsGh#u&cKim43l&3~3H;j7rPSE6z?o zoVBm)d>w%tJLL7ujEoGZ#0hd|gN#C9s;6h!I1K4B!{I+65d_Y=0&H7)PN&@V0d-8S zPbvTivYL*pmh46Wu@B|C;JbF{t_$lssF3n<^9lq-Fi4vlkmr*cL>z%7tDNxMi2Mjb zzKNNak((jNBw#rpkaE@1zi`n?0J4i&plQ8lLRH6tos*JHART!#rE!W?kQSBaUmPe< zK_^!J!sG?`y9Wml`b_L1qH;oj7AKh4KJ_gVHLv_yV=EH`J94u@9#3;bPhntLLya)K z4c2va(64X+>??kSlEnP`6HtqZYFNO*GhxA)sw0sXd%JBBY8 zv*d5VL;eOY+R6Nee#l!E>tfa>c*j3WsZD6n&rJD#;N>Is*;4@e&xe7bHBPB;HX6Erw=-MTh3S~KX||IOgY!1ujAgDDb@@xpbwodEyM9Nu=4<7;vZ zZ`QwxHXmWVTh9LnER0?O`yr>4)sG8d`!)G7w|;1|zFEp``)+m_7O!;AnN zQdjE!^6it*^sIKGc*Hq9NUnk2hmo_Wg02+^# zqCW%&8X1(9n3j>4O~nTr4m7SHG;YyFI8f~cE$sg)uBdI7FbA3r2U-aSdW0o!mbJox zZq`%uEgZRvWp;1?84r(+B@X>CerWikKIs^wpwPYvw0_ii`0J7J;isd{V{uAY)k#2a z6cjOD1E*pkHr)^9KWn^bA9_5FEs!-Lp^lFhTAom_Au60dhUX(QJ4iyX_d{>UdXnQ> zLg;SlD1fkK~ zW3~V1lRWwJNmfn~_@%ig;o?R6(c>|A67JlSykvNiH|Cy%WrtaFPqGEpt~O^Xm00{j>u_BuqTABo?~_Z<*$%iD`T%7seM=5g9`8_C8NlkiuUf;)Xn z_zhj$*rJ_XfnYLty?Es<*7f`j@ntcENASOBya*Nekeq?%cHu_Q=#8w+*_UF`^OY#; zeD&fdYRx|im&E2qF>BiQrl%ykEXIi}&N88GH)=z%xUCg@#X)lzOXsp?-|?iG7F(m; z=AQTfsr$a2=G+-|@W!sK_bK`hQ10#@>@ zr%|{YU?zTT(t*M~NHg1O?XCW;?M(qqZ7n{nS6X~qn_A1e1f$pc{CY=6U3z_b3?IXC_(UrOq%^73<^gwKYhd1Yr7!_ZnL}G#xcTt!~Q5jt*`@ zk#lb_G&Y&^;pbU77frAIrx))b8Dz~o*i%BR5DIm>@J`wVS*(Rwy>lrBkQXc12+zNe zKn+|zFHtYii1Q$={yloG7eDv#Ne`B}{;~e!Es;{CqE%mw?-d0;3pR< z!wcg+>aO}k`V;j9P8iqiXK|e0A@FXU^*+59bPnQZR$BAkP*c?Wc-paAm-j0sbgHD6j+Ty{S)wgBzIRnT{Y3G7F{U;zOUc09wq^Gm@ z^wsu0kG}sfzy&d*oOvt!0pDciRr+h2w^&{#UvGO02DVJT-u@QL1HC}&*Pw&CH6(7{ zL+b%-b^$EWMeI$#?PlYZ&Yc-7N6~8c?N)Gk0U=K>6UtJlYcL^XGQKGHDlY8N!NM!H z@lqR1>NqS^^Rk|UNxf4q5;NIMBbYzV?L&k3rHA0tIuQpu#$HC$F}}^4+cND ze`f$36~d9=-n~0$^YHMdEsmeqY4@vy<>=+HLb^Zi&Ej&6ks#~iEgzkrL<4Joo`6i6V12pWL z0A%nwdd;)l8#amfbW~iby;OU#`f_1M2wLXd%I_S!=7!enZ(8uv4fpANBM)wR-z=R* ztm#YBB~zKVk3xc{2Liu`kQqgfgYje536r^>9Ksr|Z-C!GylqTu&fv$8U$EBN*uqd` zvMvZEle5n;-Vnj`A*@0BVfvpiW)nE+_^orS@Z@8*K`@?LksudRw;GEk>lYO~g+F)> z{`Q5Nx<$62jKQ!I3su%XlDbdvhxC5EvG>2plTportEaV57S}IEm-%Z>lvto7XK+2) z$ypxf-Esi&Dh`TgQ>|Hrl}joL3T5?+`VqGkbH|ub1V;iNM2_?^zjXna43(u72?~Dhpu#XS4{>;*vqET{(iiFdgpGu z`7qkN8^3YnHtR%kxo_=>DXycn*2J2#I2-ToV(w`M!ZurinGIUtb=GC4J-0=fFW@}w~B5$-RYq|-QNmu z?!JwIkiDs8ap?TD0JU^3-YPEh@cDf?VgXEq#897gXG`jq)Jh7W2kCpno4~w>yEf_9 z*!@UC1Je`QT}NNV>w3{HSik=A3cPFB6@{{@sw-;LHTyJN=WTDTD(Q4`n8hX$U?VleS6Da+AzltHh_KM@LlHDY0x+oO=^l9ea$rY9 z4`t>%h~^EC45HmlT}@|tWUcbvDA>PZGAg{Sy!H7+pnin+5fq^>+~X{|PXOcIhM4y# zxIPci6nz-mdmwwSKY0~EqC4=ee&}uoU--9|x8lt%X!BnDhHEb=NY@;1@NGS5;m&tW za7mFRDI)XZZzzV#daL?RO-6piV69aEPf89dk7x&*JNl|_`zz1zmQAAly?Fn}Ec^=K zw;)eexZjB&pKzbbpoS>e5jTpxXo(YI6{B}y)>&U!ZGg7RJwV zS!ZLD!cwD_CUHfYq`D-HYZ6x)Ek2VUMR6W26N&(o7uVdrsw?rlhbr;i5uMqf-mH5& zjE4ciwn3Y?t_05`&3yYl?d$JFCWA5J)V8k1F3VR2*s_mAx9*Ii&|9hZIcs71V~6eu zobOQ_$)@VJyH_01Qk;Ki zg*}&Bu2qa$uE=EX0Pn-AhQj-jip$Wy07{i9qp&gE_;&x;E;Icaaqv&zwq>$@|AvIiUm%0eKg>i*Zd9jq4XJ=>I)lTKL@2(&FgGgb@+DXt^`9ANDKL&)v zf8fUtLZY$xa$_U^+WD5kYvC;mz8{s|&Yk*fTvjUh=uAC&e)_^)4&mpG*6T1-R0E)p zdf*<7O?Ae|6c7W^A=(Q&`ygkqUToW7yv0YiyajKpMY~(@?lw{vp?{SQ3_alM79OAq z(nM&fOBv*lVp!EzF%>#?rX})H2iITaqH!n(KXxIjr=g>{`kJ4Tn*s}$Sl5x$HS9FJ z`w-sbhBh6;Z@8gWRzze_^obZ}l~d`_q98wRb5eUkJD_cu6XwSS#}eHbE)WzXPzPY! zN2&n|Ao=+DY_}z1*ii^0qhM30>6YxmYJdYMP}D5CM}D;%Ej)r2M@Wmy)JoZU`Ma7O zbyqe0b#1i(zX}nq5n-O^VG5I>=7?2@HptLBGOC$ek#1=!V=G~bmhZ7vqzh+*RxL|W zFbAKMAD;&p6yFdjd)}vyA%F6a(`)`K`NgT4Xff;wsz;oW;U7KAtU={K1EDVaxIUZp z#N?_j+nvPx52atR#tm@0v(ukur%&`g1e$)mg<9C)Jxc z8fyx3>J?Fy!A1Of4R)+FP%LQkh83D3o4Q&G<6^A0VM z#}o!<$Mj_MX4dD{=c)_KsxNgyo<*Vy)Hkzn6H9r$s7KaInQiYwUDMwKH6IQxxDT}4 zir?%1x<3i+h0Qg)_5dZ1-8gu)x0RD1npvW2q%V53>{t8sUgq)}y@yV~){Kof0y=to zN&mGD*wSs~1fM&E=v(N+He~9qPW zL8kBF+{eFH^L_@lSeUWwF{?#>wWLu6fS<5FG{-&1`+~nf6I2_jj);SN30WCk9abcO zB?y1A#^S%T2IO{x^C}}NLyGv9D!JHPqTbQ5xK+t;Msgm2uKvXtqP)K|#^Tu0(DEpy z#TmZo_lL0s8CMdnjxG)paKDE|(0}rUfZ+>8SV0n%NlL|K(yuOpyaQ4#Oxh>Hw&3}+ zmR}X75NON5ij#4wFl}^ccpNv@1eO+uDq^Zs+-+o7oFG2gDpQ`J&MI<^<(hpki0Y=$ z^MjxDO;15R^5fw{Xdm7=@Y6s7?9MoZ4(%Rt8*XT=AMKdr{5&R{`EBNPI&oVlHjcFI zkLPN~Yk$%_qV#W{rK`4TR~FcgLRDiT{AtL?6yA%^)3_;HioI_wZd6u35;KA!Nmfu! znP)>&7DuJPykl|Rk}DhNJs|2|Pol5U(J6eCNoQxv#A@lkuULR!sk4fY#ByO{n*N1DuW4XVQ<$O)J zT+mp(|S8l|?vwaTl;A4acUmhWkWk{w9t_>@TjOQW~vRqIeC6BMK;^I?zC3x{TDCVlK3W5J( znPqPWGt9AuxU7glRHC8Lo*gIvK(J!2!T_ z0|iM*=~m(dS!ixNqmU8NX1I%D+bAx2pVY)uhR6gLwcO?83D{pOKZGp4$1s))N&IppBab%0N(YP z^eN1B#josJFOOH9EJ#pll$FIW1axow7W#P#A{s07!X$>&ZG`Z71jsj&fQPdXS>ME; zk!9V=K1DC(sNYH>3ruh0AxJMRgqI6RY=KLdi&oos`j=)TbU@5rMzg{kjSXm5Hdx-L zJlG_xci|z{rr&dU*aS%WybwmM-ox3CS(Ut>fTkuH!WEBM&Cm2J1^PAnIOx{^LAgP? z4CJfeSBEP=zpjA328C2DLG<-=@=Q%u(fc4)YeBsd>nb8)PdvY&0uEE60{z+ymN$P% z5?J0OS%&@D(By0o^-LlvnX$ZiV0p9gVlcIgf(?r?{il;b!A7Y0&lIf9x;YISJZE_s zBPx@WNxp3qo#QgM3|JvtQR=sVfp4O^@id@{BlZNZy*f9Otyea>Jf zfB%H{C#AqO+t8^2(n=tHa1Eb;xEMID51GbtM{Jw+v{SJUN?pV&V zgV=Q=w#AdW4QLYEd`@z<8kySQxAeb*XS;*x6*M=wh0=DB`ad97EtRQd$ha$0k)_R1 zZV%xe=T%15oGyyK`0QNU7hft-jfj_U@(3fF4KQG)#3ShI@?1F_~vhRPk#D7!`kKxqsa6|-9*V4=lXtAl@1(g`buEik&2yekB$s124rjhiRfM@B&NLefCqxE6%quQmw=R zjws*&iMN{AW78WX+VwJ`?Dz8Q5eZQb?1yCCI7p8GSOUpuUT9*o5ucY{K&O!arnDrVXcn8VLA zA%5M&n$71e2UBe#G9mu60jE6rp>Oqn&9k);^aU6JR$aDtwtvov*zz-#5v4JSV28E* zibzE)fSekfF0~Z>52tH2_~Qhd&_=?(=9n6=ZDe7|p2o$EMb!_*OCVm6fJ1&bGd(nI zF~??}H6cic${IKyj9yq2TN0t-7gj7W-2 z6k27-GfT7NM;YI%-?b-k7#R=zI^5KI4qUKXLmqQ3*arMo&lf!@Ug+ht!3s7?;#)cGXQyb<#xqPj0_V?V&i{;pKc_y*wl^=nq^y3(*xdvsAOgQH zEEUfGc_9&*0-G#Vc}twZ@LI){fu#k#EHM3-D?~)`r4j0w;;=aG53YJP>3fE6@Uvtw z`N?qp|I2MV90PzX;I=W}sx9GeB7Gua;!~{*&s3Nyi_H^KocEufXT1|ju^PTMOfIM> z?hQ@Jqt-sRNnD~`Mn_fnQHM8E@2^1+dh8}^6IUqO!fZ+HjiUzA=Oz3T) zFuq!7?A?MYd~WD$N&Ti~TA@%YklB>npW9w62rT63<#7@r_3`(m=#?jnl#tyO zn&geM&f%Tu_9+SJsp*-*Y$_%Cd=4x;OvsDMj;+k9Jg>}AG+Z7c4cX+yMWN(1 zd2exh33YfRp0rPS&wra$QftUZ;r*1^rlF7frM*Xb>$=YM9_eZ7YZ~4 zrvlnbj^1oVUn=;rN=!eFMhO*dK2H)3NW(A9(*emB~Pb1hkVHUQi9 zvH#uw>Suqs_6v+M2z(TLdl|6jf0{Gp=OAKsisAlex_9@d(>=R?a)MD&?=y_wE)g0; z7p+gH6*8>^t?Y-`K2uY)$U4xJvx0zbFXO4+J)chY?u9qK$T;T=S2158PEqigytExb zOzBRdHdvZw^aF{OL4AK6^g(PsO8>gr8k+r0p8N_jLD(9t#;@umw8IdYxkGp;?07;) z@XgS!|^_8`%1G>0A5w>BDpyb?0|D?oZkrk+|h}?vtA~4gO zn{q#HgCaRLAwiI6Axl*W3RCaisQxk^(a`#H4qK$Vr6y5nXGK}5i&JCH!$O`yhO^?z z!Eh?F%W~xTQiW2LKaq#_%)ZDobDjEisHL^N`TSIPYfDROb8~aeq@(4EZuVtl^Ty49 z@k3+oR}nDa*An!+ z7-nQSQ459oYrdB26>QE9fK)faFwS!HdqAf;Z%I1>{?SqbJwa{zdDX+FX6Qx3zJpK<89EYWZ<&Ldv&dC}f`t&iq_a2#!Z^YozH}KY?l-ek1@-QN6(JCd{ zUbE@awidf1cJ{~io=_yI0XS*=@<+Uy3zrD)TnqT$Z z%YK)oepvBy*<)x_*h`}|te7{#tfP0sS}Io5>XREicRPIJfsBI@l{;}^GUpbh&(lNw zgY?8C^xw5ZW4-5U9_R9FgyjdY+&}~)QghGw9Xw~I;W>ts=tST!XMwRfac_?3A238G z8dekdV}|N6zBq=VdW<*xWT+nF2QPF-5V&0#yPg+@zIOa|r+om_7b;FJD&^|a$al5; zZ=hpqIdp7&T6z(gi9tbxNnj}d)wAg_K4-8QbR8W_E4Z=*VoFz%v(y~Y{zF(yme;5* zmNinSfR%+i&#g*|rr_V^6Cz<+W^#5yR#0|ejx;VmEIXz#^Aa>vX)Y)W6za0N9AtU{ z{qXyGHf~|b>*W_E4FGQSIn8+vFuovoIk1h#aW&2(^a*7&dI@%RvCw`vRC7;99^Q{= zxaMuR)(Y3W;hGkb_YTO66mAba`eCplr8Kf2;oFvm3EB4mc93pkIQ?;S6ukh)ubx>= zpB1ErrNSmmGgzSpI?b9u2({tOtSW}*jD}jOypOH0J9(5TE3bb_B*KkVXZ*xPUqP0w4H^eBgy2ywGRi47ya@ zkc+akC7#@&N`9}`XMSTAhrtC^1(XC@V2^lH&N3Boxvz1$<`#uKC-v5Q(F?3F8=sv? zf`q7~xWo*A0geC%2U#c5fW)%e7I67?l6XtFbmvbcXt@?yX)H1(@!R31;5RM3iI;*) zz`4Irh~#(?jiAASW+At0G+GEfa)4EZtl;8t!^KXxc$~S|hWF48Uc5xxAkX(;#1}dh z6Mt@O9#MWpaYO(Z{{lXE2OR`ng+|tyjr7@=)KKOaEtq38*-pUf!!}q}Q#ER8c4qQ( z5Z$wrvB1tr^4zK5mayVpI80#Y(SH8$?SY!Hm>a=^BR9f|Q27LBg^Bg(94B)bPB!ke z#T_WX(%*`$D@U)<9@y%2K(BxPT}3hgVM8=)Y(rJ4Dl}THrD+P=74=S#l_6DOm3(=b z3Q8*LMqWkp9T6$3<30sf2w!IP4|h&qzSp$9Ye$a*bV5Mm{TILe#J)oFco+o zmg1pEB!!*ooM2PySx-~})gaHecxY=o{fp;P7(QayZk{)DrIr508?F=>n2rLb3qNWT zEH^OSSAQ~HmhfkMbPOH+297Gtz;S5?jw1~mmtliGhNF7bz;KUygN8a<)noop%vFpA zwX`)UZk%drX<6|3AH+H+k{slXd^+epsvV7*_-XjcbjoD#I47j_q^^I4OQRxhT*UG- zK}BBh69vEvA9w-QQM3$tT5y=|6bd6b1uqb@M-}w4^`!J?Q)z+?CP4_5VH-cuKkY_if2c@M@qO<9lqQz-R=P$%AfucfWc3fGO z>bxwcBnKAt!7{1Bo&vOJHinJg_?g8C+H)DY{S(%+w9QrBnvQ*HZrt(HwxP!etgDHZXp}eld){^cB;L?^B-|o8R*xpoe9>b** zy-`DH!=?SzBe5f)eJy=4Ll>lvx?pgipJTe+*!=H<1OMmDfZP8uGayR?4QB-I;b{Qj zWfXQL_(DyL!glw??u^oK_lBR0!glwA7rH961v)tZ&GGn})o){yXC42{HnL|O|3Xi6 z?tgduOG`>C6pAf>b^K33V+`K%zdQcNq5TNo{3$YyRHt&sl2( zV`0BU(7$?u&$O|f{@HUG3vUE@_|==a&`SU84HwQc;s8w(#tu-qVWogjYzIxO1Wo*9 zOWlO@d+I;<=*XNvBs~*|v`BD@Q{fCi@u1tN?R}oO#4sCZX4`+KJAXK|O4wz|vS4R{ z3*_H=r7ftXrX{NNbX|FKc!#<<q2;K*TQL$letK9oBpLW_a^LXb-fxET zhB$;NVJ(Et1cJuY(6r8c_Yi=)8JK-ecJ57;^H5BdaDj#Ot`5etHZhN7r4azd80M z3$H^<>8EbYg=_RTZf~+GL8W&1V6U`VRgJc)$|9uH6f{kY-U!D)5YM!mm=GuY+)gH> zB%~ysH7NP83*ftsF#k}LU!IHT0tzbB-qPUmn2rKvUP*4LLm@Z*1D}Hj{1qvxXmLto zMQwwoh1%6R9*c|?0~l3KvA6=p$AhrddBI}8Y}4DY5fzpjFzqWQ#2|^-d5T0?QlZeI zui{LDQ@7_O3(ivFIT*9cw1xp%f=u2Od}KSUpDT$jO!%th;-unh>J#ifg519wX5l-K z=-|w=Sz|-|%M)N?*0j@KdA`kTi=yd&c{3MU>94%sX1VA@z`N$Fgv43CFhm$my#p$} z9d&%js|EXjR<`$bbar^4S3Noq>-q#Bs%^aa<>=>=zYKmo&H49_x)*1*0bE68kc{71 z$4yd&)SYl^6fC_@QR>;%N9lcvDn+ zGuMwP=xQ$4R$f+iP(QVUt5MP6FE&L_@PldHh21{-{ubLKyrn|?!mF=am84Vxp2fc* z7Bss6J9CgTD>YUaD}-p8>-m%=2#pCB0RCA#KqMafl1x4j?6V_6Vc7cp<)zF0iigzq zKuTf8?sK7{G*FnLtdzwm5!n~cEa6;uRG660&rYbysy$znt;#OSm&%Gu@>+9$wPeM! z!Q==aR+krQ3Xxg8MW!@Mtyr`=8X&44$jdi&?*I6dMWM1pt&r_1_wJ6mS(RT=s4nzu zkzgspN*Aa(6oc01_|go#?p0h>%l}0g3=cpcM8rnB5=A8Z93A>?&H; z9a9!RUN^d6s&8<(yYGu`_sKKW4KO;p(z&no`jtrz_a#F2vhH8>kuLJ=j`+hKfl@e^ z{M5d7&8>p3U_SWVy3A9e6pF)T5obkbv(mDuwD|Km{3QiRx%`}Xs-!$CN4}&aTb3`8 zDoQSN{S^8bubtGr zx7QYrjk93S%r`Xul<(2gP!~DT2Zi#Q%1f7PF2c6U30fj96F;fVken;clGzHl&{Ni{ zDBfJNwbR?8P+nXj6MJ0@or=FxUZ^T8m3Us}2J&qq9TF+bv0N3vhAI)r@s3Bt?3<{c z)|dC`dY<7(2{+musIyo&_;aRF%`p|x);GKKJZO=aY_Rx0$``SQh&N}CZ!vP1x2 zA@MTG`fOUb_i0QU0Jy~0mgua>Ut3M_cN02O=oER5`{<%6UY~`H&Izny&ZTE0r}DGo z7K`KaLUW=ibyao&^!FAhOLH5k+=d+3U9@e{1*v;kvQU)%-YHXuR|v#j%=LbwPP<#f1v$aW0*@H`d8Ffa2IX38iU8@p)-w zWyM-qRarzym@K9S)Pg#+KB}sqJP!s7DA-a~Pm6ZLhx>}tieWiCe7IIxEsvGQ79`fd zhnK`^V#@Q&^C~V>Qp;dD1UiJ8a14o?l1w>5Kxc49;dI&_fuo-v>^ojYh(*P5`Jz(h z8>`F0%g$JO+psyNBAeX@b^ZWWdS7>u{lx(s0iCv6(PkFM)&>CHea8i zcNibXOBFTz}>?+BR~3z-xLz)8|eDg5k0@pH|8E1)$(6$3e$O?^Zkk=Zvwwg@RGg3xt>H!GvI@W^jxP|33%MMJbOI=ln4eZ!0%?wl;;7;X zacq4BH(3?c5M5bNo)5D!6uu-jJpaQ|Q@=so9q2r@P9d-l)*@ecSxz8(e32HpCVGaS z5GvzTf-;~Syw59-E83R;Urho0562s(5wrm8fVIh{ePm5&X{kaZtFTbS$>IQvsDLX@ zH{3=ka~sM?WrSD&6~rXC4TcfmHYxx-5@+h#e*cljd4Uq{JE%J_f_L3Vs|N6@FZKbw zWIP+7msw;V;O%-(qYcuA;Rhg6Umfm`9xSUq)9l+)GLqcnhgP}tx_rW!c#fD^3|$bH zJldcOqW>~Cy3unepaT}}0ocQr=;ZHhJdPP9+JuGyK3SAI z5H^}9F%ABW&&HCiuu^jwfi_9#vqw^nN1cF0?%FsNtfqxf+!wyvF46I%f1!cXVJWOe zQYA@%gcP)!0H{n|C8R*pL?R1`2-d%+!nbN9mCDGXFiC7}Ey!L}eROr6I#-jUq0mvh z7kWZ^NMEKWK1^^5agKGf*y=kj>PN;|0?--au+W;X-|4A^Tx*P-isM!MEpXy7Y zkygs$B=H4Fa3rvBR}%-_3l2LdhP+zPGkCqoAQE5xS0&UlG$9}-aZpN&Yz0pKOxfQy zgJ3=q^i64zR#sUWQ4%f{)Yfqm%OdI{s|z$Bq7@YW05>rAa467^LI+Si=99QS$+R0( zvjYL|R{+nH0F|Uw@;F)Svs)5q1<=!GLz(}1OOJBlGX#$QjFz*uCKz3rmu66NRi)vj zjAE_lCaNOp!)xGc8C?nE(N`F0Vx^c6dk*T{N3i4l-@ckte((_lVUQ=MC%bMr8LXAQ zl{W~MD&*Fs0m~Qnv(d5gO=z~{xu2*&t8MK+1|n{$$IokBXZNAH|>hJvH0~seCiaop!5T} zOv?otHpg0M!)V{1%1k+VLL09^-91Op!0pU&8xzRY@`ZbB&N{7)oDEO$2XR?QOeYSa&9^du^G@7ZaOH{9K+LeZ)w@kLCT{aW zZ{XMA|F6IEB`n?jqV^_AzsaewW$FH{O90q190<5&fDu?g>t6>@+0Az_Lc0L3=|6Y` ztwZ0kW<}W6z)${ulC)xjDFi(Srce@BkW|m`l0X})GFU4h{6=u49c;{*$NL^QX+U?+ zY0kK$^3Y)^on{Mml3^n!bLPVgRtr9?Gy=#u$3@}w5x_Z?GgJQx$xFlgNcNB5r%ibB+MCQ1Me_AC9JJa zRE0N$S6je08W>2-oH0v8?K-FnWK1XYkgTvq8wT)(FZSbz5B^fekO>YQ4xG3)fPP{} zF)|Lyxb#L+vo9mzppqF0e|Tl=pEtk13569)opi>6*0Z5ddy>RHVG0|a)u8mWVi$6)9bgiiT?-*BIt$~HXVmG>^R0a=H~1XV8D zopdk|RJj8xVpMrivS}xy$`cr|lvhZrr3vEr{NxHXH!)5Enm9kz0>rgKQe_$RIXkK* zUk&r7putNS4SpLmc)xC(@ga(UgKeyZ1E4NIU7rATt+WhKvMtRh_N`nPg%chJS`(I>SEB<;QA!1BO9Ws^DWJ2_OTB1ZYNsO)!Cu$t%ePLW*t|nLc12AMboKM zX!A6(82{mR@GXuJHS@v@Z09wh@Ud|yNVCx~5?RCUYB>~_GeC(ZW*tB8k_{CkW`FlI zEVcqADk&(gUxei3Y#ffHtK;ipDg+KOc8RuWvgG3Ee3+ZAM!Uv7LYCf2ZX)#6LZS1g zJIbQsVucd6iFWA|k3!^n?5K_J2cgiUDv+i$DO=QyS_+1-f=~K{9*Yhw^DhrBrgRTr z^8-v8e} cjI?ykD0Y+ZtKidB7rEgj$29@U0%?IXU+N*_ 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

SexFLN!7uDq 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%0ASI&8E|12vgf~hF^;K|E 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Μ@Mio^((e*u=_LAvW+rNKw$V@#oNL#%M?JwwjBbb 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�ZW&yc& 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

ONH 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 z�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

6o({bV`3uv5W)YdO-A?S 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

-2dD!gG_Ht2HB{&RJ?5=FLA*FQ4RUjX>t?(G*V?nZ%WsjZ$=yZ9_1>HFs5*kCj1x%8mUJHMpYPAw{%I2y! zYBXIlzooX9W;0AnwY31+vG}vFS=?o*J707iY5uZwAAc8(T`V z6yWXycHd?O61W!U(y)CFz>Y+3zn*IDN3B+X>~A`ry^;Z$;(Yi~GY$L(P$t?%;yr8$ z%??suL7uy`%m4yc;%piWNuCf`~QI5@)tTIK!o#)f=+BuGR~Dv&TMl&L_n% zYN{Y8fg`564fQUAmvt40dW=^iD6jDp8K0D4a5 z9^NMZJur2GHgtjNNb{o>G{4!I+gU%CcXI#)?Ooe`NobXK+}K zXG3c9cr?8kCKARg08hjOgR5hYw~g~gPuL2X;pfECjKB^fN<1^=9(Z%48ToJHjs0Ui zLDx?%ITjP{8*FhRe3;h}Abb8nNOL+Xq~}RCn;lHE)hiYM*ULsM`q9}QlH zHH6cFfZ%AOr0^7vvmqoC56E{XcgwvtQ02U3_zqMV&{t*ZU8u4-vk>WWph1r$pz2RQ z8WZXhVgwt^i*v(wuy^p*L{`8G2dpEUfgnm^re;9`Dn1k?I; zEZk?){yN(EyAnV>FP5+7Y60{y*{Zy~?lxN8ey{WN#a#+&cy~J_mcnGH=gVKFW|+VK zNM|IKh>&AVOma;A@eDj~w}AS_1g4s?kWaD{KOkH}4u&2vf*%$IC%QoMg5rM%>KC{_ zb`y)!{qGv3+=s$i3^#W34F_lP&p-x@@s}7OsPm}=(C%!5Z6tok9*25vLp_~8pl8mV zP|q3O#rApWhrGj`Bis#Ua&_q=Gu1lICik1-v3fUzbW4$r^Z{&kR3&P=+mJEV-jhi8 znn_=yh3FWO;-8a~l`knYU>h_&bBT)fqPyAcOVR}%SkDFv7mOznXpsZDktHhzeb*Ku zXz`}Gc*I&rQQ{0tKS*UAg&h^+ID{_gJLusPaW3~CqLb= z=`3*kBUS@|5!{ML_r*9J*+ce@e#`~GZ{vdB|F_VOT%FOHLVz1A#?#xW09j82qUpNT z>KUb*8p;LEH_RTbZziehvR*d^{CnIaoK@({UiKfknyC>b{e(l?Yz4Z4NfzPJ%DM=_lN&`8DkYgm1!C`dadeuO%lI#^qal zE!o~CH9Gs>B|rI(lDTr39RP<2=pGG!$i9}onJYbVHHYrL-W~H-=|+zsNOv0q=`Owx z_?#HcM`i+eS5R3QSajDSRYhDOtr%~brTS_XIPY?#a2z)8mw zM+#x%gqHRg(Xn`n9)1XY9Bgm|uU&(O86_VEcy9!$KqEzHClIGc2&qFnl*5b1rb&3B z1oO8pur^=3DJ3HNKzz9DYE9kMmV)6ItZR>#9Y1#^1I>;&iq_7=VoLyNubhW<7XQ2_EiBtp6!fO@ z*DmdI5=Utv7_Md7i&>Ahi!-we(!?_^jJs=nk3_)=oc{!gTG;gvnn(q`{D3DTF`E3L zWFP%q#4@gkWuQ;*$LmCO}w zl7UX+hV#Sy;21!&RHs@$G^6RQAR%j)ew1ot@|LPUb*4t@R0_3;O7wFRsm-=-UEM6I~| zDG&L;#~c;V>EdqhvJO7!=9=TTgjsrk#wg`BSXb-Q^WJjVG2UGK>k`;n`-Q9lNRoF9 ze3)+*stP*J*Igug_3?Q+d)2c{L;z4Q7i4Htg7na+7p+s!I4hPQR!8Wesx@53=J_)q z13X9f4w2qNvp7}wtd(#-na2fl|5fb#Rx8L=9&%s$vFWk2Mqd4=&a_CKPGN@#C?y4+ zO_Zw)XyWVXofd1B+HW@0bLZF9D<540D8r^mT4vKWyKF<<6@l}uHzLWRx8eWN>F3@B1k63?+CNhK-IsJ`L%!5>6IA%wa$R>4JV?8`No_E9Qkl~ zO}cU2UOE)t+IiP5s5U58DszH-~X`Kr0La?50h=Ow0Le?)8ql24( z{nJzE;kfD+-!GpSoOi=St#NR21}t`|vFzpbpe zc(v>%^!qiJQRW^kNag-TWL!wUlnUA$7EBJ+o%jVWu`JVoIIi~=Xm{X%Z&4;*1u~(3DtVWAA9gHj`r5fr$1Lp%4cgZptfi_tVK)DeKDaY# zcjTV`YhC}bCYb*x{Jk#Tzt{E8n#g(V6;3+@$0mA@_c+qj(r)PC{zsF(?<9eE6wXk8 zlM%T8D%5|B_Tw+Ash^=Ij0TOQS3Mcs{wvZ^VVzIQMuOD3>7g{K>8Rog3=u!KhP8$( zhG?4JH|C)^HQ-KB&0SkO4c}yUcqYEvphB_bK9TH)5 z_)z50h+$#J{Z9CvCPjg9K0!mXkLMmwKa~MEd~rfSwz&3q1F7_AH~(mK4)rTRI=N4` zXZgNNwIro)>LZX?H80MhY+r|)Q&nd>b`<~!(S^lQ2Y9w8G#6ebQj8_*P-2`sH(Z`NP9rQYJ(#;E=03prbI19l1Ik^%bKY#A?cnrW+}mGUYibUid(uMBE6O4 z$-&%M^Hzhv#P^_^0mBoG?u~#9K==i((Y=WTI~*u&>@rOyy64o!K=@8A<4GsGiFYUL zhY_p=li3PW%II@J*Mlk`_62XCBOLs!{I_$Nzn8_)<^D|pm9XG+rwsde(3FlhXPzqY zbe`l=*3HbTeX_D*P?{JIfZT5Vq(O(23EIAtx!-B3 z?IluA!w{xCR`)Sd&mzFFIF@FOG!;m_N6Q!y>_i9^a~r#E;wnqfa&VxDaW%c)$e$o9 zgo)&+l!&-PxjDIo*(GUPvv%dUZLzm;wDuB5Wcb8~%8QyRZ`PBm+g}C$F$@{-V$&0g z5>rXHu_ye7O`9XGwJ^TcT7FORnjIyaAWX_j%umQC;Z_9>_W7KVeZ*2B)o&1b%uJ%= zQ}iGQFnIleE$$9JWzbfwBcW|M1EiVCUxmmNnD%HQL{_)FY>3k&HQ!L6V9gHbfH!l& zWI9@OP=5LjTnxz7;4~P`J%8}OBIBqo8`O)z+1fN2H!&rA)HIy{7svz%Pt;bSCZ{{h zc!C|l@Ms`J1=@On<<_h4KzU#lc@_buaopxfR%?>Ob37Bm(=*Z~sW}-tv-jtCZQX3Q zcP-2W(tP5B|yxNlG&n zg0Q`4)HxM*^3WLFZqzxOz)zVIv`7M?V_xtYZ+o{bUgdt*f@LH;4^E@~uy6L^W_!x<<^-yuU7~TT-$WVL6&k=RGn}Wp3@vuUO)t?{VXj}hJsKFJO~Q4 z;+N)kiWe$qsA++U&VGSc?@FcsW(A;>)niTO2+;{fhsMbYwEY)615!I?yujO4kRJmh zeP72%Xrr8ER-<)u2sHB%J!jk_!WDPXJ7(ztS_i@cUG3dAcyYl_sd-nzE{9$UF<7~8 ziXR>j0YDM2vjJ#S#vdh}H}1P=op0^0y(QRKvZ8ch$xzGtRX2)G%>qvKPd86PeG8+Aepy-ris5slFz#Em%}CySVD&x$AjAnbK*Q%}Y49G$khZ zNbr#m1H7D%cHS`r`4}E^auBm>Joj%ni-!V z4i<&PhZ)qBc~)DfZL1A7dgS=$9LV-Dz@7)Q&gE1}E*bz{gAIw}$rxu8z@Fpc*yl&# zV9-p?HUhzLpIrID+h5VxT;zNPEd*f}FJ!mx_JDmP>TgL;!>iW9MDZIOwU()r;zQ^g zv{j0?J}0Pm_u*sxAj9AWMC+w^JwYki z*Y*TjnVb+72LYP~bp?J^8->DKw9Sy9gYA|EArJVPV0+Vc{W1<3keNW5K4OWLJ=GgWGa)b75CS#-IED%N$iruUe1nI(h@$@~E&y-pfa@E*rB&|c* z>z_BGQ4rs*kg2sw3Vmee^|aB{hmRNnp8ys^c;mqv@XzZOCWWMV#s^-ImcK51PqO|h z#+S(6k+(tQd7)MimTUddw~V}fPqTnaCDCPp;xkQg1MS$ZWG2D7;MaonV4en;TwW`& z7aqO>w4km;?+Dm`_s*#ALSva{1RiTaKW81L@t87r#zcW=2M#3k>*ebDZR(nu+x1Fm z?vqujV^R*_J)pH3yn9(V&OZIkTjjSunc|K}T}j zZk^uWz_{^^{4NCk!Ap59yeS5-6BY?k5s;K91)+nLXxmDhw;JVPLkQ4D`Es1EhEcZ? z?*pAhe|{til;XfnGgIub7I}ataucCrxa>#%!sbmkk%Q)x^j&LblMM>Td}umDp3-f@ z(QSMx{WU!AhS5~n>#ypdx!k3gy8<4dA^M2g{yGxSo`%LXd@7|Y(r|VVAKSyjC6#_p z2lND`1eNke2ZcjxD*cfTUJgcbA2aL&?tkuoBj7>ImjOi?Eo*LWxT*4L#;`GD1x_g$MoIve3Bw0Uh&~KGiq~g6{dzIhl)cj zH=Asjb09e^)o|qoe#ja(Kf`Tl{QS6_LgR&gq=;96ci+JakkMR2 zvV*_mf}P6g@%7S%%BoA-s|2ntn;dqy1YQrViG*jq0C3xRa&k^Ah_X+@v8%KU*17d*=Y zlfGsodeT?bNNY%(_H1a4z;qaKXHNSZ4-ftUM2kHV4cmIBz= z%ijP32VM)y;BBb`aqiF#pRPEqZn%YvTC^&MX1JK{MJ+8)>P~kYt^Z9y{iwsF_Sn*B zIxl#?-#&ks8_wtD7H5}0ydt%F32Rdc;OLe|5bCTU#F0s1@li$j0xF>aD<)f_bYj=^ zdA8WxRSZcXXM@m|3okTP@XQ4v!Yj}?w?q&FrKRBESrZ$#0yi*Av4R-_Uc_6mAg5_WUPZX6B9LK4a zC%(BQzSB0eyaRtv*ong$j1n79h=wJC=z@wI1RJ)v;@Hwy&`X54VXs0O**|e6Hbk#o z1v~rig_AX3m|K!nngQc*7l3YBp(Mj)Hhh%QV#N#u zxn|+T!W)^D4lDUqP-$XTyae2M`=?uC-SKn#9A_bAr2?!X(DwIGYfN9QKOi+iSUq2O zw)jlxX{ciZcx)jFuFrRD%3WAfxUgSAZ)k4wu8X@gsG?cRt+x}j?#;kI9DW-awNj~) zp1FU}UADi^zqO_Mnqb{^le>%R$X)~HH4Wi+eXxMPwH*-VOU;4T3V;=vQD8$uQ+dHI z{`>qr$@Rj#5)cBwOChlv?`J)M)tEr+onmT>O(dH}NT^G>+64f+YFaxj! zukaFF-pcJO`CIym?z_Cs(^q2MF;H}tZ(>MUu>i{nKx;G7(zsTKq|C|=VH2c z)ruXd2Xg~O(U*!YT`H}r+1Kb$=vsbR;96o`wXQ5NEg?NVeLaX0%&`hjxK))4k$Dy0Y$+DzB1qhYr_=0WcxUPfb~rvOM3qyt(G4si4UdWdU3$7M7)0S;wt=w{Fy{f$L&^+fIw@I?=`syG}|VL0L=?&Foyj?&MKY zo9Q?Pd_}iQn1i6rI83l=_{jXo92`U7D-c<_oE>Dx3|fh0_B|Ww@ohCDXkZ24l$k5R zFK`2mt^OdKqmlxX4#fKz;InJ~HB5%Zyc9`FZc1)otl$~6`Ps!di9VbaS{RZ;qGn#S zZY|S`;0r4JH9Pq!sM$b zuibp>qlcs2y+@r08s>E9Opft&;z{nCGIhnetAZWwrmjm4kRKw6oMJsjmq+!s8P3x3 z;n)x!@}3RF?>l*JmYbKES(8+FI}>I|KjJT`X{vqQWTUwwz1{e*Hd%8NEbm+b`jZwL zHG1CW+fMOLGNH^T8ai z_mJU&yL?l596SfN-&JTDyX$kc;f+JuU};-n<87}EQ`B}}ut?;E9~nI}qyohv^Xn`(S4 z^=SUF3^FEQ_pt+Rc>D>EVeW?#i;U5-aN^u`JqB*gH`)#7DEK)dXljIYqAv7`^~SH- zdAkfid;|H<%|91%>&gxn(5t6WCmQ!si6|labNU$3un%JD!N$ zW}Fms79h9Rj#iz>Jy~!%JuWq~s4V$|80Ea;L$oN^T(Xm(z#VT<2YLwwzU<&RF0@)| z1qC{|F|h=avuo*~W%f%hYC_=mk8!wr-MFt9L6N%HK*Ks%)D#DuLBj3A*Oy%dO~# zRx0gF&#a!qbnorfo;VKz+F8HP6_5CJP!)k zG*kEhm<%>pJWcm4nmV2s2NCm!-NScAx{#B5n;;1o`L$xd7sv(TPah%=27n{HidTBn z2uId^9I!l^9s-x`+TUEZX~1O*lHJmlt6xjsNS~_LN%ywFN9((0Y11Y(%r-X5)*w?L z;PX|~zg|T$kp4?gz*FB4XtpkzIvSQ?gJyF?mO?*^@|WchE8+_i zvv8Q0oz6CXPG~b8=F6aYwX|PkIcOhyKeqN1L1Qo%oF#xUdLsm3BA4xQ^ZZG1wsE%j zY&IzO-VoF%7~p)`{S_K9lbhebAn6@DavTp{wx8-RYxdM=8ylM**QIKXHxB7gAswY$ zIPrhv2sjckF!(nThb$AF!7H_0f&uB_b*M2%!+OY=kHb z1P=3O!Q&YYL;i-OFCT?0*25?U_RJ*f{!kvGu z82Tx?Oc{E^V~yGK^u6$po^^hiHw5b?qjMLDI0+f+%2@tSEkoD+Pc8Fi*$MxvlzFq^ z8+L3yoo%HD-j~Oze`Fz@Y!VMKw3*Il_d_tpg2Q9hE&=X)F_)~XdMH8zUkpVQx`*cz z)X;T6F~S3a=0%eFHqBkqoObtyt{ZpjTV&ZJKh7EmMF;A z^gb1$iM?~?b{sheA6kZ5DXq|lSSOO?i%vD_;8)s zcbrdltn@DIPvCQU4SY@y2Hhs`!3<{~<0J7*C?{EK9(QPa#JY$*2C%cCB1ufP5tRFW zuw=zZam8mVy2mpo!B#s`(e*kBvh=|z)t-)rB>qt#=O2(iADjbd+Q3d`LJJJkJdvp-Tl`+ z`_Lg85>mQeq5i-gmzG^rG@%!PM~;jN3TZ_1B(_zB`DY<*PL-H78JOR&#ad3hPC;X1~Dxrh8Kygr0(1J!Z2V{0B zcd3WG2jiILP=d# zq`-LhQ~p)Qt{=}oU3eNY1G0-Mk`O-$z4_NHKK_%JT#!^G0+zRZIlCNFe-W|JzYtKL zM(`*T07C*AQr}kbxg|?|TCS8mu5Z?CgVvcE3~(K7h-yba-KHKeFjA|e(W6;lQA7c{ z=+SKWf*aY)KmxcSs$ZdP+z@UX>{WhZM@|<~cjq#$OdD-DJDSt4?`vvl{3L@5WdUO{ zaAmdDDn3Zjv}P)#o4L{>r01Bf>BO^937Y+0HujPA96aq2fu`ZnpwylAiHJd3TI?fs z=d6l}Sjod9qp{s&Je1394gf#$do&^$xu8iyflYcy1+AXQ;-HJ10(M~Ux3tgZeNGN8 z+t2N};(47M(L~#n6UA0n*4=Wj*lp@FKQt*c#WODWS!Q>!wo2}F$E`asF%8al@`9A* zxoggm&9|mgAO36*q-82wgHh3en0TSAp#wVFw2;iwta1V5)?Z7fT7_Z}*bN_M-r1r-MbR7Jy z+u*y~AXBmLyT8D9sjN}G#h}x|!961#+EMW`a2+O~S4;6L9EWCt^+nlTSD-mk^Sr)8 z=9AkA_P^yEY#wYNLs~0agH@=12O88C4JeX1I?NUriN_1qFr(ltt*0X+*c@PzoA(R^ zT-Sc?HKCmh9}Or{7ip)#7pu@18h>DRpbrG~EkBZt1!p#%-})ItQx5WJI`s};nJXMkgY|Tbh8~};R34r ztk_?zS4)*>7YHHdu9eo-yt-AbvTd~G;gOv~gI$~e`jcG!m{Xv8WfImP=W8mIE^>J zDc;I54q0ER30$dF(?R%e`L$HjucgNPT`Jg7 zzLvVCuhdqUs6rpXlz{og02+G}D^mPbMpPDWiI`jTEm-tf&}n&!!ji~EM~_Aaz{;ck zSvi*cIGI~xr8*v6}GUJiXk#4~qjk*9cnBJ{r!teLK6$W@If3BD8t zUR$V@eFE66vC6wJzU+c4?WluSeszDq)3)K*GyLVvh-k>sMAh7r-fxahcnP;z`761s z5p+#xu^8^(J$|6H3bb+>xp%>}iI+{%%o=kALCuc8IzB4ZUVIKdfM0P;c@K?sahVG z@dU6dicig&CM1-iS#i`3wkLq`p+n4F@HeiruZ%f|c}Yv9*@7_TUes^T^McNjw~`L> zQ8FE9z7v1Dch;?+uVONNsWe9rpxlPO+5Wt=^TONQ4zjlg71NZHV{aIk=H@dih{VW* z@Uv0Zasi@H1|}eO2YT2vkYZnfb0m! z{uoE!x_tfGrJFZ5+}d8hlk6S-g#N@9j^r_BU{IQZg)ayjI5FiQ!`%k4sYnQ8m*}H0 zQJl$r@38ogI4{7m)s&u0%r=%lWCmz!c7Xc~?*yCPtWD6xtW-8uqy9JQTKl1q^(D6} znxq}g4RGnf`J2gSN1~ndY*ya_HdNj040EtxfRJ~TU%DA28mujDSk}*@VbHJ)V zx3Y9MJPQ4@5C~4F7q>V5y^x={h4fD8-9k%}OA}CT>#^jM#iuh#NbkZ?_IgNy77-5s z)H@L}^uqo=@OP347Z*3E;J~sQwpRq39p>4t+U0cF`8s#tCI4~YCI4C7JnY8X;p!b2 zWkmg1BMMI2ex`qGHH;jwlwZ-<~ZkL^@ zSh)8IZiEnW*5_nU$+6-S8K<&Or^JcVvhw4e#G{j6Zt}6zx{G%R4+)-ZX?5(fV0e(; z+cympV0gnk{H<}+ zxyNuaXeGD3W#8i(+7{`UDE@fYIPTuF#_4xHHja5&n2c z_n>jzzgMe7d$`4JW_t7ypMGwO2F#Lfu5J=oU&s3QXEcyhbPf9(w7vmy|G%Z!3Yg6= z=%d~-K1aY@9B&ExseRi(0i?iJ3@7IBsfi|RFYch@tq_r-H&OW;3?WmF*W+&^^tzrl z%y{r?0X7#gbne1lugX*T1mc}7yptf5L^rNFHSM9yq6fk&y zWuj@+G7q=#nAHpfnvL_oC80RrSX^j4ms8tu|5WTf<47O)sP_YVn-v5)TbRvU|MT9| z@O&>(XlsG&T_MbtUUT@-$z^vt}}lrj0^8mv?#3T)Ag^L^tl^*z{-)!$rxJriUVu(% zzi}d|$hbH$J`>Q7;1GA5%AUrHA?JHKd>mOo#5s;^twst6X4PLVPpW=;z4@=Yo16Zs zTZIm4_iOL9Q~k~Vqi$-~)DmDGqJuMlg18D9LTn49Y+4g6FfDor zCnSdB&4wC<9@})n7W}D6-rPy4J|QYLF6yI-q6TCWGV<_PddX@$Vgv5)ks6u?BN0TMN5f^0JII(Z z^!%}Si8Z$MfcXjt$I#aE;EH>Jc#!Ga`zLh2d%sh?L-w3#$sRUW1KDBFeS>R9&{xB5 z1YZwa<7*zYI$Ru;;SWMvQ@oIezYWJ@$C^OXq|h%Zg8T&95zyBR9Qp@ytQq9A7cc2i zwyqgYl5LU-$+?_@yv^wXG(m>rUo4%1!(m^*^Y(T2+ZAY=y+*Pjg+x-khKAO)B3+X$ zFRVRKec+rQ+)meMkV`Wf(Zb1Y<-px&Ra5W(e=Z-t?=hp(Jks|jzzsOdiczYo8P`yYm)@0i$Tyn&FMlU&Y(n9j7`BX14&PvSPJ9LsH`#s)TYTv@ek)We>5 zxT>o*rklh6#hx3r9{06cOSUYGwpi4YYUuXYaLDo5zS+=RTEi9ey5#qb84tg=%zobOhk zNh${=$Ij{pu?||by6HAm*$w!1FTh_J-~pie`LdY*Vdc++8#uNP?~2?RyoAB%1#0mY%EB(g(lutFs9#)Si5AQi!AZ%C%WQ3c-Sp@(cg z6ZLr1=h0}=wtsEmn`T&+ZLF#-R9v_TulS`ky$o}KX5Xs1(KggUB!tI@o`GrmHzp0G z^_ADJ0s3Z;H#%`jm`mK)UgvbnO(aRpPtCwOb(r{l8GNLh@itEy1swEmEJ>>066ivp zx^j*(oVWYj&I`cxSyU@?-{rux zE^C7ng`Q+^E1BZ5P6CzP1aMXdD&yfD@Xko~O5UFYcL7`W2+t)jIwZy@$zQbRbWlc8 zQ3486#3UaE#XDZNRRAiR&qxijyzrvHiy_72QWp5V-aTCX-K7r(sYIf(lbcNF;@1xW|RQt$TN`Dm;ANz5?FZ&th}THmGCgYUBL?a&kB#FV;gw5--mvKYc`LK zA)I^yLk=2=!jrwufOBaaIt!#9Gc*d8T6I(4%q{hDRbE}$rE{> zCmngSN+$iI&9Vu7*Frt`1N37BH2#&qV-8~5$=Co|YX!cEVdSr9c=DI4d{oAWfsZo2 z{R?j+C!_6#*D_dtxg;JPcKApkvjEM95gw09`*9Km5aN=lVXrk8>E0hzQsXgY<~6)7o`K|Mlf0)bc;w(QF#xCQg+ywy3g z@hgnZf#UwYei8?>QBd>q0CCUYH{Ugu~mJZAgpEk`)zkfa581n@vGuy9Jl=vDy&P= zga3HE;n0(470||ijG7-d?ZBFpBYDx{!;z>&*Fa8*0-8(YO4&0w68* z6sUiDEoARNnfC&edDiq})^!$-sR7`>9|l|D(2X-v59J0YMQ2^es7|jT(R^McJ2?!G z_H{iNh8FQdTu#L9HWmfMd7KO^KUQ%f=XCz*bU3XgS4HS&F|(UYILVz>H(pAexG51% z>lF4Ootjt>hnAJa2E+!Q3aL*Q9K3O$`+#C&m>|(3(IvqZ)BXbGD+TPC5ouE(2yyV= z9^f?ZprG>Z<@ZG|SLX@5P1fV@LOep;!u-RI#sww1#)c)G%{aZd}^em zcNYW|_Kt&i^<~^RXc;tebXZGx6YXI>L;uqR55mVR(R#ve(M-dkN&IasZ8EX;Fg!=1 z$p8KA+P{DM4oJvC4GSTT8tu|W4W{q~H37Qw5S#jc90?ljt7*#03qN3N5#$Dg((>`D z*K@1aL!jHHnhE!2%Sq&_z{zOPvagG==h-fvZZ>ysTDZhJDfm(4vrgG#5<3w3rb>Y6 zKW+M@{(nY+oxgc34lg(__UxoK8xX_NH^?51nPf?v->Upwjkde zoAQoV9lv&cs3k8J57)zAa4ruII|O1(@E?m0=6M0U?G73(M($|zP~=2VSARtHyac=~ z4bRQRLtK_RuHLXYIU?IPA@btobJaz4{)Ilr1>n4}QdC!2smKb-3`z~DxGD%r z_s#ImC{yNiC;jdkCx|+(H-i^ne75*(A^E=k%M~F$3CNeJgWm9tcpP#&N;MS0q=7uaUrd0)^=FhS#+Z<$s^fmM4I#-o6enZ4qpy;xjAEn zJ>CF#51Hye$HNpg^22|NLW7P#rZAcVvoQXWC3eE&XtQyN=DqLH<{Dj`l?&-?OHsNj9MB|PeW#PtX;eNBewY_hv(Xd>i`BT@s;hR8~; zf;>JmAUz;CsIo#3B=O7k&M9lj>_~b_7Bzff`M$i<7xcay00#a9`Y|tRS+KSD2LE)w zG`}>_MbWjyYEs_=G}i4~6(DJ@MC(=pOJF^osO5Hidg@M1Nn4esMqAKMeWTkG+@pr~ zEvUeNC<|j+==YTN{q?zXX=itIE zMqqvNiHA|i)#j@oK>YA6QGxC0eY+aH(5QvCTnx&Zc4gx^U0X5?@Ux!bv=!vJI3vGh z+IAGyg2Nsxp9hW;tV6HD;i{FZ@b|E{w1A+CaPE%*EqDOTZh&fb@&gBk-p~@$h~`S) zRyS2Q)Yd~ohp%yLM^oFxw_~+vd1p~8$hOdEI0z`Y+6l_25FJ_#yZ_i_o#++5U(Xmd zjTRbijpf_(@JwABe5i>)${$gz4UXk4Mc}eJ)BpnO+jevVinSviLO2Z-DX`lA^R9^? z>k+^ZfmoCm@Q$4!q_WmM%qm9uhJ#=6$HW{AadO8bc+fUSpD8wOBR&@+{)NXEfQNMo zI00PFAdExT05jSbCY2fmN(5>TfM|Y~ZfJeg{7i~wx9s6EzCOv(wYF98XVX8!nT|uPnG4PM;4?UTcpL zA7xnbJK7<|JMNhQ6etItiGaY;%i;VM_5jXrp#=;CB1iJ{muL{+(~mVl;zz&xiqG%8 zT<#gvAGCYtfVK8U7`f-f$z8_r{s|tZ{Yh#02}owk7RO004MkUo&!YH%vw^W81`T=P zbxsc*TMRb%rF*8krnnkl$AAa-?tjpJG_Wf3-Q%^#Yo7ro4_>@^v86<25EXkztWB-Y zG_bE-;EE?6z=OjKz`EjdD&#`!<=D$-UCno_4hs(P3kwZB)@Nx62N_i?e7f%3;=Cs~ zgmiQD_VYg)A0%>(4JX+>OgP;;OYs6aRY!<~i1^4e2Xl(D$|QhrleYHQ6M6D1b-H?0 zs$f4BZomR_z|p`~8j|Bavo<3@$#fI#0M%$CnhGba<*odWR^$yQtqKMo7gk@GE#4r7 zlXh$8qus!GC_X}mSh~;Iw=VTyR+u>YVpGY}EU@D(=%HwHb39}v=50+375T-6q3^4a zfug&e#9B5RPHEN|q?<%yDf^foweQ5qy~ZF3@;@Cy)}K2W4<|GsE~R8BpqEjdV6?-W$1^frmDvJx+a6w z#kQuimih!5cn1VV1ooYp(_txb;7R&Hyxnov{=hJ2FYh23ZSC1c7g00^pe!l^kNuLjqh)0x|&aw{$Mys&EL|D#umnC z;+nhRw+3W3YxYaIhkah_8%enax&42fy$4iOSKI!Lg_)DF#5g%-&>1!MSQ1TQZ&;9y zNE4K*^xk_JP*em3grPU-AfPlU()(0qD0YpSXiPM=B<{nr2lL$rljnWj_x-Q$|E+Ij zt;|5koPF<^IcJ}HU)S#vg%5*bit+~71&~ZNE{8l`%i=zg=py^ri$zO7esraZtaWTWn(Nwml@nZT+vWf%NBg&u%|>mz@+~^9ae~^+ zZ&~#cO8W@X&-BEela@kf&b7{KcS?H*r7Crwd(fFxU&Uc_FWFJt?aH}^)Z8t~uk8-;P4^P!HtogFDOWb?BVrb#=TNmR$Ww0AQwiki=UIfCo$Jlh`ug^ zB1JzA+!vop>HBw)h>?=7K*6FQIbEeaUFA1WW$;fQt7OLK|5Igfk|%tJi0{tDM!EW~ z1J(73*hHxEYi2_w*{lAJ`26w=s$g zFtgw7Xeo5zRCm_gDS;|gsC#sk1=)Eyxj6(>YD}r_P#!U)j(n$_uLm0e>mCej2R`_x z%!>MomdlO*tK8MtjjX5ugTs}na^3u00=%Yr*}f7~C#rx}_AtZ;G%DDh)DlRmDz&|0 znB_85$qB1+?e%ITsOhQPRh`4El-@Fhs>FW^|u)AbNlcqYwX$+nMgt4oeH#% zDbf7p&`pPlb*z0A8~b#@qUk8YIS`g9Q~pT?8@XFqJKJVkXIp3GmE_aD8@wIF1pUGN zK|O&wZd-$vN2Z6Tdx3wL%zssJv*oVah-;_kU?<14RHJftWlPf)kjoS3a|!;50i69p zLE-)^4&OW6GaQ^1+B#@C!`s@^&f5k0+W3bFpbXJ5JpVl>(gw1}2X7C)?u!;gqa!+! z`S-^1M-kAAJa2+u{&!nyGmvu2j#cQUog6RZMAo~Dyjlq$U;=`bl^BP_0!&jc|*@$;>i6RzI>>t60>?MiwMr)`X*PFn=QT|Nt zC(WR8Kry6LLO@EDU7caBJ^ZOH{*J+ZYKxD++Tw*Fnm2I>&%1?=0BoeX=~_=^$IfDo zpP9`8CksNkjWJXjTx&Pz)Cly(bulb<7hicCKFpA)sgHhPZrY8%)Wq{$vmy$-(?SrZ z0e$tuFVN?SzX`tj=WEfrorq_TYFU_5iI)k#y+feigvxJ5K#mEorM#nDPU;%rxfXa@ zKu$O?p@Px0a>SM>MhN5-OujpTI66}6)*jBGW~?@}3i_K}d~AXaP92?Ei?eT{Z0Z~O z1(|cLiZ2c#a!Aq_EYi=tBXp>B9lAPrM;IWQ-y`yn|ERd9@RZyrYybnYi0$aMu?IE^ z@wcKE{}Qs$$T|slw3M^(QZj~T1X+h{)p_21Ot6L@0hA(BD|*i^qs1A~#eUGGdm_tu zeShIS#ALG1B^hE3=D{@Y!Nr2Ic)_BqoP@+Q5W&Zl#^n;{qQm0&F+SjgiaMVPV7XXk zh|-?)({Vj=(!!y`qd!ngtb$DO-{6HOsFhNs&WV#z~K1_W+h&S8;4h1%T z$izYL1;E$k^(Y2VPt#NYlnhikTU8Z2gpGiui(){*KlgwL`HDi@``YjH5A~*r=PQJ7 zMN;vQa99{Dfgq)U`<>1`?Dltjh(GivW1*F^W36Robgq9&1e)1b@-hXjB@_uHc4peb z^)qsDRL13VKwpkeUzC$~F*$unMsk`UDThePOuAmbFeJ$@Gcuj9k8?dAu{iG3nbT+D z2oPPJKgExTKMf@E3m48VIdd^CIe8&+W3qn$^JNf~4V&x2hiE&!C%0V}E`DoPGA10U z9`Y+Uyw4MV6RZP?#()`G7lo~$MGi?lr-}npBcMBmzR!kqqwf}?0Orlq`+_?e1eh`R zK@JuEBnof(mblS`vEEKP4)a zusnC>bU?_#_=rUwXA%pRG%?Yltk^b8MyN_5PW7yBtk3C?u5eCPRc8047Ov_%c;R0m z%XH*BAp<0+bnF*_svXlHHw;R929o{G+{-^CLumo25vhcB z+?ixE;M7Y8Pip!$^iY9@-dJ={^Mn#o0F{a&kLgg zoJ;OuuHauXevz)~$6Yy#m5`XeBrY>9?|cq%Hp1h4Sjc|3_`@o%c!@u+mI*!Jvn_ZI zUp0mYXAKvSaM~pZ1Hj)WfN$7Qs*hES$2Ba1AH^i%91s^V0g?~qRmfyhKM$2>$ z!0~$z(|$irWoPZ(D{^A;?k$r2jrx>oLY;2cs)N3L2825C&`WfU^wPG}J_=|5vY>Q+ zQBiS45o|%tXRwpHz@ML|gYvKr#Cx#S`RGe{gEAgrJIcsbj^Yo2PJTVoK}8Z=1pfU9 zk#Z52qsux8P7p1ug|rV3mkVEtP{ptdn7~AcJv9E8N-baIufGiWD#Hj=oQp@;7hY1! zs8RC5F{F`y{)Q@B|2{sTJVvrF+{aqf_LIE5EfWsMnF&i%ia#5BLXT3x=BEXAf zR)r>_ErV$Ft6ONTnpH_8EQ8`1IeA%m8F>dXIb6-ttm;cusnr6)_n4EVooh~XSzty~ zS#fDq(N*G)wOF8!1bEYG9^2r)1y8wzdP}nN?oWD)hLPdKdI)KN9Rs$qSe*w4pEy7b za6diCvW4P~xDF=?0UcK>H}9bfV-4RKMqiDB+LC?z4Yd*!r0ncB_z0!v1VKv2-=F}% z?!hlU$Lm11pCiOMuTi*AIft25iM|IO)XQ-XX>j%H4Q zcLvHitxg@*)lLUIb_b|OWJF~7CWlsCs!FL6_}Dm|aPY{Us;s=Yys8+eOfy#FY<-lC zcj2$VL;eO_Lr~lYFqyxZJSBRK3?`OFEM?k09<><2^xe(_mbFfS5&lvBQO;LQ zT8~}LPtVQBNhe_7Xt0R%I^pDG>*iE#)L;Qd&7J*S-7mW=hp&nIdVcO{ksA&ePJYiD z5rY$;WjIN8p}%gtY2dsfNx@!_wUoLDWLZ@94K8D7eTZXG8<+7W3lNi>R1x2Xt6n`M zL2y*CJ`88JaxI=c*#!;@NCDKP-h#pY!#v)*eN6Uq4fzTtaO6KFmDhl#KKe)z2mS;PN-1f@_uVY1p(5Ud{N! z8x|^tSDLh-E&)ap6syZ+blllY=o35HK! zIt_MY+71cd`uij7HVW*7=Sg(`Fuu>QoQdumz#}S!Cx-Au6Phc^Pk>^4On*CgjgN!t z)-N@zqo}#GzP!}+np?BSx30^B@N|A=1Z-)BqS@JqQSw_8aohe^cQh>&Ww6$zZ@GM+ zT;AB%Q#)b}+dpxt5Rj0Sot~YZL!cs{fkiQjM^sUfQ2C=iI?FiYAL|nvKA5L*Q*?S*re|`_b$aUiDm*nOw=^?z zbK1__gQfRdrQ(K%)^u`ZHX|!9Ge0AbKuPEWgu>-ZRFPDu{8=B_Ga__Ufi3v~eaQ%P z_jGl3kIsnBJe5UIECbU1sIi@vn`z1MD*J0!Dmd)@UCI+w?Z=`G>16?ZM)*v*gtqw5 zM7rVM`bJviH`+%{`{?_{X|mU&pvXsaZnMMQaF4HIp?SEUJm-Ga>on*;&T2yXgY{Qi zjUWOrGcwN;G(n55K9zL57p{AC0L^w0W#KucE2YLyAflXmG>nC}DmSC|3>UwW zfC`U1aN;iI&T(b|DM$Gp_VuQAfDY0<*@N&&pQhBI#Rva;SWM|JV@M8TdNegA4P`R< z$mH$zHj|c4*cDoScS{$s0?=Fkj5A6TIWer(Y%#*0yZ=4Tb@eZ9-9QTn)IK$+Z7?YG zAA?%+&!ArYGbn9(P}(pkrcwuZvvV!`&}Y3NxtA2ay#pP6eLel5IZ^4A!}CXJ`0`U( zpe!O6@iX%jCJOfOj-{huuVBy1RGFM9eFB#kikTjs5p*fMr?R`@1{l7lP2-%z0%slO*Q8KxjEgp1cFfH(tDH%+*i%Ef@MZ3>CO0{uxC z-VC8spV0f^zO?qNVER(waLuEz4K;N6aCJB8)USJNPA!rhJeU7C#jD)Pzc2s>_24n~P z27gDsLDgU+_+pK66_xnGjEC9bSOcbqO(*{WU{&#`8qLgpPf(wOfC;;zVNwegxbwcj zbB)s@@?28FUw6K{ee0*&+Yzz(op~~7ebaMSHQ-tIG;ZaUaVBT%_{iA?g~dPk5_&}j zZ+k!bq%T1c)(e?xeFHs^GCC&yQv{o*d`Sd~D;Yi^V=o_~${#1_8@zu)q0sQ}5=HHSGo1kfW&aL@(GDi|qh6D{@hctl3I{4z>-=lBMEMO0 zL`n%(-T`2oG6GT($Ev7y?_1Pv=Hw@^Gl(i7lXfhwIHN((?rQDFuY`To&o2uCKTMWL9)hpn%_4Qdd<~U*~zv?C@@ci3iNca&-JOJuB09Zs9>4hP9t06w1 z4m=!RPkA^%i*<}Lggsv6SLi$$?CxUada}W$#wyQs;4&wq%)QRFrnsWC25iu1Rt_2D z?qubB^1976t9;jf&=i!r)wxucR8&m;_s?=je|LK;hm)-~4OWG&eTAH$GRHc{>hg*! zHDy4JkndL}g60OxnOkat(cHylCqArlK!C-lj6uJTKW zzCqgtAYbEyAum5jhI9tReb=P+1HgQS{2O$2?7$d%gM7jjKJ`M2@k7>EZ3`pW&Yon0EXm2M#mfrm${uwD;oR{byC#7v#wXC@Tf7qR`6#sy|r4 zk6p?N%?J~OtMymAwC$Jcx2l5Bs;`KtmL$i%%G#6S|DKJNl5#Un)EJ zDy_U4XT@KC-QcUV|D|HgSLyt*U)*1mw8pg#ph6rp(tpc%jIMc$>#53qrAV1_xXdK*lxF<$t zB^6#oOOpt8nZKcnxvzh;eW-hsZydoM42C~&y%?2ysVougPbILiH!nKcJwCyOriJ=l z3?(|N|EKz= z?T`D_jx`2^`G@(1tKqNDf=Q*Mp+YS@Vap^;mQ;dlk^+WeHg-i2j>quvhG*?k1p)bswVIYZoK8}_s*zFW06q6>-YaG4} zfOS)#ApB|3n>uk5AwEq$^nBvb>E7AKF|PQ!c1tM%RI+S{NjLy839xMWWj`I4Fa%Ey z!V_&kWGla=e5h|>DbzJNgg}t-iR>lVCE?>m+a)fW=uP5{TX_>ZYDu=q^m%&t zsfel34QDgBoHb0JhgPAd`n^7-(I)cEael&l5;XVPu{>@h%+n@n@ zR_;&D%B!bKvD(zE{N}3uU$a61bcZa~MK!lop-UMFvtS;7!E8xtSgQZU@UipF31z87 zSxO2WFZwU#F|KnD>oK~CzXTq+FJ}1chE>A*Uz1Q0mG{qdEIan>|1=#>8L=j@ey2_m zr_Y=Qn^?4W{6)_tFdab`B8YS4l?hpk@-Jj1re)-0U#d%e3iGjNYCeo5pqjIlo0JFT zqdk&iZR4W4VS(79eg2pT%@F@2yx()L_kon%Z^NftCqBWqc=$e$UEOyZYG_$y^NW2z zHO!v2UPay|{h{_Lu@85W9-9r+HOxUok)c9`;TYvx5UT>+IcYXO7qFqZL8;MjFOZQ1 z8;V|QbS73yb_2&u1vqBRtf(x0c1&7MadCF~w#I``e0~R#tq#q0-=Cp!S-Z~ghBcu) zz_`)bATCxA%C(RwiDod^1zZ&+2~&&iVh~++Y4Q==&g(OKAW!HXJodul19-%76OEwg z+s&-nEIV+^E75pOum1JTPS|)AEB#6BiIviWs2A+*y-d*p zbovoQvBSXs{rCiao(zQ)MYaKb-}eHXihWFc;=F3rA@Wb+~Sv``|ZVXqv{SdMG$yutCzt$40P5R$H8Tx+F}%zw_%AM5d#;1Y$t+8Q3ZKd}r8y zrce5w^mZwigIF~SCvJe;OPhR;cQ0k=%zO{a%xT;?KSR72kmwX)l3#_-hw2mnWC!T; z7jnp&@9S5b=tS#B**E^6mcnLu)Gps|>`Z$+InDi-f6Kg2v@FQhP2F+sXkRKPp)=+| z2&8-H5A`o7HZ44O$+8m!fGvIs?=E8K0$FBPYLw>Cn$L;4=GE)fKul^vUt@B%>^Yp1 zul|RH9lb!EB)ioFZTQ-R2k|%0;RzE%`<7H*p{$tE_)yfL&=4Z}RP_0XMPc>{wDvO? zoO(gcagFhX7fLVYB*6wxMaJWF6y(gpQNJPsAvSo942I}`+esNpb>%ETi-Fi@E(9Ez zDk7!Y5E+~ZcH~|7dk}+5-$~GJdC%y8oL%vTa!1yr!;vHUM{T&X4zfDiL^qoy(8J@C zy=G$disLnP2JEQCXLCr1zK)IVX za>EfI54g;Ww28lPe2E}1-5t_sB#rUuWlKskQZCmn1QolN6ng>WbmJe)0ctA{m{vgVd=)#v-`bvhj4~xcb4YQASQ70g1 zz{$`|@0c}Rcw5`R?M5l=sPV}jbJ4zXO9&cpZ_$?cW`kuE+DtbHj6e(}Q2% z_;qCL$;kHidtTb7=M@3j?#n978rQs8T*tw|$Q!UVpX#7bzZw4dmSivRYsht^&O`VK znp=4G3(a-3QRsh_wL>;l)k|Rgh~Bc71qY8w1Z)4gyaF z{wGM6mH<`d6r2IMUwp<=0IcePgir0>Gzz{zUq)E9Yl}1ZaXTQ=d2kur6i$&oW+}I> z0}2{FWDBls2TMT@5&A7-kw8D+$>%QT0;gmrUj*&q`|u+dESCQal^W%m-eo~r_1)d- z^bwdJkD%kr`FMD0H|$d0Kh9SgC_f?ps~RR;&uqDxCt37C2anMQm50lx4muh&@_#!h ziqpnNAn}f#no-n@O{D-9gki}48HOJ-wA!`RtwzU_bJ@4hH3xQj)9_mPGiuM@qal>z z^wG`4$UtN)ZI&aB_(!P$G+SX!Fip}c{&_HEBx~>&DSOw@7?7}m5%3Ju$EBb?hC7^T z%4I8f$N|kH0od|6AkNd!p)|GJfb5{Okp5bZbI-23hk8t0Ii&$*-X-2RF^W@ESJHg> zI)Pq^@eKxhI`JI&i?RO)U*0>I(z|O{;FG%O_h@Lk-<DN!jAN&NPb zwku6n2_qyLWH!1rdtDFw7`u(9HDlJ#``^*+tPNOY6=oU|)WVN`!8yt`c+Lk^9`ypq z6(B_Y66;8Lf5MwI8=fxILJQZA{EBqeD}F^^$sl}|J%9F}fR)8fWSwcR$&eL+I~W6P z?LAc;u*ohKUCD9VdpXQkg4Qg@NjKM!>~vHPhmac@T^lQs02#egCRr^N}Dcw zH4>W#UqJ&!q~Odrd&C>IZYNlz;Y!S0gFn|I*lV7vfbueKs$M_?Fta0b15+a#FE^Cb zf=YbeCo0DfxWQIDR_SFnxHo#%`KRX=mS(3f@5Z11dSEoGh@)&Br%fAc*6==Hy4C@b zJ)Hqr;szhyPNgtU{lL*eoK1?S$N>I@ty!?2z*2d~#6p0ybv3rO)I*K(&r}-hdd&x{ zAj6SPc7Hfh10PXfUC5lr(L=^R1+=Lngsi+Z&fHRJ_n74dyp!!0t(I#5JWZ>)|XrZRL{KW{Nr@N zD>w4{n6(~t-q!-tbD-dim7VyrUo;e%g&gILF**f$&G)<>9+FT))IQkv_UI)C$-dp2?cE-DDPus|9&g!%J9kwSF)O? z{eIQRo8N&iB3=Ic;MdH#T&s;N_OVBbiBo_-7TtxVMLjGnG|Ar??CsDozQkf@KB|nY z466w92y_i~i^>e=7=o(P0C{npELg~uDc}Cv!RPkrxwO4*ZS_it?o9$kaMwV0PVlI< zuCgT`)-IWk?6{xXu-tN7vRyNYkH^e>L|sJ7Nn4GrhjxOo{4>^r`p5TM9}s9&3^{7} zWW&cxxMb_+BgKrw9+UrC`1r3|O`vpa66Km_bJBuX&`^oh+(lwt&r zC>5TVz!R;vBz{LNMDLi&IZ7YW+~ynmZNAx2C0^+f4=Z1H^!I{Qe&Qn8!`teIzY4*e zC~)K*MW1FM=3J|&6lc6jml*8@}ch_zfgtYqWac!DdBEWp+K32t+lWVwh z&$D3Om76ImxB$bQ&UV_%v0a0Ft=zgAL^bpad~y?g08pPFo!Qy`2&B{tQ&9E+XthkofSX5nVRm<@Wu=8{Z z3@i>Siv()a$zG*DG^kw%lvi;xgL~YIHI5lf$B6K#*u|;g8SY781iSH#(vbob;PPQQ z`|)om85afxW>j^0P-75U{>&0F_xD-WwsL$59E$CV%d)TJmlRmnasq;#yxoHwF54Hl z0JL&7_8>C_#R9bKT3kk4aY8Qs#G3<#z+Yh;1JuDz<-xAM2g9TXbMs54O^9Da^y0Lr zT;C*+l|)=2KbFYVFlEtc#{x>@e~b(rmV1a_3Sab%%a6gHn-t9w%el+jS?sW4@Zn$Q ze)k;V_|G@JfM3|7X<#u5E5_4) zW^`tEb3bioLHLyKS|&QGOF9^tSm`=y)ta>0gK%#%C}!9jxQo<xY3ohvCoaDPQCh4m*}Fg#dBR`A^?2xqUw9aQc+5klLI!({I{y6KGai~XM5oKo zdd+CgY|m>d0FB|C;Wp77DDuJZF(?wC^D-G-WFstw%^)02S5L&o#ZzeuuHxq)Dwy-J z%RNUKyPL;G*C020oE{o%OF?i18^N0bLxZ1Yuf$uaWg5z5c=KEMg*|;}_&uJ*UCLrH zB?+pO#l=MGbH+MlF==mX0`(fyo3}c5Lu4)Jygp80yD3cJrV}f?m%$Wfc_arTA|I(% zy=uQ@{iDmTfiQ`ivv-ypy!xm|K6d@NWl<<_)g|xyT85o^ZVouq4i7x;iX2Ytn}3T= zCTWr8$#fOZ`_MFnt~^IhYj1ARF-_CNcL7}laCSPST~)buE029|2A`Vm#QpvQ3-eJP zNwfWDXsEA2$50XMi7~+h@)$8UGx0N&!nMGxse{%kJpMlDgN}H`nZEf7aM>wprw$76 z?w>v$^qwDoVis(G(65J?4-k_vw4DS)_(2IO!p9&qbvhsCVmWA3-%F90@L|~_(>XK|GE$&+;Z?f<{JMs{#tOd zhcr2@iEl}q0(px|`CDMc`zLkyOF04$6okudMkx#&hCd}S2T!A?Nt=l|GgtiZ4!Uj7 zq_IP@=%J$pLc#DUsZ|p1uW8zR6*^fM)j{myM8?EPvdyS_ccH~#1;@AD(_w>|pKeq-IfOW24IcvfEQ++vs%Mo`YANP0>|lC!>Xl$S4KrreuUc zT1aGXHOHe}Lw>l;)PqwTRP0yei~Ub?M*HuM3c;lN9tlX#omSf{jB{ctg46l+ZT0fX zQKSYTn?>(xj5;}cyzvr0{3)2GnaYdgIj?iRXMjm_Ngsl)9e3@u#9O^GB6GZxLy<=H zqrRr+?W><3M$7$D^Kt>Yjd|G{+pHfEC{2j#7>R(SxmXI{PjcuiAWx?@PySMQ!dFo^ zU3D6u&yzEysCA58Pb0;TkcB4-jW4+fX{5M+w;$N(RaHV=WjP>+hif?cApjEd`}m>3R~#=4Jwy0f>aV z;@#;FLtA{646=6A_B`1Y%&DlY=`ZgBNY3N}@~JIiX!cpxb#jh0W2X4q27>LUd^a%z z1}dSifgc0#V#CRjw8lhgUM2Vz0|X&`l^o9-hi@P6HS^}=2Il$Y`Qoe)&a3vi<7FVe;XbI)=*D?96MG##$um>k({<_E6Zpi^OSmqJoq6C*N{E+-<@Bzr%%(2y9r zK(`1VFh>Ynph?b-My|PJobNfmGl2w-)kl5|*S%Vnc%>&zT~7`}`>x%T)ci@H79p-= zBwoH)c%c}`<9%qyP>`ZYh`0mbangkgBD69jLf{b&9v(v)ccVwx_r@qZafdr6mPNlT z$uQ&RyX0CXd#a6O$Cd|Q53Hgot&tA?77$Z||G?kMUr`!sM$oq)Jj|=U+*Hu$oyd76 z1h_?PCBHuE3UN^RIhpP*a7cD1xVAT0RqqJ)z31R$i&+(YA|jkP9TOcFz9`H(A<1e< zTF51zgy0)-t?@+(WfyXjv$Ko1Uuv`PWtys;T#;CLv5G(np#v|rGNLIG-o-yq33u@s z*6x1fA-53U6psnf429800g?QHB54SPj3kY*Z^$VE?INSpJ@%Q~_PTI-TZ`{!-=~=m zjc3Rl)3k%BmZIkL#6hT#zR(HrdSU$0oJ9~`9Zq>poV8y5p zG}3i^!>};o)ajVGuti~Z35nKR!$g+S@FwLbRyu;8m1Rgx2P3ctBeNu{A`2LRrT{`i zH^3YTO#@&C??Bq%yN3m%4b2X<(clt$S;$0ov~P4!(KkGpH9Q}rVk#13Ablx&C1N-6 zN9fkY4Do&FrNgC!rv9lY*DdFvAe2KIA&v&#br|m6*};zvV2;tBOyeEj@7-#j6;%L` zkMV|EBYk(lw|(2!A= zN$dyZGuo`lln#?GHPHwr3!cVU>rYKjweUJO^_1zpO z{9EgZ%tUV_FGau{{mA0_A5zRtYK$yBQE8g#BBR|2Zk^ts!oQqALg4|HCv}}6{zqnr zR;uHLMrh$q1GOIS8N;~ip|UQbs)Iy%@e>*YvSno;h6y|!iaKaYhiP7 zQz{IPJxy5C(H+J%0d*$+_jJY;FBONlCZuj+GN#B#HDS$h{5qa)#Bs~G_hwrIC;EBXB%>&GW zOhXCwH_j**U-*dTM3KzY=+x+>(|^y04pXzh`@!Nuq!oX4QGMfu#JnZvvd&#TpGTYt zv^ekOg15!HFLH@ZEniZK?)Krk8O)Z;EydSi0tyDP7PPQV|G!SaQAinL!ifB${GxpL z0L4YHYy018uZP+X(zVZ>sX%{f(6!Ga@w2~ce>TM`KLu%wa7(xxZV7jaPS&HjJ@s;v z&heq1{QW~eM!R9p6-#;!|WLc)xj2~BvI2!z}Osuy%X#~9Y7v% z;fC4Op>6_W%)fT&BxU$96<3??+H1q@0TTolkUy@()1Z?)3vUskEofd3vma@Z>;zVU zCi86E*|-SUmyKBzaPDH(l3u2gIYcT0WYji9Fi+`vJMRgkx1N(DeyO<9CU`{LGGdry zMhq@alR|n-8g%~nXXE(i7e}9WinqT249a3&|CBMZSTKdgUkm*XhKzMe@oJ#A=a-%Y zMYv@C7+fX9-O@k_;)u|f!+uh><3+AfCktCCElKWfkhf?0-~(f%GDb>iQoAq)8hac~ z-**DY<%bo#t{N`k!J@V@Ctsl-xUOHbkkK%;aaaowXFKPQ4kFbdWF$l14YP+wCj#MK z9C+dTX&}k|sUC{OOJ-`KOV=*>CHr3tQX43V61lu|98)v#&vwnS&u~=3%R)e3 z2rB0uG)qmpFxbM+$jeAAtT>?D4?1aAuL$dYsvNURgJ6!WBD|H!&i`E#@}QRkeifc- zO+DSJ2bNZ*U3%ydj~(86YVKFuO>bCq9V$0hbJKRwbu-tA<-iy+uOD4V~+}syIGq>_n}I2HCJtCJvR$jk*$?lmb*(=%mV!&V~hcEVi>37RGiX(6nIHo$`l_8u5NaX?gQiOQ= zaE?6@d?M)Nx>KBbS=|q1_w|Z6z8fr7g!_bhM}|av#DGL5Bycj4FDL$zX0zQ^JH*e! zCcrcBOzIRlgg`%EAhT1;lF@;ZRIe28q=5cBPMFy7H@90RVVq3YOot2yOonmBAwsir$@ zchU;;^|1}|pnKL2Q$uNH2~6ZS(%ydbQmiYn{5X+l>u}y3T`~Gh#=Z!VVu|#e{sMFQ z$A8T!sPgk*ipnn`69`G*t0#LU`CRk|4lQ4t8LjaC-S>go;fOTHG}}~|-Zdc)(2NIY zg%~YW)2j@#3$_b7skZV|y{!IG*?l#=V&C=VDoy>!z&s_rNw?l&s~O_! zVNK5y?A`HG>4Ko0@$3SbL(i37Y0A`Gfr}wj?(m1p1H({GmVLHOmMvZy!nuV$8Aoe- z(Mp{%PJ~s6Wv~rq#K8O007um>;rMNsnkk=%VEUkGu!_t`E=op3hW%E@LoiL&LGD5S zygTeK0h9bMCb<@W0X<^%8Hx$I>tnguz2atpv4@4}~bs0Inp z3w9wRv2qelI6t7hm`>{?=z?F`moJruL^U!AOP8SHAgvIRjXjw%?+t>}}e6`Vx5%H=zG zb5F$fMe2X2+k7a_9{97&L52TY=uhT&B($Ya&8PV?19(+$&1a~QQsp;gTI(!DV zQcu85tPfH2ju1t^d)P|?s3*T7P`RuhM(UFJD0)<55UolBSc12lZS)4&OsJE&$FuY= zo*c~LoUIQR2DT8lbIAttMva`~y)~TB7F$TpC2&bS8N9*yo8tyK(dFP=Y3QrISN!Nj z_Mb&}%1ydBMt&Roz6&KNCn4##-W<=_yeT`TFfcu=;!1H%;nmAWOD!vHwKR8`ZgZUY-{Fp!?P!F zfKWbB?j_UrA-!BxcC0$y^LM~U_<5^%^9zC?fmdP>A#I6ID_>HUJ_YU61PotJoC2>3 zSM6bnuPX*RdXLv}Lc#FzizX^(cz8IwyZREIFu{8Ukdg3L5t4|e-4?zXL}#QZPBw$e zofsx<_v}2h(>j}9o4|dY0&J?&iJNs-H6pFF}WHXQ#wNKJOS&C(sdOB zK63lWO4I%dMV+Ef4YNLI@o1%p-ICN$$l3@c`rv|Gyrf8woLjSyT7paUkUieW+-$qW z?ua+>GxaH1lvbOFekjWc$qGpeho*wljlDOu+fBV;qq5w)%nK(3b2^(lyIOh;syXKS z)%W=a2LuO4h5l`=Cy+brNLH92>-Va7_6HwU6uA2Teem9fc%Us8%UN*bNq&~Bm4 zR9k4Lyep*N9>x2(TOeejybEQ+cFJMc@@mwQ>PsAy?^qhu`(#cdaKAMfMV1*O8#F@| z0#VpsKtLEhj14dWo9$p2L(~MD*wSn{p`nqWGA>dn6^Ml(bxRt6t{8C1cwXMGFD ztNcilbp>!pv&l-M-YeodVXazN*ZvdxPn!Fwp`Sr#(CpD?Q|49I%<(EUYP2pRj^HE` zzJ(QowS8CB!aEPyYuO+7S5x*uP{w2+1iYdB6AV}aP1(@mz=n1L9U=9Q1OAcD>X}^0 zvP(z_O&#a;)Wr*mmK3F30#P2djCo~&9&)7r2GmE3qhO6^mjE9sU;0Be^@p&^qd!#3 zz$*kmok49J7S`L`-P_);W>{mqYrDD+Ok)sCV_Zt80InS{jY(*vG#jiZ@IkBLxL(&D zGw&qk8i~iFubijSL?xibz9ICK_V<_er}YiW-No!CcgkZTm_!e7C4K;E&}Ss% zird4H=}^8pI!RRW0A$VA{*g6*rOjg(R3iQWiN|;8PHYexqj9Fv8N>dj;8!eQ_Afw! z7x35zkH297fO-6(uNU)`gVtW#w3xhrOZ?{2>guAVdY7h?B|g_0I9_F@%{JA7vg9j? z;K(wUp|xWL!#_fXRNZKpc>XQOCK|38b{lCM{BfXT++~EFwSo$RZZjHOiw7AfX(k$6 z4^Ix#vZr~JIW>=|9GooplE*GNHnCi0f+P^1B4G?KK<9qM=jfgAb3ZD7XFY@Gum$c- z!fpQK8pR#30)f%vI2b*?P-=h6o4af5pa{}Vw+sac(FXD1p$DSl;#YD*;e1)2o2>MP zq_t?oTO{pA^9Cg&>}g-ApzkI?Ip_q*7)}5eJxt<@=qT5}42eAxdVX<; z;rR>tK&MM{fiv-gt^^vNoSs(Hx^N<&Iq9baV)6!kI4GLFK!P{uFy?;vz~e6XfrX|G zB+454?}}c4PtX!CrrWfGGCdjEG@_wETjbghT;rxfO)TZ%Z<*&qRbl`(KOiK(fXOOadPd*t#OQ?1&7lP-u>zr6Z2w+sx=kxIjyb0+X1n22D%riW+0_KM?Uj?5) zz93R9pQD1|_L;;%$O+`RSE5t2s4K-;!5JZ`VQRuEw|4Du!)~=ZC9FRZ^*%=s$a^=PZ%z}QE2)%&4l2{Iao zcyJpDTl9V+3=w3f`Ov;HtNv<^XN#V~9Q3qL&ywy&7Qn^JjxGg6L|b#O;+lkDSH8!e zcawR^rHN=WI00;!F-muy%i`1!@BxGnT{ZE7%W$Dj0z)P{>peOF0MQhX@`PN1+sksl zR@0H=v=1ORC^sM%KD6ch=dC~Y|0q>6z3v_072p}91`Xu4>h{K#X0?;WstDkrlK0D@ zvmp8x+KMm#i1;GRf3oj;?5&5qzoS1zlJ|Hk{P)WDzk@5r!dS;VFft%4BnBGJaJmO* zKvKa)G&9M<&DJj{+CI=D(hoY(0vfu*76;<{@U~7a7rp=i1T^P&X+TG4D|CeR_B$h8 z)9zcnE$!i15MgL9LJNnSMDx)hS%C1aFi<`!GLbiB^pB*CvQ_sjotQCU31=((9kyWL z>7nF`HP~b~*0Ki*IG8u@EMj5m=3rQMaSHC>K99(`aW?8WP0V|dmrCemI2srM>q_FmZ7!3mJq zgnxqTfCcZvA3RJ7A-RX80`P^|AKF-$1T<@1nmYVQZujQJ#CV=ha9^Af_Te6@ttwtn zu%s|8IUB^pmq93N41cp1a(I;`;2AbR3z=b9#a?TLZDv%i-=%OgyQ}$O4geF;=Ztf! z&)LUB6Q@s~ii=nj>5!0kLYd2JjBAK5xKNysot%-KTaetJgjN!Q_#8la8Z#0f0dnx{ z7S*{L{bM4bq8PqX!l&|+3TTr&8Kj1~%p{e(U>^HH66%?VCp$caHWls-ogDW{-A0QN z!Wc`)4quVO74O;}j$5g2jrnCTtKKIiPIb>)&}UVDsvWuNgjJ7Xj^i#h5RhFVo7_9C zid^gJIewL9O*WOp*OZv_!GtwPsrF_?mG}rEtC}l%ON0c)l;htR=~vT$2uPg#dW?~R4yYyw;)~)J;%xpvf_h3f- zWFSj%uJ?ZLy}`hOsPy|_v>Qfh^2ej-lTq~9IJXs8#?w1j9|JsXzelhU| z6gtOm88+J9F6US@ZyDMP7@5)uUD92{M9aXMKnrl;RT0@91I#iVIW4{4izBhF2OxoG~=} z7I*-->-Vxwkz-bOj$F69Tfs5EzIkvDIKSQjX$)Qm{=w$x!hU!iWj*?uWNXqQeeTj4 z7TPrArVyq5EE*XYlNxJRCI1>huv^n~gmPud9`5DQyRyA9DA zMzk;AFA^dPjOk!t>mx@j_dDf80*f@FwX~t)YR#pHVJA^%dU|a=CL7Bkf2Ipwd z*P+{DO3P?U8tm#Yns$%xy<>GL4{q4A4a*JNPk@x@D-lKv0q}uR;o@*^;3O|cs*2)8 zaE(YRORNBzCOe`_*$=0UkMYt#3Du$C3cNz{68erkfcT&}|K)u~{YM~B?28Q9X`XEs zueq(aEk<#A5b_m&8$k1As9MIJN1##48svr3FBwpWi&WXuR~?geF_G$}3%!^LON`XF zgUUCDpFnq|6qhq)2y(isO>s5j*i2My{P!csG8spixH?i5Z^ij!tyiNB~7G);OG+Qve_c-)s>lUubo_9NAPFU1uZyIMh|QzBTn zN|dq8NbVdF>-dt?;8gF#5JEDQ*jXIIqRz;~%n!5C0T7Z3-=KYeG7FpvitZc}!1Ml)U0msZP<93UvjxiMs@ApfbN2L7X{nNZYB#uiO{1OK2j==ok}WSa+~>kIhs}b2q=|qXGjFXAzMSZ((41I(j9_* z6*8by7BksPig6;foy0fsV)K8^>j2#AP29jty{<}ex|DMmf{wjOr;JsVDrVrKkFiz7 z`yrO5k+QX8Ds7PkN!i*fZ5elF{{0(Fz;8~%Z|3?69}0s9YTN!8k_vzBHyQFQ_Y}^T z_uTDn?ta))-!JSj@95|-?@3J$5muuc^<-$=&^3 zsKs({0`>`J?pul%K=LYAsMOF$TtJ=NH<0%rHravM<_!Fa5`5;{n5m~yVvY_yK$G|PL%~~V{UaWr1UZcZ{krxt?<2aOYKJM)K*ckWt z*hLX>u%DwOo&bS$8#qoM4w2g+OklPO@_PtunFJNg8zLt?x}T!O6Kv*OGch`#=n#Q( z;Hu&b?0Ew!N2nOiRXpf@DvljtqjQuI+R35SBq;m9w$oPW~GJV0ZF#V#$(ayuozx>hW6bOgm@- zL^PfhI<^vMg*Dl{zjaNHet#7wWboJvt1&|98$^cgb6?eJ+Yc}#b20Uj{Zi1y|N3XVVlX>eZPWfGnttdlBR~b1QigF-dkvj zR4EArLhph!L3$MkNq`XSUB_`8ow1J23EvZ(w}Ug^_I~fJ_s3ha)(}HEXYYG*?>T#a z%FE$T0-luo^;`+iU-=ZeuM3A2BLs5CI~9i!V9+N|r;S#ktt!6tU1Z82Y1?%aJHU&A z=YKJzDZ8ogDzvOrX1m9acxt`JB(&W+Lmn_UENEK*TVdnL1j|mgH`8Keu!)iz1 zhiA~hd zGRK1-)KhTnQtM^nFgn+P!x&dfK{cTA{#MxKzqA>vy9(x3V(RAh>zM_ty?MsVu=*Oz z-lt*BQM4$(p|riQRUlpuotOJKF*wOvg{HZUrLpq(p#)+#j@*YL&2YX2%HJ+zh8&NH zJq}e_h}WMG*T$q2Y83$qxN^o#RC5j2^q_NH_}neVqojMhzBFR=0#Z_7>;-xp>Jk@z{-V{9-JN^2-xnl(Q6yq{4P_5Pft+oKR+#3kSq9S3{KFOGaB;jkx5?nzLoj*Ouf_proW@#u?*P)fv3z-M*5T}-jAH1 z_%?it1P7^*b&l;|&y4d&I}?zz5OHAd)*LnZizDm|K&oK9idi~VNZ7QHKC(~YNBJDF z+v{ivN|p4UT=0Qv^{dt}^Upk)btoCSF%$6I{wIo~0CJhTgi~5q+E&y^D5qgN>ipUB zXCnb60S_zE5ZpYh@rEt<8$5lRASll{Iq28Ep$BarFCD5m)_BU`w66Px05Gw1NeXzH zeZN%JKnxzg>-fxBx(62xbcE`#FD7UKzom6pvPt#ei2j=g$xoeM7cv)PPgDB zmM4_QLEGyhcvoW`9G6Ugj4sJQfH7O8wR?Fh2n*Y}T6&xW8>)b+v(4_6hTg0!=bY2& zFfAxI`fQ+w0&bV31Y@r+xBdT6K2dbT`r{?%BqCaNq6@1wGrY=I^}G3_Xr5Xxw~y=!K}zaMu`B4ZuH1)s$(x6f@@2c`*}QC@sP3g~tm{^Aq?8 zG&*Xowbix~WW)vQGL^vEYg1HDcXK`C;_T&|=_EW0?D4HQ22^$QAL48D(|)!mYz{OT zu==ZOMTMHVC64ZuS~l0jfzLH1f5ay=YsF@zR3v8-;d=AW?bTdwl3I08%PGev+Xs03 zvz9B_AlIx?fSIz3`Chqh9$j5uD(x*BO6!f6=5;;qIneiuoU6*yjlQ>Fn<4(a7mbHB z9Y+sGTB*{56}vNz_hqsCui6bbwGqe$d{};8F=s4CzeA3;sB$zOax@-tH2ygn|0PGQ zf91$wEJwd%D41hBRfk2nez?O)0LSM)!er;Tpwz&}>#wDNZk-qvL(3ky%A|!HbOW#4URSVc=j9 zR99WoehF~1blARnpf#G}oAL2oT5(w^<-Bwv(s)&ru_j)G$7Q35=34pUtm~=wT?I~@ zee}z++K;)NM+K~)2sQkz@oT!LvE#;IKdx_ZWYBpwq01EY$@{DZ2SpFN?{o@ek=Ind zK@$fCWYLnBLvN)qa&jeb3By!?ygPvFzY4g10IEz>{;-Cla2}+{L+mx*KV`fWQQtp& zNe90J>g^f`Vg7`NZ(&0|QCrCYNf8YCD0kRUO(0A_GH8)8O5TOjj8Pia9zvk01fuD3 z#P9q5DVik0lO6&Jda)ecpev87Y>nkiP^^Vr=T?0V_CmH4TD}c0rz5Z1@ZL6ksLzT9 z_lc)7GcO!83%{zvBrN%Owzcz`*?o^b0gFARnKLyBC_UNieC zN!!rN38Is>DLSlp|w0i4C4)VRj{^mC(kdYz!)!S5WK=7Nk?dOnH)Ag|} zrHj>MMtX+E65W-?NT`R;0u&6-8bq^@uL%3fP=4Ryn@{@I!q4CdHyL;jJbrH?HC+bM zn+|WF33T-?(XhxwE)$vb{n~xEBT*iE?MoW_fv)|vk@!=;!60bY1`nw$iLa~b_sfR< zKzmd>+dY6<9#1Fdf6!5`12+RCgIw%<3ROaKG$Qx=Gu*sW*C| zdrK3C4(Jcm5At~E=pW?0;SuMNhg1;$+5@YdTtU=f)L=NZ1BhvQ|Qc>3%e=^@!(DFIJJH7_z%POxfFxSJM!C?ptwvMRe*u6s

{5<2-vx(%)aJf;wh9*V#9!{eNm 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 eJ@&2#M1&1#Uq1_G4+1JVDcY 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 =xVV{QSjzqCz^MOpk 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�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πr#MBgF3fqh?a2|1W5&nMe)Txm&EKU?9@6cWu-2 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

    Sj0R*Gmn!v10z6QIP21L$~QLkZ?K(0wfeUJZ= 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

    q+w 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

    D|L~1F~lcG2hfmc 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

    Sct!1lOXe+yrJ_L2lv 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⁡Ifc6V1*u*N35*C_>=)%4 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

    ewEjYEBoOR!A{ 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

    e(JCFV})Oi{YGiNxXCws53d43 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

    3a%d){B_*I;+Uvf31HK(k;K1+Vx9e(94d`)0 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

~C%4cO|tBwv?rd`+z^BYwl3{^NzaKqQ)R8 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�la>X2E=v--;4Py9T?2JL7wcF=u>@q%OCn5D%Du?qH&T zA$bH@bS^Vaq0t`&h=5zKFeMfRW`2s`4FMZ%&Cyvw8#MnaU!KX0Dw@0hkN!>i^? zN+8M-Wr_`?e70&(#{U4i`w;|DRaRPC*i7?0I$S!f3%uHz`F=I0d(7&xO4BP+D{0dI zI-1eVH!QWPLJn&9jKbTO%TXVc+8G76o-UOmYq@ZOV`#AMG*NR9BuYY4qbCZ6RCJTL zuqPg$vV0M(P9aP-QK)#;__U{O+)jQu_>mF|g=h}V@>!JYpt5_F+2up8KQmz8rs7SM zpCadsez94lYaaq#h?CARxdyFj^QVikF-X{-H`htm~4|ykw(C5=)(}VBeys2iv+I(2l1YB282?dSB zdtyOhW|n-Bye(K;@HOr~U6T&4xTsx)b0*3_Lzn$GL!Zn3$!Henm6_M&;FHr0sNA1ZmHgv+d-g$zzavq3ThG9c(WucVo zTsG7ie?mBZu*VO)dkar|$`E<+@08PKXY!UG&Tm*+Y^ShQ)F`eAYzgU#Ae<1gB!Mir zgpLK>iGj^2uuB3em^%+S4?Hi}5D6PDB#Q(Ecswq=Ll&l!InuF`{_6TxdlnfepHRYd z6oSgY3llBoWShmz)^$1yKMnxkqe__m^hd3u7ockkT0eUC;4H=g30wV3u*MCm$( zZtNzDatc(NWQHc#awkb~HU{ZftI9ZbNE`1B0O2X9jnS24Lk7kDtiSo#Crk{=y~C z+4)SORpjw7m*!A@Zj~e7=B)D z$-{i?hbccGOmV2DP+EwC=`FuICa0{iEW7*SGJkT z$VwBW3gU0`;iiDfLao-6xRI9X`#Jrs7VT$pT{>|mYdbk*+C!%_-fV7O4)z8F`vf`h zqC z*KQI2{rZ@x*j}e3eksyLH>F?9j;@T$TgH3k1sTx{vE&ftIKn0G?pX;Mw^|cA9P1id z?3-J9kOaA-t?SEPybGQ0Z@&McJFq7>pXb-{nBC4IFPFXp(WhMIBKs@;1b_VCh#yQo zM`_DCj`NxQZe(jXa)yT9#paZaifW)${B>8$XY?}{%19dbF@l#CB2K1PBpkelYIPyW z2`RM8dCM-f>cpb<(rCv?P_#hG6PrY3NV6i@9B5g?*yyNfllSa1lSJY7;->N*7Il}E zc-8nf1Y=_jSs6}LlG5YSLU41#uo0IOPaZ~C!umkidoEchD9Fen8Ro;J4j)P=cNZ%kqaYfrW)v7yRnj9DImW`cftBfF z*E3eMWJyNl!hGTP&ZW}US1(_TiZew~@X*V*#H$`5?V{R3cn`4Kan3Pg@}3>@>k;I$ zIy7M14_fy@quryWwf1J~#diDJ=4SiW?!O++4}Gw;zwMJ8w&87khy1LJo90 zGga5Qu+^4qr63qT*qf1w+TVYN1VP%inkduo`5V=>GRKERj{dsemZPgap7 zZJQ7Q@RlR?fWr%wjX7NM3_A%d4AoIMz(L-hkP_P}Y7o~*E4=IdTSEv^r0`?OvWuxP z8Ns)sVS5^E$%c2Euv-za_p~#t4ux$=7&jDTWswzk$zs8pqGvV3?M=gWg?M*qq;e%v zsYps^A#?kgT&ogy6mS}(T-?q53_@vi-=q&t_QIO~TldXW|MvbE()%Ajv&ME~aD^2g zluCc;o5JM3UL^CAJ1jLCaW3xQpR6NGAFN0D1iAOu;MuJAPl%Wa^mq5Q_e)AUpMD|T zI1F{y5P=r&7VZ>b7mXW1WNLhqq&~YQr^>rA0A~cHq*ug{6&F$4JLon>13S}UTMjTt zKr=ffj<6~Owk0Nu@b;5QRwR=JqO~PcHA5W@BaZlP)loTOiC8S5#l(X-lV?%lSatIT zALqo?-O|q}1n1;|X)joIHS`R(+Vxh{*FP|;Z)mM~VBHfT#RWS8CwhSyOzH>SM`s`K zA||`ot#bXp*|mw}I{Mh)(wB%U7B$!KPfoaEk)P3z@rkhrb&Nsf51#V8W3mv9&8m2V-CNJmW;{EQ(Z4ICH! zy)NV^woqq<-H8K}qHlXzzNV)gsWZ~}PjJ~UzAj$TD-rbHTNQIVZ+1vWmwTezBEJ5wK#iaYuO&gMpI3byl0b zkL|YVPHFAvGwCtwb*@?bYV_pz@jn&x6BYhcXi8KV0P}}`D&)yif5y-M{1boEq00M0 z-;LeGb}(RWiIrvCI?XNcC!EI**76Sm(#U-726GI$ovPdTXB&x9->ynpcIp~)+HRZG zA#dbz3{FmbUi;6%u^zgByBOEu`nBz}#f8xCQF8t11RibB$p;=L%6$CTq-50Zx&IRn+@ruJY>(Fvx_d zCd&>hcdCw2*ZP%V1Bx}%NeY&#SK%#X(7;|tg-Z%#Gj;WguH=EV#;j#Ny{IdvNL$74 z4nKPlPhgn=W2Cp?AYqIJs3DRX+n7_ASDjzxR_D`#vJPDeNLA`4zqqf!CzcV2d&kz< z9e7Cx&{m)x0PmekkqQg(Zn-v@ES0ELe$e>%LGy%t0+%#UIoa7#Nj9BK&M}5X*4X`# zjE}0HavQ=cx!PaW z^>I&dXHWsJ!O;uX3CbOsI1v-deJ+?d;7`CuWa5k;Oz?JES;}!T+z~(y#Nwfb;;JY( zfEsLixj)vmeMW!juCnTVE}mpDw?b1?7+WsB$JNO4D>>E+m1GZ9(h_}Jnbmzju26;9 zWkp3rItBcQKsOivpn^m`rsfONqw_s$0}7TIhvtSB`ier9kt=6e4eDZCQdCk5En^TA zCo-EBeV6?W(k8X)#3=(k(XREq1u5p(pI3$?OLCn9gIgI3Ju=sT>~qUV65@F`Eb5iE z<&}BW1T=>bp;CFFfLd;eyY)l0W306mFUa^{+$__`au^_&GWybjRbUdlVkL4#- z#J$86+WN|si;;0Acv1ylL9Y<{r>M3On>qGTQxh$29-^Bfaom59-BQ zns9;Baixv-tTR!?rj@Hrd>L&EHAjuG3KSrnQ~Yb!^L`q%&oMuG1K)JSy$p}QYQpba zUqX{BP_MET^=Iesk(nGLD}2*(I2IUK#aWU|9Ns#uV=70_ad{8467f~okFSCw1DvDC zZAL~=3fSz-+EZkW1B#-Cr^dC3n#8q|QvaIZ#&E*%$5H}Wela}`StDX#dn#;|!n??L zNxQ(BFbow^L|AvsBr5VyVRhlN%7-nD1K6MP~;u#hbkOHmESjyaoSTi{hjW18H} z+bYO79OKwt^FBD-32SB@pVx6AnOF8vm)!AV#E;Q7TkW_x6wm(Oihu2ShRJ>1XyzAh zSYiCwwX}?8qzt+{nkXk=N-js4tb1_qX1uL zA4}h)l=G<kwh#KW29im z80450IFx0k@S&S>bGPs}!sv+wi{n*uK`;QB)irbL$H0N~^1WIt|p`QGG1&(PYOFvfm{Ohe3GI7vDfTgu)#5 z5EjkiFDd_PB${|3t6ynFX?0VZ2w58Kvjcj#9aG~~5pV~yiM0^=Tcr|@p!?V@Ei!gzdBOekSvR!c^UmVl3NsQu(1 znY&J$Sc;;lIXT(+lEN{kDbr>LO|n8=gS>4ZN-PY_h=H@eA#@l!hsNkS-A=s+hO|s) z8fG}XRs&80e!tss4^!Zktgt>Y+P*jySZR>N9H*Rtl;!Yw36h|6c~oE|ivt%la*b^{Z`< zlVzK>60V^np~OF3{_Srgt|PINMX;X**XS{nk3q_K!jr{pz;R3zd0taIM&R&CW_U+QSXZ})g$UE`!n-rBuqov}Cx zt14KZd)2Jce;nhYer85b5vd(_&GDM$&9v}T?_1&YX!FhV z!c|3?$$53lS>!)E^nzVD^xN?FU3AexA3+Rw2gyrX39 zR&8NoVMuyHVQxWTesQNqk4>4!DXoJRd;GHEM1d($aCREk&(8ah?O?>kXD%$ys?4aO zA$FJ|4>3*yV=7WF(8$Zuv)ZKDsytJiDNUE+b>@Ez*fP$xKACD(f-N%z@*XF0Ac;h) zk^L)CZW9%TQCAp_2~aUydpTILtti-Y>xxVX-dWL+*$M9^uZmBP&WV@MyRW^kcW#Br zHTEE@aC4Q+O;*a(1U9*s`<~W5WVzQrD_#_k5)DRE_`J720KtD68$2xYJq6m7BbleI zw&#Q(u?UMzUCpii^>|rC2lNAtJ*~V;9LqiOFr)SLFw{I$9G-ct#9Cu_o9^hxEJ!hG8+1<2TwA;6ZVdLN&v@>^$0vykzX_k9MA{E)f-p z3netvQjVIZ4K%IMnU6=IdQ@?8W=4s%LuU;3lXo_e_w*3;6q+9MMg4T!NEZzil=T^1 z^TQ6F#nu(hxn^$s+OS67GJiEo!u2F`5nLzmyB7(WJ%quQHn&*TOTN{F;C!_c9-{`ZPK0 zMKohkB-7_k1VBA}3$z?cXD!e3U!mk~N#u`WV0h%=%l>CWiZkYuN^oK(pD|$bj>s+< zv6l|MRy&Po(PMiE?*Lg~jZli9)Rm7 zkQ?NVHKtEdMMx@@V2+q(Js*<8I0G^t8TKO;D`OhjP2b%7l!ICQuC60kuVllKo9u}W z7%WEUGnhT&O|uW!Elh(~n^leva`T6jTD3Zr(YBQg*2&$-e1naL4;v0zKk=NF7DytVVl$anvohIbp`fNc)Cn?VUmvPv>abR<}Dc!E_t+)?HtU^w^@P`F8$ zVX^K3GUuaH*639I-%eSTGiZq`dBJ;RzK1w`dpVqR!@n#nD<~~3 zDz84)f)?&SsbRCmM;)&(2p*=;u0Lnf~ncw}sm~jtu|fpU;44goAc+3k8(0 zBoi&9$xl+1(-z3v!-xaLo|*o%?BMa=sd22T#FquOg;czGB7a*w#D+7FMMW9}oBCSW zcWaNFYNs#52?l3xBhDUYhd}xuh@|Nm{1pl$NmqY(=FK2y^0*lbd} z!E9MXU|2|KsKJocgy#$7X@#s=yGq6@`Q8HvU1c|%eJ4!%W#h>0$kARP3++88iv<5n_Ph&Cd@ z4sUw!dCRXA@D2@pDoHC&H~N&f#6{|zKjZ50H5p`fqH9|1@0Zjp4B1 zb*J1s4NiJzT2FG%iGMTiJerqO)SBBbdGw#=c@78lf=M6y*5Kk#AA`r(t7liAf;-Z! zC$bX29CbeDNMZOtS7w9C^_&%-NGpJ!O0dqPbm?Lg#vs)7uoc!9@06^1iLmv2c(>y7 z8q*H`DI|mOUK@R$mZS8ju&F=W;k`cWfq}4hY@}X@jG`4Z{_C zJVy;EL0(3FdV#i>@1VAS%NApyJTg1iRo&Iu)Q8=&RPZTxOIzpQe6ONFJWVx6z`Fm* zzz6*ydGOA7Nbllb2SB0kL(EO_e(st%j#YNvKcwDAPww1s^5z&n^?LF3mGW$MWqMU= z6$ZW!dbkA?I+nTSWo2bx(vPl_Q4CV)h8g=iyvJ4YyZ!EBjdbZyV#GAgsCX1JUB1R)aA7Asi@3&VQ31{cYoUj z+Ie(-Ec+AVOO(wCZJ_mg4mU%@mud8m z!#??wm0@R`v{X_hs-aO$`Jzno0@)hscRud?m6#iJl;ic=Huz+d{clD#UunKpbh`wb z48;W{&G%qiG8^^!5SJhcMtOQ=S_QoisW^Tn=hjm5vAuZrz<0x=*jhW1G_;tl9|X?e zv&W{Y`0Om0valN8ykNaE2Oai?Aet!PUJMvLg#EB$7_K@QaTcK2L(bCh)Y$f_hWZM8DR)w#R=)Obt|2+TIE%I( z{NkwcpuA8E2P1F8@QegOaB|FO;iKBVF6X@mwvYvSsOX4K>uFEJwCK}U%i?R5&G(}ug)|3p}li6_D@A9DLZ(A14+A=f^+Hza8(H12V zvt5<9UO}dY8gH%}@$n2Oa4vPvqX)r;@z&Jcw&7%!oSimalp)9vWL5{(dKUzl z+MIUR4^^SQ<>Exw^mfuN`}S*)g}Pbiu}UA8FUS?-i0Zu?Tnc^k&Kz>w7cA$21>+HX z@G3)a(E(SAiK;L!D>oxgH=RE*Jn(VluMURX{i@5-j z=^i*M_W{?T;Jh>5 zr^V%qa>dz_hJYrILci0ekJueRg>Mm(qXV)JY=xDB5asv>a1W_sOGZg%@tHLK^Wou7 zQEtiMBvz0qQ6X+hh4cxTgh4_3DDyyDA1D7}hYHs`fk=Sp8F_<~w#wdZ-2a0^FA`xN zm@=i|)qaIRr%oTWI~ah&;|_`fnTgmNi8t?64@}n0M=O2Q`@P&z2<2NRF?OrpB*bf=lvW{$_i0@AfXspv! zCGPk^ecevg*{(bH83*jS zU%}W`UX`q-q*#FyB1~}sl5fvdo8a)IbKww2_8l={q?yWCY4C8g_Av5uEInSMkMPsK zooq)ZpAMsw8KV;uSJ26a7biX*zdY;Y688DuPWt@BsMv*ad21A2*O2so8N6ohZ!?~p zzXr@p!2wAZ>&EC2=_4&Yl6VRd<(^7s-5n<99&u!QUz;)&VRc@`{+s-XLe!e`D&{M> z$Flg&mItf}?fnKZHJII%*H}bD4`pSp<+Rtwzu3OoCI3H-o*X&##%x$JVJx@%G}30s z4d?Hf`A@@n{IXfwSN~-@Dh$Xcd-a?*w$G7I)|xqJ`|Cg1b*N=|Odskk@OS6+;I!6h z4n#j>$mxl0s2wJ?4>{yI!q5N1+EP?B%Fk#G>~yR0QS%@R!pPf6a<6dLRAVwzl+g_* zdt(n>KkqV=Itw{$`EX}TPea>STm7F3hZ7Y@Dff?F)wZGgi=T|AjAD;i#Z(#cVvpT; z%KI5rLD(o1dG~{jCoJ(58)IPIiT$d?89%6<*sXdJb!f=p8Dlui;q~5oEybENSvUR} z2smd=Hk*>A9$4+govbOVURWh+jp+2N4&3g)HfjSx2r_~&n|%RU7^aanTGLUUpUt3z zSy_Ti0S&TUPhlN4@DMGgNURLyl&kYuuTw!Dg=fpmk-=)tj4pE++gBl%6B`~C5*i8*tTZ+&p{JVfRCTmPw*qBf?#nhjr=F4ROwPIdgxokX z^*&!Lk>-eVP$L!bmD@T4ibC=p-fwT8XzTq`K_gLNP_<|A=ve3&?0*P{hvC?S&je3W zH?xer!te~D;FuQb3zPT>1`cZx>==&1Py4k_VMpo$hG@-*KnvSsW$sLATy1!HB$*RL z=Eah^7t-Ppd=djic`!T%DsYhI-I?o9dJYqR-%y3(JWScg+~yea z`wR*=ybBA@SgbZDD?Dcp^0>#@1G(LWG|QnZoV3072YX1(3!TLDvl%;;ch+!aN6a9T zG7qo_kGK$&7;-WCN^)$f&#m~V4#F!>2Zu|=^mO%$e*cIS5%)8InQzu!94GSJr6Vcp*GAn!gfZ2?7#Uye$jdR9Wf zgw9h4sq~zbj`9AXuE2hp#(WiS{>-CCR$ zQCLd+Zz52s{IrfPVu$2|-h;c$-(N?ks=oQgx|Euf%G9!mijabExgF|qJ9_>XJ80Cc zWNLArZljFd4g_wC49sya@XW!zLNe+$)HnED|HJ-(!Nsp0emkx|@Y@Kl@z|@G3Pv^$ z_UtE?$cV$S-E(-$NvpIdp>J~JaOdb}ouAXBa2&32x*W&P6!|s{@cnYhyie_l=_;hr zA*Vis!-!MOOccbWBovAZq&ZTI5Ilqg8$2?LU&X`bFO@PB{-|NJqyW+ztT+NjDK9WwI`)A*k~ ze*8@p@a4a?RFz)>(z12r7Yn_e>raiUf&qsY(*>5>7`#_R*@QUxy89K|mE(rrAyZ^P zmkK@WYwhadTW(Y9Sdx{A|76j!Mkt@v@qxYHF~4c3QM@HxsI7O0`C-d?)Ykik`E83D zZYYvb*N5`S8yv*R`}pD4PfzuJC!aj_C-yT2qwU~=FhkqpUfL0IhvgR`9|btx3%dZx z4xMdwb1nYOT20*@YcTxEiAay_uWM_m#cagRH#al)pj^*fe-Z5>GiIuT>b;76wM=)q zYlLV1bq(r8{qc8@slrw~TNK<$iSrSFnvXXjmjnAYlZASMBxz_`>{#7{mS)V1pP)jG zyse#`{ED5+JaaKlIwqTD2jvfY*M4Gf5|sxX_h@a^x6HtYeJ`@VeehN5Cp3{fg&Iz? z#RJ`3FMQbHx%)q{Mqv1!QF7u#qH7Tjam^hWT!b z1~uPv*s&2phN6e6`(E@-KRk^zoh$nnj}ON@0`o__iXD&*SD8>#hoe5Wt-k!S*80b# zgLLLyrp~PCsKlNLo%0CC}SZ92?PsMtQ%O1u4*P6gak78eatHYjqBk=VKL@gPf@g3|GfCI2# z7*)3ZdcANK3-BWSDZXBnZ(gr$vkxpa`*=FvN84b}Mgt+n!0EB~Ynqy>a5{ET5f%Y< zUM>N7u0=i)J7<1*c&&e7kg?@)53Mi^q46CUkoFdKRk@$i)7fH)7yGc(*pA|caun@? zQ&gy-x1}?Nzm63ixtOX8n#RVXVI?Hod%;{gd}DpwYUIv5il>1qIlF;akXuAS8k-x0 z#jGpXf~NpxZD9V4L?Ktw39#6XYy%}?A+dN#mcpqM5R9SQv}_NR`OA&(Z@k2XpZ^#Z zk15vwzC)=O1u1md!<_BpFxkG3n0sZ)lh#pS*93cBeGl4WNEN!W8?_VQ0WYC)zeY85@#jZV_(5c;BRS=>dinxi(4&lb#w7a$ z=Q#H_Bdd%puKRtLEciL^<2R%8I64IJbehD}Hw#Bkjs*8cSpUg1xI87=uv874>{BFC zY^fpUm4x}`v|Jx#5Q_@PX(@&G;$bEC{!#}B=fU`8a zcuKaFx?OXlx^g)tE?-Z*F4Kfx$lDyxYwnlaR?cTHO)X~6ywH8j>tf#pb!Ob?llN*( zA9eAayLWwf{65W--kDj6a@f~2Z){K0pk)g+S!K$T*ls4yB#X?&_a-|PN*nCe0T&AvT_%_*th-uUvyG)|y=Tr>H&Qr1zPJp0!O zOEqy9@F2DtID$w%LXP~+e5>=-7*gq&JU>)vIo!^7?cVe9#Dm}{{?HdyKW6_hji?F+ z#Zl}x|JPyn{A*bF0N(_hRXb#ou$y^_igjd46Q=faT&}uYa#<-~7D`w^SgwwB_B;Co zaoIjte!Kn=mMj*wgX#xF2j^KCTrAV1(o*bP^==|4N-~(g26mE-6T61G`G-Hlm9>_<^8*bV|2aEC;}<`} zrgGSi?O^}*HSD}8Wb-lU#m1NyysDYznI0s1b0aO$BIM9G1Lod_ zIrpyayK*4$C@ov2i9x0-Ikpi=B-4+GVx<8ok#O}HZWgLtuz#AEp%V+h*Cg$Y6A>L` zIbf@iEzNG6lI^2XU)=ua=EpEU4XCV7vvJ|JW8U_uBJ7=npeZ89@^?Gh?NGn4wF5g%!#rEwFZqoPibOZScS+kS*^-vacgce?0+va94V| z4n88t|3Hy8nGb{9qd`Q0V|~T?y!A?$T0*W;$!#dvw;b2ZIeapI*>GeMVy7SkZ72g5 z6pq*n+mRCTSl0mm=u@%_-X?E-fZ}&}!9?@=&^#q=mf<*%g{#rL3z`|R5}MaMXVYcVI?Vg}tGzJUA#Nf0rP7;K#qRaG0u&f$((2ztW5`zS-i0Jc4iC3d< z(2=Is<;*NmDgHOZd#-d}E4)>DQ*u|FU0j;+c_y46;9@s*a`T<$+pY95)Pzj3i?Iv0 zrOERXSgAZo6xXgN8+C;VIaua^Lmwlh%sUgnM@~*^lB`0SuLYoDm@dvP5N44@t>o?R zj{`+Rt0oAyVIX`$};_ zL2yP44GzC%qX0XD!YZ6+NQo6=7-kw^O_RqukWFc8Xy}mnHiEbU-_#gTdk9KjzC^lr z!~{)~`LJjcth7#(WEYDw$%1M^O&hN>qrWSwc2vdfvC21FSXFZ_xWJ{6xpS*t@>A6Hx zAcD)|Z(cJQQty% zrv&kZerYjX#jO=hHMB09J}yh;OpXja@BEU675E;g;}$y~HW;UibBfWLWi90Gf9Swl zH1v+kBB`8qNtft8&i+I6Hh)@)EyvDeP|=4~yIyLx_wx5YCF?#TtA-JA$%>hm{{OUW zb!i;nTi32{J%mCbOGzU0()S-`tuC#7eCxUm9fykPog{`i&K~8MldTAPQY5Ko5Ly+8 z>#gbuf=c*3aGGX$K z;mYOMl`&T>uAAIUi^%Y~9YH^BzmZYAsw_PvyKy;d%|Rv9HH_1?6Wg>55vUWA8UJ*2 z`lnWe8a=(tj2(PV=9Zs84q?x+gP@@M99w$X6eiX@$~p|!>q{M6Nyi9h9k!7p{19K> zSWuq;&MzOpS{gP}PswVSkB7%v({xFWJp6gLfpES!2}*d0dm@XY#P!0)OgZW+bqaT= z3HW(ZX=bB~DoG-HG?B6DBGL0C2%ROn#EFH$8L?Q0R2l@N0a;#6jt&wPP&k$@&6mF> zYd$dmmq55cgWDAG#*IRDWeI+Zp(dQ+yifGJi5)pa_TaaKq{res&btq5@?dot;kIvj zau#hX5M^!{KMOkpK#PVQQy6?FQS(Rc1O4#%;Fp8bql;O)DM+3?_mcMqXkY_$+|f{t zh|?37_-WP3$b#xg6gF=s>1J86So4no)IFH0{O>Bvx(S{Gfk9;`IZX1@8P;6~S7 zshRada3?EMBF$(?|3&V`YU*~;jiMWfPxu2yQ73P7lHEKi+x(rDhD`k=tA3137{bJR zayR_11zZlGu|l8zDbjl&@jbl%9#ZkmM`e+nLtj7qW(b%^ij}_~P_YZ+;x3*V{bAjB zdv6bI#BS;13>r&To>20=#lIkC8-itXH?T*Pw?iZPBqk3Hk35c zU_=$#S31_Z(Xw&OC7gyGoJhl9y)fI!1b$|zGP{5ldf4l=HcV8{WKQv?@I+ zIpAj0z$K(}zgd1mawl6G%R0lGji%d!wDv-iwI|*FskWAHQ)=cU5|1YT%8C z$CpR0m)xknk$pQ`TvnJnkONEU+o`kmI#XreD6@CaUTBuFsGBsbG|-zb&wD4 z>el!7$7#{B#mC?e%7&<~-9Um3g|XOx76q5a@R;Ft!r^Cl^Fj_1u*PFXT7t)nK?7la zI}V$$d^RaNNM%mm{pjYGG&|?1{N|!a-GEKX^EID9%@ZVgQ=CyjRHp|dF@px9y9-T* z8u$UdCq6!nq~{Ca5b`ij>adeU#EHFlDQacpwd2>dvRjwq zIpXz%cp*V^GP{~dFvEn0GfjTiLBc1^14Pl1s3AHn*Eb~w_BF$z(GP(`e@%U$2CELi z26wbtehqmypDcQCWQvA|Ph@8)Nv)tU1A(3Kj{*NzJf;Yzr&wUmt-vG)#=iEvOOJW!BU4<;MbeJcna1z~lqG7qi;+CqY*|8CBJPHAKjxJX<&hH~(NOwCW=&;ROK__`B<4q1hS~-= z(d2~=cDNpWZ~bY)bri-Gc&A3!iE1QONDj80vpycr_xXn~@F_whEDb7OiGUA_mcuSh z&L>@y!vinnjUiq8U+R8gL(jLFf~b+7hGPYZ$O8XpF_eo~jdY$T`(oxDrp&V5rp1ZI zTSiM$Z5!%ViolhLw~w?7w4=#GJrGkTcWCUx!m98b!muzCVL4en+C=6TwnYrN5J z73Z5=jM#D;L=9OD^yjY^FtskVHWjwA#o_#lhczFTPSC8u$m@IhaGna*a^K~BhRl1P zpQ;TzQz0=ELIul2L7Bm+;g8Gt9xrx-@_`TVxW75_+eoY+DHYV;*t5{hZ>npn=%A&u_J+^eJEjLY)bSk~_K_8GdjnH3F<0@d`cYFa+WW{_Zm;3mi@jJP49X1ocRLLH_%$wdw&;pMDF4Ag-*o3Qn$??iC6=-|V5}7$ z9vU1G8FM)$BHb19z4K>iM3cEKX6;|{q%+R!w@f%$Y_s~19v#tsv7ux+II&xv$hL5J zPM~rSOH@Qfrmn-P6DQ85W8?z)X3a`LOilnsr`2Eo05;j5=kEN0*g^gFR16C7Y>RtL z<^W^+nLD)M~Yzn6f7pd8t)C0 z_H^ZoxZ_8wqWTwD7%7G@^Ic#X%=0|FX*FEO!;s7MnK z1wjxgf`atk%K|^pWR0nd#XWEn_0bMxrM{S(u!ybf~wqWX(}-31#>Bx-BH*c%pAIo{JFI33Ff7 zzT(}nMe1Pf9GN;p^5>957WX4L8v600cn}I~66sg;eEk7VdB7nTwj$}v!7+p#wok8N z>vVe!M)65>rY38AsCe0a4c5t_vZB%!;S7n2*49mh!S zO`8t9UBW4IHMF3nh%Wc4^sNkxPD;s6h$kQBk$L_5A2LukfcsQxp%@t{7;;16$PQc_ zwHPu#q`${xw@qRof`_GspQ?#!*MjV?M%OIuW)M6Zr z57>N4TiiI~g)uV9P`Og^4R>m*)}1QOj&pirA*@zUY!LRc8H%=@k@l3+5QUJbEzSh^#0+mD9m6oSV9<{pBp zFV$UdERHCM$c@B=3fas4NRQpSA2l9x+brB75>tw5qi}EFbuXw4&jKXEE**o4-Z`8T ztYphH8n&ky^yLU$n@+#5>}SYZB!Wtg&yR-d#eOyNxW>5C^5YMEjN{7pE1ZG32+29x&9TH|Dzi zrkD=9Z>BGOl~w{DM=hJ_If!A_TnX_!V?#c<>9q7jaAf7OLCAr6y2!2Av)GUKDiY(! zhc#s0%R@iUOoTI)T#8L2C5#>s%bP5dIf^m`g0~?Z$7_n? zHSJ)D>9=4?p+XY6@dg`+0R>y;ehUWMXg7=}(FBVb6I5|NRX4sfAu!D)%C{_~B()H? zY?$Z<0}Fapq0QeFQ(*ng!lmVvx9%n`yPkNh;6^fY)$801CtI=#d2CGs6B?G)KsGe+ z7I>ir;a2U(7mOrUr5;{;kUhpWIq zPa>Y+OyD%RLkC^E78rCk^p>$=q<6c_Nc9YNVsv;iawm?`Z$ruNY*KVi6zDbjs+>4R zG|XZHlAR;`_=Q)DsnmILfU9IBeS;=SmeMN}UMBrLb3E6iOQ>o~=Gm_CK%O1i(#scD zOmtv^(_Q@BH`CYH02>c$C*dTOI^qGDIrtlTNiU z1|o(q2bEfqgj2acMi_9;)57T-gSszp0~xwB)pqC*+U#9}L1A2eLT)1C-6Bz;Cy_Uw zTmK0Lv^cOg{Xh4AH*mhxrU;hf(DWua5n?Mu+z&Xmic_s7eK@u&)JWw_{jC_`N`B|u zENt;AU-9mg;IE>?8tZh4;9LSf-QE>(rHMs}j3jw_|DfcTH(W3HNl>ff7twcID=l~_ z%BA!D3jzy+qi~aqkK^rVSHoV6$RUX_m0p}ul7InLiKLk2M61xU`TNFddO)G)$uNk} z_t-WDQRATYU``9C%c@N)Nu+4Mt5-ck6}>}MLJ!`K@rrT4?8>X&u&9z{#W67`%fw*` zWSEw?Vl;96;#IpVsQZpp*yohl)-9)rwR22Bf)fHgPGtUC*YyAdX0S&43yZc^;kGKW zqn|9Qj!YGD><}h%qQ#EJ>;|woZgHQ zATU6XUUUuI?iKG>9AVg1!e`V?D@*hIv3c?7z975caRHV^523p(5V-0vr zcAz{7j1H2~Z$YDRzvyTHzLVGgkwr=2))lL^aOp7L25Oi>C!RtKy+B2#93Vy##rs8q$Ii=?4@mS;}h#KeSh!! zz8kjT_WCZj{w2GU9Hu+!>sm^2TiigEoNO@Yz?8W5b7}?LVMP~wFLRE2lY*4SZms{k39Jv^xEidlOd~NS1dU3!R7o?B7==f4CIYq zaGRKq`M?6kP}0fnBZXQ}Najcc$EJVimwbZBv&KHs_!yt1RXxwU9CN&r#`+%iK0q$@ zWA}AO;DA_Atjrz5TH_6u>n>N3ZL32Es;Mh3_;}#v6`&24xCI2b@H<(C7I;!R%6m0e zo)OQ?W+&?r5 zaw&pgb1v&4`^F5G9vwW>+A36OAo7E&kjDZQ{iLT&*-)43&U`ZcOC3zaU2(na8iKoU zoHmH4p!Wv=PG}6JzHh4=K7#eupUs1B=7$-xSYOX-E zuo>ZqNO*}ZHS`$&; z_O2J*aOK^gvU4-@((;%a$W3RoWh(@fPeUxtcFSY0CFXqGP0ZrHk+A}kNUfrcBxG`}U4hx#N z5`^@&8xAWMm^PINZEIA<^qTOQzRucd8fu<0jSEiqiVA6KXrC&bWKe|m2YHjPWw=$e zh0=gK5aU#`y#Eju4+V|fT6rA>nJI}SD`?WD zgF*vt1Sk>_lymK*96^?Xz_8e`xEQ%UngjtVve*7|3cIP?+^oC|9;p0ZHaJf~;y-M_ z>4lGv!Tv2@oT}^;?r0+iz9RFxajTr(I5XgdwB^CIJIPJ`L_gf+{h(^#HOwx2%@53o zA6Wa$z_f5+f&9Q6@KK5riG3o1C$b-u_cgw>fjLIq7jv3~C-YX;?8xKWo}v@HkTd5x z4nGNpf2&j)(b)F?$JqCiVT2Q+j^s&|ReCEfqAj`uZ~9>!VRiy+Nr4S%vNyWPUU%tV zr-Ni(!ROuN^GCGcZ>TS0f=%nIXe>zDFDJ5E@j)rr9|0SuFno>$Ee02AFgX)^(AS#@ zzI5q^-%>xTn|ICm4ks+LRNtt&nRYAtmN+~{#$W&NAvrxa;$fLy_ zC=X5kDHV%!RDW8Fq~I{w*biI&Wx&%hSUVv|XyIahVqviX9NzzpNmZ|KTP;zYBnumu zwQz*JW_QKmk`tn-)Bz}?jjOeZr(K*swkY~QYz54D)&eZUoftyARCqS}!1;_L1*;yF z!{T`P-CN(A5lD7kK#ant>QR?s10t~_w7*5N0#|zkf*}*7*Q0}KLeKdA3*tM6i957neI zdh$Lxc9&jJ%dx%`c+tnk4MXxt4Xi#0cB`puJVq9p#m{unS0t2(i;@^Y>^ab(ogFXQ z+Bv($y2ZLj8JY`w?Z?fVE*!Mpxy6*=bU9tlY__P<6Jp1Vce4x~Gx~EZitciRuzOHQu!?`*nLxHG2rS|azWB!Qc z_ejNZ#{R*D)9GTt0H?`Ku*mM~6oY5oZR$xwe@A044~%I=pW=xr<|-(x$CtV1Fr<|b zR(vn(4(tryerCn_<*o^SiEfyluK=FmpC znoh@%Iw-e}E`*yA3FGk5ILJ&1qFOk^k%4%yMmmj2kYBfYdUbWh!vUz3pT-g6ZDyR z=YBS-&W!UJ@5oV!C6!U0T#g#Af;&c@ItNm0>Rmp-=iGfJ0fq0dn z`fuXDVPKSmPC+QP$@98upQ8%1GL0WX5Key_jolLq#x%@x~A$Si;< z6G9nnz=DbUMINJ%A!&3K%>J?!7BlxSB;Aa4A-I58i{yON`#F(97Imnjjv6<6P;!Wh z$h((+w}?5+U1irxHfckrF8!(d3gUcJF+M2O6_N4~{3r+MI_4lGYZJL6fARwvxC^zt zlFffZ8&rnTh6GgTl8nHn@j3E>qo{C%Oo9IxnoteiQl~d!Vb}FyEDjBOaUxJ^`ioXr zghLxgZBn^|V1+}&XgQJSCKLAE(CFcR4eb+7#3tgZj&o!r%9_x5iu%lb6|qrQObASM ziNxHd@53xa;Yi7~;{d~E26zi<2P6yVpgiXqp0WUkIhhPEQ9VLx9M-=d<|T<3PZvEn z`uPi^vVP&VdZPA(ENW#whlA|Zi&yPK@N#l$oD|M14&U82hM!sVSu0`A^O@5{cNo!X z;W;>c1t@FH1+WP&Vmn?SX0Imn+EY7_l!%z`S42o4W}dNo@?|=b3eS=&fb|3gi3wB= zNLU(WV=6s(2TXtHMK7j}XTgtSpgA@NJf`Qe7KbzrY8WN*i$OHlHuh!R7Yw)ODm!p| za#w+Ve}T}ZUHORyCa@wUu~-~D0f#>O(x|gVxVf1eeo8)WWmaPn?3&Xxm&>kn(bvF^{Q4@o|!?q%G~yn`j1|1zbTycmNQph?21;6JhzF5I*X zi}a0jx$Vt#m4-!SEX#<#msli+DZ-rpu;<+H)BxmSXr4bYXL34}EitZ8k2CKr68cqI z44kiJ;JHMDz2S%X7;(cE&}fBIEuAV z-o1CnnK(>%NBcKq)s>WElT#jLbI3qX-Q2DW=}!vvY=xCJ4JLW#m92MpY@FfnM}H!$ z8ccqC3|GxkF>ez?C8Wfr#vlgw$e&WGA}M+4Qknjt*8bMM?!Te2cQHfua{6;N^J1=T zfjy!j zeRnf5_o^@gn?R!AS{(Q$$fkIRJ>z@_OND1!w?048rDZ3~b18Jlx0fh*3e&4Hnvrgd ztL~R@L)^|*$D`3#l_&o%CS zq}8TvCrtB9cTabdsCfw+iW;hFY8fu0U-HWkx4l)ey?^HKK5i3cagyjxv)&8sE?7xH zHoetlgqhlpS(Kzfi3Q(;jP2C>lGI zM6N{x0f*ej!;-BSW1}W+Op|MB}&sA-Uns<}8M(<8?JVSCJ@ux{pCa%h~3KC(f9fAd@lKMC{r zlVS*%f3u|(l*ybSPn7f-ADIApQt*>A7mf0@Aa@#+c0bocgv6&E;f+qd!Op1!TZR}-(qPoW;=f=-Nj8EFs{>bQm4n-KSDl*? zQS>iOEKV*;V@T;}0~npBcjtM&QA~rwH#3&LNX-MOh-Kb)kWML@FDLt%Tp;VPBB?71 zFI`p&S#X0c@hI~t4Tu(}WF^F}%URjQmu|v)BuP|4W_)&R4)jC|u}9TS?AcW%H0UI= zpKfnCbzWHLRqR^mLVmd<%r7sj&8a+)CKSkv+)b@?Y>>|Hy6~O`R7D0j`Z@c!$^x@Z z-ejeWde@il?R+xGD{X<{7#Pf$h|R}ffF>dc#efnFK1WHF95yXH?+zTg7vO~7fvKWu zw3_-4tHq@=R+o)6fPp2w>p!iY_3u{u{?qD+(q-lU-&PCeNMREnZ+KTU?kriZ$)25+ zLcI<$=h>%ir>up1Ut@ubWOcA`uxO}ypy4n|f=Npq)Lf}+@9*I69_)F8K?@?hqHS(r zL?{$t=fzgudhGgt_NUZAk<{s~KWEvBq`DTC{{E?;X8Odm4LhduXnUb99tzeyd!MWH zFyz4*HqSEOAliB$Ll{_O+iZuMVn#1(wOM`JkrOBegdA@@Z9R{xzXanb@JxlRMaPM@bP-BkhQ$>#->8SgF(B8XWA^a ztc1k=`dtiHqp$|#7~+h2eam~nBSGO$siz{T&RKQ8uan<|l;&qhiTv65GG|@LIeGPQ z869T_`Q3+PZX+aPAuPi_CpSJXfzHyASRz|{_)7-f${!z<`S0&i;J-^U`>hmqkDr6X z%3tf_uCS$@92q4F<*3X675qg^T!;mK5p8Ej{vzCQ{;T#5f^3g<@53(Z?g!l$`e0T6 zLhMfirP1+GFdY} zzoCCq=T?+=^xqVoG=)XyF3XLMOhIlx6FMm*`Et_bnB){b9Vwkp=s_g}IpXPMg>fi< zDk;TbyyiU<8^1b+RC7m;Tf^t&E)7_^UeErj33#P=J=1(hk>ASS*AohcIWIF0{{Egp4Ur0y9c zb&#Y9$nPTgC@7krA{ixr#Z*Wc`CyC_t0<9VT}VDkuIs_|-T1NRFUTHwV@nxG@A~bB zif<=TV_J9!EF2b{>qs^EKuQ~Q?*`v>ndecd?T+}SeP2LfDCN|O&prbZu(emx-w zdd&@?yHR|$4mGnF$){9%S#4Em4U-B7*gzX^8y6ew_BaM5)KQhS$1r4%4?^VV> z8N#tsxcp8ulM|hw)IyZ%X<|!?Su}dQs(?oy@__OkxguGSaP2YR3bwrpd^G!FG(tkFH?#U%Afv2rngklaaE;=WFIos2y}GP(T?%T2#Jz0tHgDx zLX{h~n)FyXA&K(gg>B*Ix{=4uUDt%P4DrZm;Ymy@EFVF3jXJmG!A5b z5&I1LH<0mX#&91rJToNg(#*0%;8!+bI+fAzvVO$yCFfwgl6w&2m5ZJiozd{W9)n`) zklM=QM9v~UQ0yG(n;DrIm5$Hq=KnIB5hT2B8O(YfhZj?$1ctB4<{9fW>5+=|x>~eO zk*Y9kH|a;~IBmo}?0reYUb30RhPQ+Cozk$90y(Y&HnVu3Z6Qa#qwxlAWj`V6^qrEh z;sQL(KI(33X|Jv<>uu?4?(W1*XB5`-yy}A8SSe4R?qH2}ZauigBt9s^G0Ojk_P>AK z@gswZ!WUTb6DrPM^Sa`5#g~!%h=l6NAYm0W8%6 zE1!sDJhu~wb`)7$qK-vc=*G6yyNP#`?j$kL4nL8QKA6=k*=hK_76n`TSgp-{v?t1j zo`B1A%kCdkemEB+PRmM+U0u9#-1IA^QR2YrNmANrz!UltN@Qhgz0EByB*iyc1X6j0 zO;N4P4_4IaMA3+1>~}ZgmnMWsn@dt?kRaF-cAwzObl0#<=jWr0EcK38XUcR7d-|KZ ztINxVn+I_R-ZTn(dw%VP-3To_F#U!6ftxattvVP98tz+eLA+7fJHhdeFT`boOh-G+ zxla~=;wiY`gSrpAp?XndX^HB$47a#PaztL^q6|F6{-5KLmTnsK=E6=&bnc?n8W(=Z zQK;~dU($-De~>ce$2_VS``4RsOXDmhEfgmYYjMPy)b$%zZU$ggF<`0F)!WHfRpf`; zUS0h^hYL;ITGou)RE?7JWE`8{P2s4@*Js=7gd6IJ$`DzCDe(oj6LaC0SFSV1yJ zB9V~^-x@&ooqc5cl^6fq;n_4dX-$O|_L)X>BR)P#9FcSn7uIXU0X?E}nxr|l>+J(w z>^%24ElncHV?=9Q_73(A^ug2fICA0%4syo~Hdqi@_qZTL(fEK=>ht`8%4>vnMky$F zBn9Dfd&de32JXOC#+b@VPD5f{26QMKN`gm{SBf|GmUKUDdC>K=?~5+1Zv3TpAq0qH z#>OzC81;Hm@cZj&P3{5fBTgX9nX%SyGLGQxZ7LOR8fH$dVOkgD=at!tZfv3 z-Xee`ok%qv?MFyMFK(j9nwSxtekfKL&55 zw7N_Nzu6d+cafj9;4MvLG>F_OB6}gmAG@hB`uhEKf672wUlv0e$MU$f_LK0(b~-i5 zMSM2nT(T?-_o4n>T}f31o|=fJv1aoENs#leGB1(5jZ;b^%&Sq2qroB z6Rxq?zH=05df{k0(qa4^Nl!=*Tz@;FaQx&ZGqTwk)56Z-K26a=iEyM77sz7l`57r_ zs3@*Ou|i6a&y|`sobSY3+9`zjuJa@F$f8|}alu*6QT~rwD#ucBD;AMCRN%F%{)&O2 zXKotYiu4|n8ACPloMkzGyprM8(RGN>#CEE9S+W565Gs%KVB)VIVTW~sI57^v&68}4}4i|s{+(b^?r zu%;hW`+-SJoD?9c>x=1~tRTU>{@8%ds4*jnqaHT(bl3JVlG(WDK{liE?oC->z2+y17a_36- z3SVp*$xMvsaZCxDafYU@yA_S_e5Wq(BmZ_^mY> zAfb#mt<@sF_VU#CGLTpSv!6C&dG;1HilqtRVX$+;;D_gKFc1?#t0rHXkQ!XeMWAqJ z(NpP-l1%oR$2E^D$gA)pxyTuE6%s>ohq{}52RjZKb$E+JXhhEt>*{8PTE-__VuQtA zkpW$KL!GsVQuw(0H@efh$F$I{q*QpJK&?&}>B;stV_VHCYQFsDgEC|Rrzn5&h|D$5 zYo1rJ>1$H5SOXgi&um<)L;O*ZU5to(NG^w2IOrvlWG%=mVYoIUyPZh1ldxi;`Wpj( z3}P_=PrH=cDa=v%L#t%?uii>f0)CkRsP1}kvawCLxs#|pCh`r;{^RqcUB1bl12YM6o{M^5*ko1P#yoOB-tPTP%|xMTXaPj7Vg(5Tb8MIXPRq zn7f}#waIYHVOo&9|GMW@uPffD#BmB6B^*v0Tkdv_4vF=R2xP28lS1>Zc1V@$;BgtwQy8=lcb%!?se7YD%K-^7fIx_xLK6(jy0!LM&PwDO->7nyerj-tbHA16;t<6VD5GB|6nL z^IxdRdn4hkQ8N=!*m)@e);0dMy5-*{BWXhLw1rFk0l}mOaA@CGCZ%1%ZFS`E6j{)S zJJ(`(EVG4(o{>h;dl+yA$zN|cd_vZNg&P#A%HSe(RpT!WJ49sl=S^$rc7wTaQ{dA$W8zn|&@Pnov}1 z-{yeXIq6$$hFQjm@C)6^!b>Ht?ap{it6*}W=0c-kigkOD(4+Wt(^(W4`V)Rhe$`M&gEr*sHdDC3DF!tP!97Y?UE#!xI;2MUfH)v!dgz zFn@%s0$D9^cE^+`Uxhu5E3aDXml^oo0V*PQralbC7=t((8>(BHqjs#DME(RPPZa2& z>o)>I6$5UV-88xt=^NvE+lLvbx*3(TEITS9p+pXrfqW;CZ?d5aOG|36M+Fqkc}UIdXi_F7|5yZS*uLs zutBBO&F-ax17Y_vmR*m(ntLOOxng_d##vpGe)GiA z18(VE%aE(NJAy9uD)B819+0@=ksY5Gjm2M`!O9x*nyH$~>XGWQ zKI~mtInylJ(4IV$+tJ#ZJC@cin70tQZ7|X2p=zn1ju}5>Q1}yh+6B6sB?5AtMF`60 zX!LxR>;qq9IK@x{jiv-;oyy1_a|-6MWDf+w9-3Uic9TkjR%1+&N(`vJs^4d@Nq$W0P9GOAKoW=f2=LnyIn7p_g?7xhe)*M7V5j_s&<7NeC(^YOY-ZcU8 zAFh!hGKjh9SHkwovH^)Hl~O1!!uHGgdwXGNcT;0`b!&A?y;m~KL9{GVd>RbDnh?Bw z&Mlq(g7vZUuy;jgH(Cb?$x$v)DV~w6*A9M(Cj47K;sz(E$ne zcLHAKeO-0G_4vBgdzTx;1SLC01lDHM=2c-YZ0t}e{CrIE>^S#qD?Yf$IAVMF6j7`3BlBn2bhGa58--P{ zSr==wJ5NfM=TO(sE$z9338Q#wn*&4iJ0XJX zr0$|t`^ppKGZN7S`mc%p2=^z|;m@hd$gWK!b4m^%sv&+r;z5adhWHGOL<+R7beqsf@tXK(VNzL#SR(W;HBF=tTH{M4dwMnT)s$WOep)aCdM(OW*R%+Xqe7 zog;tYbJe(Q7DVv2@|F&?j&?vk8u@JGZ(S!kpLCt<{Aon+dkc32ir7=|Lo5B(@e8)z zvE4MwuBJ)olz+1FOcrA&QD@tYx?haLhiyN~o~_kczLzY}KO*~x%Vw)wnk>`pt6PNb zStiBi=`g3{MeR@<^F1|q>ZQr|n2uY8kE-%2z@9z}*onslUX8a4Q}#a*wGr-Ds^2e4 zoRk?CCrds+@KM!P?0)VYkSOtnVK_cxn46;`q6BD7?1xo4yT*zvM8m`wvKFFYc~K~l zAZFhpoypN3-_7B6%=`-^Ehv~~p-a}Shb?UyL?c&&oF#q!(d$kZnY%iJsM zvt4xkwM5#NE<%etnU=UAljNI@4z zTi5fR%-cDDeZ-c?vL%oe9U>mu!+x}>=zykyqWE&Qf2@cZ=}P#nO-}rRSdDTIjuMq0 z6tF3SXVcrzU(FkI&E|^T3{L78#xLky7!db=81&w|_-K@@??R>@ z{n`kG=H4yjtr<<}$!N+#Fc;Pm!ztA&{bIs-S7E+ikw>PdN3i=Pw@c3HR++Ym zmL7<0%y-Z5)DJuveDVr!xP^;yQA*F+sEcSj~C=b>2a&Z^ZTeAk{B8a`D># z)O1KLQ;BK3fCh72B4AIGBTbmN#?Af+C6HTJO2@`XWAc=ooNGvqG$Y@SKJ3AgjW)T~ zOT+MD2-PN`)De(k$+Zr+Mvb3jJ0SEV{r1B*EU6yN<~m)cpn`rs6IBh(HTB4xf{1Jx zoQd_L5o54x++-9yCi$6gpI*+!c*eR!x-+^8;*^BcxYSdL!q}MDnCKV=XQSM#?OYF@ zPwG@~rgxDaCexO~L4HPlDt=Zs(4xC@^MTE$`5B21PWOxq%16Ms315JA@y;w8&X%-sbjZS;J?jDjAS9!7Rkc{Pft;hgBco0DCL{?sN{aZZL7Lzp%Rdp9RnI}j=|Gbdp0Kf+``~WHgiG0OF@em}ZxccG2f3%*0y_TF^!)|LP8g|K9d?Kd%y5Yc9LVb{ zZL4LDH1%6dEbY(AM<(%QK6l-ay`I^9EyTfT>1pG_9?NBIZFT(x6Pk%aS3R54=B94V z-n~KCpWf7VKp{2BJKi?QF-{g#S|!W7;Cv(1Vc+lC?H9=}!Pqq^WLC8bdIV9O*lU`Q z9G4uEqKXwOS07go1g5v2fGd>X7m2#TrzT|1MeI;<5BIyDIhfypuG5Fa>=YMB-&2}i~v3S>9cp-kN zc_-t9K32{)wup^h4r~)TGu!(A)(BEftzAELEo^Dc8@9?JHi|f4Q^cm#ydH%O{pl`THOI94?KBUfRu2v(rEwFQmZv<*; z`uY39Zy0WOKi}4wE^V!VoMlgHAJn>~w^TROSk#);mozwMEd+xGkZm-EK(cu%w;2vK z2;D0`gmmZ;|4RZJZzVPgx2;VOUds1r@GWIHQ+qbf zEb2tq1^(kD!5#k93|X}JvWdvZ+bZ@-NpVivj6538{y&B2KZdWn-J&;!hbMIH(e~6R6^c0-+j!ec@1ms(dA@Viq zzoLb`sB=VZ=Nj2Kw^+w0bd#4DLv6zf>Qa_ZnRhlL*(F)U#)oI2NL~6p|GWOTgKCn5 z!9~s;t`%nf!YIE;pGY6_vA<9-OShw|^M2z)!*rp)vyYRH%Ykdc!kVIvoKC%Dp{x3t zqXE7FK0!f&H^KuVoo@$5V>0Q%T_@pNb^6I#2E-gqy;XCnIHb|B@t^Db@IGiOWciOQAa?vED_#dFB@Gl z@HrQICCwjKL{)qrU)`FQz$RFV4I?hri*ZGGH~E&~im+qx)3_p315aY@|4m59?7 zT;km2#9tPHCHzXn%XW2l-EV!Unm77kkOYA<%-S6r#xV zS!Ej(CYdclAz(L@ zqKiq3O+(kJc%TS$YzN`laXKd@Fxer?HBnX|JxfXno%U}jFK3i(bY{ zYabOSPK}E;N-?Ok#xf<E>=4R+$ml5%IYCZO_3UX#COM&KLqDg^ZE;fOvU_)W*{aH=F%gx~q zELU(oVz>L^E`}5y#9V?gkuf6Fxjj=(!nB4dr-!x=Y-BN2sc1@0xx{;m?ZZ95HXk;& z!K`oLqvJ43b4;&&RCuIyc{ll>n)tZcMuzF z<@(EvyCTD1ru%nh&h$aN%mu<4u2R~2sPN!_UFH>D=Ie7C50QmNM9EIN>E$k`gQ*Um)BNOm0MmQjvG?G#yeh{&h`V+G5c;3(pg;4?N*+fUW! z2tyc^zu_$Z1*w{xc6D0`V=F3Ls~jsFWl!x^JF(X~(Lc#4%KNLD@t%&as%%OwW;j+2 z3Y~MT3QY?;U~Vz2i-%KZBRv)ND%8U%X?2pwLpS`8Oq5Wj+f4I~bI)d`J<52I_5yEO z?`Z4D!o1)2z70ot2W~EEi};fzPGpwF4$FM=Jd2#vk+i4D&l$22pS=#NK9jaHdY>NA z+HrxG`Hu2_Su@q&^=+YTu3fTIYnRY1*QWSfaU0Anf>rTw{7i%};D)qdgsi3FN8?5# z`}f}!UOE%qfC

}N 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%0Dr5c^z5R^Z+;8MVU=d z#|EKZIIrh!7a7sKq4z9al{;s9CBG(nHIRyX>CCQw~$aY0`10twZpeXUe@oXe>6sSEQbGfPXq5(=D zlGn+$N8_!djW2_tt3@YwJ>id`LOxkI8UGp_mz!`cw)E&3>03FC{}trl@6`>w#LFAW zT$s4Y@Fv13>u*2yZfg8pZg7)0KM1Tfyeau`lD`GBNXX&f{TD#KjNHI?HdAlWu}oQ! zUlzLpCgKqL5St73FrUT?{Fa3xKRO#;6*_a1V2Fnc=8N)%zKpjlIV%dB8es10r(6~9 z5niTel;m{)^xex(T?sf092u=_Z+{tr3+IQxsjWEJ7M!?Y`5~k_9a$?c%X?uNz+ZYU}(X zY{N}1f@5e)a@2>t6+f2Ol+|6WEv+dltZk^+ML6NLe$x@yme5X;I*&I>KHTO@c$l!0 z3~7>R?+}u-6<bGIZp*3g>Hd*=BCZ-ZDsp!lu38xPOe;)1L7yYol|T_ z9<7ntc$3B05y!fO`aDkmqr9zrQ=yb!El1Pxdl9lquAoahYM~1q??%=9S&8bnJGw|U zUzO!ehK@`jES4-T;lIMnK&hMdDa;I(&;MZp?cmfPr(HRR2NpkL4*B)ct+}(x7k`?=M$BR5zvu9K zV^;+m)*lV406>S4x^%a4=p$NZW9XJ`BvMWzpEJ~WUhioZjpWP5n|0ldUA1>1L|Uc1 zrw#5d?09^07beiA2U_1t-j;t1@8E*9)MgX0(Wc9`l4aZ4QEZGgAVxsEH0qOK)Tl>i zji?>LZR7{-IU1lqb&(&|G72vO2~##hCxL`H6(r1Wha7q|(Hz>cGYx?RR;o6L*>X5FIPBOTY ze9^wSOXB_~uZu+CZNz9bUAmbp)uuW$tFwSKf<2f)jZgv@E+reucRlhoxPNVwuQO2G z)euSC7Oi4BalQIt)l?V|hxC#!aC@kva_osf<<};S*cZ_Y{nxn|$G8KL_AD(C;FJMX zbW#A_H;s-P8GRJ!S($+Nj3BO`CYIAmy%?`d$71JfR`J6TL4gA);J>ZI|HLL<7eet0 zdvfn6DJ%^=l`kr58mnr4sjkKgvQ&6MegixvsQGGdP4m(2m@dgzQ+RJ4H`0>n&|R-5 zlfd&w1nfSBULub8XtHBzib20fj#Zbud+-NCpDG-|$m-#T@fH+7e1w4co1Q+qXJjx^ zfFW>bFAWm%x;n)|#`kuRiRp>vYoJdUG1MOlkySHrDW>`Tj$lr4n+5#Gi}sMVi?w89 z{pHS!fZFKrrhGj|+Go;sk?x06Vt)G=4apxxv`?{~BO9mEjUvMjZ^ckPm@r?Xzw>fZrmvL7YR zwY5!TTI1fUHV{|=dC=bz)c9koBu$Ig?q9(XvB=-V%hcV<9-hJjiJ{TKF~QL+4^tK= zDFpf-j2Qgr*)X#R_;VL(gr#Vis1(MxoI|;cBgSUT6P!XJLod`)*VPb9oVCbjz#L$Bc+0=xtFY^y zyo^qsHzmw7-VAR1E19Jk=#F*t^luehNo@(OxYQ_DqNa&VZxq%6m86fLKeS3pc?sXZ zC3~gA(P^UppF_`6w>Tx%50*O4-$7LtDrXwzS5a9|p9z?UFAeIhSRxGm%v#w{@YE_|iPp6TUi0MNV&@eSWW3PR# z7EVbvnRdyc(II$Cut&%^?!PQVyzr7IW;x{8B|7fixXonKKQFnkXn3K>*ooYx48~`T z>q4)sEP7|ci;YMGszRncX`G8C8LL5UhKv^FlWD3QI6Em{?|fR?LrO_yJFR3StCh=e z{GVk|H{iuXi5=d~s~Vcf%*F$#dO}$KH@37bl01H8|2iN)M83N`OLR4^aFrs0Q{5^@=l*jn2cAx)gDqppbuYPHoPG({vZ}caMsNIlJqVTft+RzHN zOQZlPR2Xc4A`>s~DQ4|lDq1x5(_7f{x9eokqnj^Sdaj3@VLE31w&0mb7{ZjJBPJD% zeV~uPgV1&~uOzGAsK;Jn$tY~?<>6ai1s0EUtc*+146 zy>6QH$0jn-k%Wt>M%4H+Ls+EZo1xK{lw&WPb}j?OWoeXagln)Tj=qO#*WWX!+i_5u z>yyJT6-!V7gu`IGoS1!7SqSz0AXS+PfQ`7Yy@O=+M*kKr(L8C} zWgD!>k!+`GXIOz#@vwi9x*^<=@w%|7Qz^n!_VA{m8ntE6dMvPIJNVA~d94IL_+ z3RwQT49;uc0Ubw+S`TS*epY>I6+7@9(mB?M%M`b`_~7*OWKHym!^a$sI zusfjnPKa;|bHD7RR-fx|V^g1DmD=`032q5a@hGepIRAX@Y29PF+OAStdu#jscxu$~ z@{IB;*NfCP=WI7zvD3v*&DGcQh}S74gV#6WLgIPy`2|x)8xM~_3%5i5jwdgBh8#GH z1_hBe5|0XUv|d;)b+z4MdY62wJ_)ypW2xMUL&xopLY0%U3EOwe)vG}DJ{0a7?Rd$D ztSNlZR)lSRPa~}oT}nBhhG8q_@wHfvp-ZaCoBFQ$-y14%vd01Q>khJ^iR|gx-UK~8 z4=_AuwON0=o@H2If_rd4OePJ; zSwhtrawdth-l(^2y;V4V`*J{SPW_Ey^z)XIEU`tqw&il=#edN znS*H(c7eTti{b&rs%?Y#pcADk5J1g?`9n=e`|wZ1{t7 z_dABi^Wi|>hog?U@)Fq{O@2rLqsi;irHkkXF~9TQ0PW>D$K=)e@<1ZwYO z{2KLZ_>owz;3HKz^)-3u_5H=Ctq`xsaj4lu*8aTRQFhnTrQVs=Wd|Vdw{hlj_7@mMsqZKe2cEVK+J6RbSQo?tRAmQ+Bxojt zTm(0t*QZXKz?odnthQWZz0-vyNf)@x?PTF=RmrSTbb_G1mC0;QpZ$Lrd++cju5NGC zBv=}hgcN1S7-TvG2+edb#h79n+yQsv-g~dIWiX+c;x1XXB+Itkd+)t>n;sy97t#m` zC1H=>Ey%aVyBhB7xw)U*O)~`4-$v`mJ+Vnt+gL!un^KKn{$p{_8 zkTu%L4mllmu>g)u8a+vJuW8Ymc#aZi2i=WfP}5`M#&uoK$?+t z&z=71o{{$juuEgFM}eLEA-WC4E>pDW5I${$PVdLtOc9rDwfDe5Z5MQbGg>=7-FlC} zVPmNHH7*_ndS1uwn+I;#K~ZmCh2~9jDEjiy!aM!@5x7*0py)mvJ;55459hZO7}itf zZtEPk`=-RCg9e|=QjL}goyv7u*(VS`HY{wf+!86os%Z{D99iV#} zlp1xj{>Gio9|#;Vf@)sjnlTj19@u$*@3d{2I6EgJeNWxyhlWu6T#Z&d>fM3V@u6EN zt`A4BimOVS0Q+?fYK(Z!cW4d^7vsg`tL-aUXyx4RUTLydf~MB3S6b}(E<ZV)d-$=qGe6@)bFqr|ambA)EjPguU4D&)NUJM1CXKlbu;8DHE4~UmVUN z@xGTJ1cGnRU5fX;VdMCj6Z<*N!*6ZTRp<^R>5AQT^X~Jes^7MZOY-=B4_w;m*wDorO_e6pqH;#bmK4o+5`Nyqb#GiLsINed%D?yz%i`Gy2^(98jyQaZXmgvL1lF$viC)Af9F{15cI#dfOgEtvGI6Viz9C1+!%Yhd}cYR z@GP>;@au)HOo3N@KoP-JTZ(rAkPZEzivfD_vFtjUM-HLi0Xrwt!hhi?5@(Ho?u2nO z0&_4VW-Cd&m3)CFSF^ckGNloH4Gdz~_zOPPU4^-ev*HQ@h0&vri-E`np`!Po8gMa0 zY2=c(+*hu2S5I>8-2AC&9O|DW)@_Kv1$!obbtTdtUOA&B$c=1 z(yVh*!Fxu=W;kzxnF?%QHa+OG9=@m@3QBM{Arswl6Ro@vmJ~mJ4y9F2+Dvi3Phohi zvu9Tx_%A^npb(cK#g#l@^kRmf}TQ@mFTTD5-l=bg!bnw5y6Zh0=O) zBD=AqwxXi?{$BLyp1-UTrNSa%=JG21+3kbVvH}YC;#LwbVdIbZSaTWvWU=^6VF17s zxU!XlQ>9NO|AHNTycCUk!^z$FFc1j=4%V*9y|ISg4yJA(Je^9~sdIbc(J20mh2PRP zB$syq{b&khkPG+&@@k(d`q7v*-=*hi*AIQj>u;6a0+qMv>Of`MG|ecI?;J%Vf6^^f5D*<27k&}a4)?2kaZ=t@Fs#sbJLMl3dpgX6?{^_N zJk{eWpD0XkyhdA;N&PYd(n5QRDZh68+b28B{HV;x%&^QbJQ+&8YI@c8eEd`+Jx_douN94Ix5N*Jav7J9aiE?~9 z`2*U$o$)+h1t#3%we(VlDO!#Fb}cO@PtR&!?w;L--=h|pZMbE6<~Zo=<&MjMyZ&N~ z{&Q#!ZGZDaIv(mwzpp|%pccB;C{3EB0LJOYF0B0;&jpgfA=a+dppnPM`*mlzHIdN3 zMSGCy<7b2${(@A7D%b1M>#^QVq(^IvLESKvjjvmi-g}N7({$vq=W|-}!KAaX$-B*2 z;a5>l`6*6TTNf}wLMd|M5ZLed?A1NK-$`^v9-1O(RMeN3*ERZdIOqG<)=@qM#?@8@ z>5?>QvWx&ylN}kf&rJV_(_0gcpo%f|%Em=0%qBNGym599S%%S{*}a!U*z%^J@gDe~H{Yp~Br6qg;GB52NU zs;p>e^lpbamjB;#v`D3<%0_Fu8wM@Dv;D!%BZ&&ycj`Ed>`YEb8qA`cnT2`v2AQuio#6|1-{2H~uruzx`*&`LW>t zIZhpI6K0$b{Gs&imCP^5ER^NTiKXnF@ym?%K^k8KPGDvA((bfwD2!mB#C?gjXtB;* zIDaP2_i}>ItU(umCSc&H%)q(ckN&T*BZfN_V2pRMt`=P>$X_@+fvwC0Kt+4&_sj%# zlHa2(K%J%^j<|r<1E3mQrqN$xx^K8Oh^Ft8!8$HS?QKBbBP=bhzODW{#ccvjN|=hq z*&TfRi9P0cJ&SlHfUdjX8d+pns*~JjYB685C^ zrwr(%Q4tt4-j-Reh^qE$_XkeMN9Y>K^~W3WdL8ulI9>v}{n!+W82|?-w~LoHIS!+Nsb=&qk-loL=dmc=V`{5?JzWB2RmldX{+QgB>6p zbDu*$nDm41&%zO|J*dP#A^nfqYTH`ER}d&>^&PF>m);>J&y%Ijl_$k+jg?ennRACH zxE%JOIMNAk(!(12R|1X?8nihj*%jIFlrA0tQ?|nC+}pBS#M3jR+#|<26|l}*^w7*G z7*Mw5#FYd#1(d0M4d~@E_j0FF2g`4C_w70*ijn)J@bC7H-f91}*e>58(;W=&L#sVn zoLb~9lFp1?qYO%5&v%G&_9^u#_5qPP89fI%0uX=|joG2Ay|(z@4EZkVK9K+*mk_M& zjKQr;B_k%+FO@IK5h>COvi8dlyYtS8U zG`nf{1ZqUU!Ipv%Tk9nDWC^=d`%?PB*<={)9(F7DC`E@x$B^c6aAr8dETw;eUIJ3R zg+2nEZf;it9ZoAj(&@0lVFz)nA|O3>y6N`KRyZ3ih#@D<9v-N&?5?F8T6f+&(ndh@ z0Q@}sq5N$OxaOQI>R1yp?hg*XqGR6ORbL6Y!v|jiF}w&v70?8be`H5FOjYoG*5Qhibw& zY6Ed~?x=viO8-sT=^r_M#6B}t5h#oqZ@zQ8^(L^41mx(c2M4Nby6Py0rd`v=+6Z8M zJCd2+5@(SY)HhXd!Mg%=yazNYIa)7iN4fecm|~$86dMxRF=c@*Ar(Y8RAu!U4-XZ* z=rGtHfc7FdM2oUB2;4JX!{+u1P@BsX8fO(o6h`JpCrL6OFI&-o$?vuAWhp3ZuXF`+ zH)_Kg&_aOFPPPp9wh#6g-!;5rMJOlfC%lgh-oqrS7;8wd#ofuSm@K`S2hb;%ej_NC z4S>WnVyY>yk1n4?>qo$Jk|FyJ&e)LX0?XIM&BMXRrO33xDj#f_1W2mvCgJyR9@qt7 zu9gAZg65zLL&}fYIn`&bppBqj{qqf^u4bDTb3qmRvZN9TXz(2rQ%3M$(8L=~G7jM> zk@8e|sw}nMr^%(r`>>x@sCG;`*y$xjRHxK13M)Rgj+T~AS!Z)1h0$fjg{66*3H2c~ z#d{3F<#Y?16=a)FmvgC4P;@{{K&)4hWwmXg zJY6P~3ZW8;wyxlK4WYmh1bswTs0ctQ@1t>aWD4m)8}JQenD}!q92$g;1WB`<4P=sn zB6HBHGT>q3Ib^O&o_UJbKprJ1@^1~O08;7#*=dya?{_KA zAp>zmn$&nQ4KsX$RsnhT=0YIrVgKL*H_K=9Vws)GaRb2byG0PxOO4^FVHBe)gK zMOTKFv&ws`Zx@ef=TU(=r#HCh6F8ogQLYMFzxqHKIf8U5!ITj(h}>P%vsxH`t5gY8 zu;wIl7W!tId%zyO1#yI{*63)?#YR8HEZvSK9IaZxa42nS`0dAu(lm7_08Z8~>R^e=b$y-QZm8 zsTX)OLO1q*j(>Leb{(_BhwJ<^`~w~TAH(-{a(1)z@+v-AV_i(xdXr7w?T)3M0nvV- z_~TP#U1L|03*TtD2s*j_#sf6Fr|r>+?pvKV03ickIkmB$Cs5!NaQDk6Xcs`-ow8ZP)cf2DQ$Xan( z5hGxz?4O=F$SxZ%p30qqIDt{4X+QnJ;8^dy?z``|rfri?hmqxU7#NOD(2nrTVb;^@ zy>Ns_9&^#_eelB(w2<)%jAOh4%fS7r)S<#O!?in?iY|BQa)VYyW;j{yP-T+g)|E>| zmOFL1!Xx|}3>nVJ!QwmBH>=d}A|Ui%a=dJFB{|}qw)!v_L4d(eDr4zQ182_wIpNLh zO{X<%4FU*sE14`lsmc|6hV0&`;$`~qPKV3ZR|u~0XMnB_xE4hWfj34jcw;0o@85=t z43Ikp$Cdq$ww~T*Z{$xXRc8LH-@E}3)`==Sn=Ks8z7u`)r-MB|S+ote4ZWYdDpJ)Q z`(+q8OazW{%iY1pdxR{tuQbVY?PO->-0cP@)XXrLUA2kWr7Mq$tZ?jx*{ND%Ulqy$7^cUW!ngsmBVs^~=i!qnp-Q!p%Ku7{1 z8y~7=u&@7w5Cty?5ImzW>&28Gky2 zTqXnH@FPRNLG`cBv)v}or@V;@$Tw?n0N`PnBU$IsZk^{{-be)&o@#J_V)4hgi!3#7 zuIXuRZ&C|xH!#;Xv-eg*4^rXPrmBvHmIk$ub|Xt8D@PwSESiBr&wVyGw1d8Y?z7&` zE#S@bT?k_KQ!uL55*9!tov1&DJpw7>rmuE=@6Rt~Tm7XYg_Zz*yDQQMgF7h=FR z3YHZUg`4Vwu&e<8`~IIEWGlVUU6QM=h2CKg{O&HAZpYIgJ@dWk2+|(5Z2i~J(}9Px zh%uy`cAw(SAnnk1y+-kU+mN3SFVP=E`jGCxcv?NJoIGX`&q38de78{ z|9D%)D!1}>Y~iF7Ch<15ay0wV%c03J6x{DsJbNSo6Dh?w_cnbBJguGew2ZY~#Ibpd z&pY?mTMhT88}AVA$|~ND9UM2C6Lx0a?@#o-RbzuSec_4JC@=_D&=i>$o*$GKh<#(I zg8Jf4Ma!W~D%{fbsFxL1IY)_(qj||_S324%RFi~dhNnmN7g5eFdv6?VCAhaCTRT7+ z0nfWp=Yi=1_tY$zB15>fP@~WF1~CnWmR38r?ANf)fDJY^=2p|av7S2w)?>$Ad?%Yd z2&eFtKH=aVec+F}pSG~G1TubhNQ$7la(JMsFX}oqxt8^I4*lXj=WrkT1@KakXEcqX z94ONV&y14RTQ&A+Sn}qnah~=(d(!osz@f?lvf8uBI>)EBk_sxYYIH6pP^BkX?bT*8 z`&wXuMT1ijQBI3V`7|4ULS?vO>7Mt4Qm5G;x)a9l&4i+L%$bF zL&c%O$j%DNxn1+_kq&boDnF#qufPur!>QKVmiC5D!!pWJ`{1GAh|q}0mQUHd7ad(Z=Ch z{Wtp}$K^v){fu$%%VD&Nsd_X{a---OHF`f;q^VebU1wBG#ZUOX_Pj?Z7d#`4k))3e z<|Hz>o$38-ELN_<;T+umMjgEX3dAY2jpXL);!D_62bn69*wM2ZMlp7mu2_S>>$?oj zc-@tBfY+om3PyC&^!OokiLIPc4w78^NBD=~HsprN*x2njiB{o_8tQoGOfEgjIXD$I z1c35iIEf>d0OGiA?5AOUlzMU$MT~P7YM~*zjQ5!j2Z>;8Ez3CQ)*u@gt}DrWry_U; zb(c`lWuBeB&@i@BZY7%zHLejK?}Z1`h|M#rX@cuqf!{KsHP42dkOKmTSOei8T^d4yREU{|rYPgMdAUs?U-6cxfiqJC8Y|DO3XbBogU(qh$nF zUx0^ECy5R4d|h;F3@-q+3skcJ{{EFN`e_Uf_y%k|0p;abOaLpEu{Lq&~D(WTW7P-1yLrAM%lE*_$#o zLH}CceDqO1+9N?J|72$midh^ zp!>91PI8|wMGt4ds9t0IH#&ITBuIjl0n2qMQqg`l2%hC^RD~NzA6F}D2e*)ph~5}L zA_es;r;U&~>$v$A`(3`m7^#0!OtZ8tr>Q^^of##HG&Eany4P=3i#_^I=`VewU4$q! zEi5JU@J_sF3rN9bL}!4nIKll*_NpFzeE;49>E2i!uK*$YtKOhMl<1cb znSU|wk}QF!&QHoKSn_D%41(`3T3akH$jQki(pZf+@CSSht=a&4V4hg0*=TUv=GW%3 z-nQmO;_X&i4}cp(bZLKd#$(t?HjGY9BIyJ;{(-o`Gunl;a@%jK=K*ZD$fn?WpNb4o zhDZd!DjRUL>sPvg*_{zGWFIkDZTXESh-vyI$4nR8tr=@ci%N@3iPX`>3l4nko)MD) zV*Ec=BVy{-D3KhT7<4^I3mon>@W+lKzBnj3^3OW-@tyC#OWYi_FDQfv=kqTHFN*R_ zxatF97S|b>s*8E~K&`J%$VtvsG}g<|Hy&tZy^LzYpWMV>OzWg)$#cZQLw)-ZaT_N*BZxF z4mv?<01SM{c!LWdm4S5bbOP-5H`RwAD`b+nq|Azi^jvl}7L#|KUg&o^S9MaSDpvRH zg>v4KnZqRDeSOZsSR(<3$2u1n9~ch^fTPA}3oF()){oI%62cZ-ft>7yJ1q3?K$6!iw5_1xB3Dh@$8(E@9s>g5< z`5i0OFU?QrPna{ZJ^85x1fD{bU>Dd+?;8BRZ&pBouWkhVzn#v5HRG7k{!VO64d+BMO{wogmca$b2zxDXmGu|7xjfN;_cHU=U-Uh}DlHDi9FAC;u zVt~G8*&3g)Is(a0K2+}>b&QR6+$Yd}R`-s79sbRY$NHJGCvShVIehg@29;m4ptzS0 zTGMEv4^$_)Z<6FNnLN~TiAoqy#pOT}Pb|D%cpVbpxyn8=!rRNi&(kW0Li-P6YxXj` zrFMq`%8V&=6@8$(7Nu~UFDKZ67AeqDBZz@&=59h-npw8cPW@lDzZi8n)U{I%MY^>* zm5PF*io8-{{=q(aFN9-y;Gal50(Uu7T2Gc44Hw<1yiu+e)4k1ktCL}f8mfXOC|14; z0E&w9W&0~oj%TQV{j(>D_g6u0*(ErGyy5xdi5Az&4$7*0bB7jS@bCUcmipG%OZ;1F zsZfPuwMRb48($-XJWpGB`8In~C5pQ0GT1tA(~oC^?a2gtc9HoK_^crc`OhLVHU>&y ztPkHW8iIYw0Ig@)8kksUJ3*<^Kb8Nub9}r5;0Zff-CLg?{KJ(A_RhS$?Pfy+G9Of~ zVhJJGY5xiK7qh{Jb<+xk6Hpb+_+EAX{!!#J1gxZ2!&9&{pwJ#RW)8i|aibBmw2jLb z%+$~1ktO!!Mj0+$g;aE2K{zO|>#SV?%B5HHWhbDXtLA3tR! z7RVyfVr#1_nuK4ngc5&30`>RccowNK9I0YW5?L5TRg^&pe=kfB@^e=&|0veDPl zOMQ5ClVOWIvN^mX}P3UQV%)c0QlGD-8zyL?|sHA4h~Zv8UDtKc8T$ckLCLb z!lGwevXCq$Co2+57k$5(HD7lWS&Sg{QRk``OowZ7bg~Q#KyQdA3a4tOY=*giQ~-c^ z8HqZ;d8UJf30g!J+LsuoyABpmG36dT-n9h1?K&B5?|wYMxj&RrRFrh*w-ImG_kd#j z0Uf+Y1FMLlbKO&-fP-I?3(B|olQWv^f~lO_(mTX+{Eo!3H|#L1Vtxc~br!{d;4M6( zI&{&|mcA$1NS*izL`V*2h2B}5@c`5Z7~k>F7@2z?#%5#Xh4Zogv(}kD=da0^AaLCA zl)XSQ7EY&u+vtWX0MC9MJ5HjdbQ)ed1zM4iQL^Xn>3`ER)jQemTs1lHW7{}9ruKmG zanYn6w1mKgYD#xfG0jE4(gDbmxBWba`-zECpN3Aed5_Ex;=kmFD z;g4t`OPPq=nJXZk@^_N^Vma#32Bal$3#L&IK$SR14Z6{j&`Ips@%A@$z6~QB3deyr zoaqL}XEPtp!B9I|2jJ7CqiD@2QqPD+NA%&uyJPyrRMr$I<^MI!ecmy1?0a&|@~6WM z_FZk1P0hw}-DcQXFOpVP8}$!6W$>kesnJt)zYLB5VsBvP!vAnn%?9fTP`aEgfP<1I zW8PkJ3(VUQKNUb{B9Bxbg1b%&W(PH377ZD|XIhvA1MB-g3bT z7Og=iLF^Ya`&WnJ-BEaPJfZvmlX^4Dpy30*Tsf*-`wJU=o`{x-ev=UM0|#ICycqn~ zwAnbQ(SL!KjDl$p+Bk9rZeLO%$mm^1bu)2foqe;R%&DuI3aPg2wS!94S2zJUrlxGY zqX$hiZK1IP4V+wt_nrl=)_=?gtEA=uOC)|@1xHOM$UNq+!x4a zHCuTRe@lw8a>Z!bzrGcnQJj~oJ3#>kaMz*V@f)^7NMLl_Vj(}vJ28^L3;xXkW$BGa z@rJ#t(q{8oMOj{T4(uJ>qp)`rjO9*&#>IT_UFMB2CL{|-&p<;GVi4VV55y^_zXuIMJd@0tMkZ-UKmC0fBdV{8a&H37(gp%lo2$s100~+2HTv8lISU?HA|3gW z>f`O`3o(`9-JBO)?he*^fB%T|V#uo+xNSjlL77kC2 z5d){l-Mg^PL97uZjD`=1MB9py-u<6`B*rbl*?umL*n~X-(<9RZ;8)(IDEc`9?q`}upIl#J)4ewXVtA{>f51*C&T_~`)lyUw3>Oncc6QaN3a^+eXdGTU0hzq zWRwq-#Tbw+EN53oAAVd|Y~&fZ1Cc47jFd^vHAE#5TrGi-n?*pNz%Im%9{_SDh7(B) z9-U7dpV8xe-UDor4^@P`m04hKx)Y@T%5N1wE7W7~$DSuWj|b4_;M#S1wlTAC!sZ5; z4m;gIE2g=sD)a|Xbm7he-5k2>r8+tdgo$~^kc=EgS_d(JRoL9^OF^^~*@E%`U`t-f z@FBpK_@N`@8PBNGQ6PDCge5J>EX^oAB&FaGYX}?zUwk=CklwO=#teZu-V^t%pa^x_&3z$isLf-gRJtEPS*{@g_ogMg2XD-Lc7E+;S_DxD9I9W>?bpkMt2%V+&+#h9k^T1pI7?jBQw>fNV#L%(T_< zhjaVTB4akT#S?&jT&smw41r7Es6K#CHGmizggzwkieBa=6Tr!8n6Tff&Jg=K`|4a< z{m@=p-ZC}^ZJR`#;ru?kdi3Q0`n2Drk9)dBMo-O*lOlhy2XlX1b3E zB>L0@1ZHsz@*!=|L1XAZZA4zy?15!uANmw#udiH7ax*zH{ACARbHI%3K&#By*b!|a zP4SmKY-F+ktuob6S8C3v{0}@UWbJU^Yn+@}$F2bb*MfUcx~O+;buas+v7@28t){ti z(4?cUsTqh5dnaCwR1JETAhiMXVV@g>=&crQgl?1g5ZmbdVLP+M!n3(SDKQnb89hmz zptK8;b#t^Z2|RT;)^Lx`(b~m61L`)gVEiw8CnQCugaKuwzVLF&|A@yaz-FBF5TN}p zCkKzb!26p~2!m^nD~_&-Ewg)1LQN^&Rv~7HesE!BwyQazquVK$M&`=9%bAk3hCu!tAAK{ zXcRDygl99NQsVM5awOT=ibzF7Zlt5DwZC;_TAVN`96{Up+SLJewqH6 z*?^Y*2_>!Ioa{q}`NKo#Qf(hJ2R2WmPo{IHkZjoPHn&ZS-aVsB0`=$*C69$v*OT40 z1G);kVFl&eU@&r`fk3D9$QFa9otdUX1yn@0^+PBCaR&v;l~M0()DP!GJOlp>-euXM zJK1^Y1BmD~-xP@G*7NZ(&c1jqs8QRTWxPRPU)gzw@H@GbVdJC|H>BaaL{W=5CL8a%LWY@m?DRAr^rQ-Vihjrj zzS!EC9nAQ++XQlLQt3*U(yq9f-T1@ajI(}Gj(jgQ;2?cLhWq$?dU~1HQYD4?#o5It zQmM$GkU+lx-)tY5Z1UJ_Riifgj;IND6&Z`o_&@s~QA8SHL&nxOBV1hKCFULM%!CJ!zyq_~@l z@ERUxIma^rtQ!2FUf2v)1@H(>Pm}-;E|GbzZ6Pay8$9J9MTHbrX9r|E7J5k`3{aUw zPr;QME#Cr{wJ&s&!euW(*QGL`*+m{yT1;`1IDjqz7TK9NpUVm0;SiDHdnKafQppv0 zI+2~0$apwpVFgZLuRDi7H_%`n!7xT*^tUv$3aKo7%B~7(@{k9Ylu&pXJ0Qot&?6hr zne-#xymK6`w#t93nl(qD%CvUKa0q0b42GT#PBJE~kXlUrK4>}$BAeGHE)IdZo9QUG z*i!&$6r-QTct>pm%)SKRDsaqbW{w%dQ(&7^Fqk_c8zIiC7XYMj&gUxl82#|weRXsU zVn2se0lthe+I}7Gy)(Iel0^Ukh4um2cf_()H4rFPGQt*Y4v9T4{-J?ZV#X|kc4OR3A8Izz! z&n@~uuo*99>odAW$?UJ=F=@1sdFq6NeE5*$VG0zMSL3xT0TXC|8qb<#_+E`@zEpfA zdp6KgXbP9Y)0p}6rTBv*>Q^RJ=kF_w89_eq6@~KVq7gK|`}|15XxT7SH|ZTS-|?Ic zINV&7i};H;Y_WX>5C-7R)U@PHWZopWJ^*WMyy1=JEP_d0m82AbU9%tTPvMLBF$oE= zOOO}ZF^G4deR|4$Y|DK2fl&0>_2#Qh34mSELb;^t`U9A?=TLeKu#Wh7Igno#65I@d z(r1M3WfwK&xA4~Q?YJ<&8FPer!qL++E3 z8M~SLB=`4{Xgntft9WP-MR?TL?q2wQ?q%vl?&JM@n|AUyOfrurLmM z1Ng)$b?%K4#DahGqy!98w*q14$^F-0xwgdzbG9DL5aa?jdUCJ|ykO=KpmT5ze{|M1 zHin1?+pm~KvEG-jdMrtaN({IXR(`4cvgC^VO2&1OSelbOoPu_1azIrERC@BS0ND3Q z39=iy#7dW?%hPg&n`CRtG+g&v96xFa?KbbE=%JR@-m393y$0hxTj+fih=hb*qxG=c z%@(FTxPl_ly)?@#R~PC+#XklnGE8-YLc`o{f_NHj5h(L%yF1MtKef^wJCd^6CRTHhvUS7SyRolY&(CKu()GLWU*4WxvHC$>?ce2}_ z0i$P#^y_W<-6x1<1l`@0Dd;HCK8zh$qAp=?YA?`JV#&YYwk?jq^Ssx&zKTGqg4Y15 zK*c1_md`1gP)uh7I&E*GcRxDSe;3OBgOdLdGqsWP6AhCndYBv0KyL#J1a4EqYP^aK zzUf6NMMtF+^C2K8UnQy}OwreyF=c?J2JyTf=?qwX5e+ZEIqJ?v{3n?nAoNND@e(cl z-(hv_^uhBrv@&9u)pc{U>Zt_HWBjwZF?Zter}5UYd{|&-VSx$WFR=d#-}?oYBoxc! zAcc?i9p^yw67Q-a$01Cz1chO>Q6<3ReNKWD#yPw&2D5#?aeWhk=EeO9sParQ0IxfZ zSL?19#pL*=@WnY9d7^yLs%-p0^=daQGoynxLcYW&DQ3K}t*dgBDABJw(P@_^$te(t z4C`$N-6xw6TZ)ze9);)`2DrbtOVpd*3kj#Tj5B+@<}wG?@1W_;{pn9yixz+*nLb#R zodqqI1NxwOjn6V#;Q#Bjm(X%EL4~GRfTc)a$i|iDlZ;fkj|f^jP4$@4#)!7T%idk$ zL|(GEYN0uzBE!A39749|6gq7M_56E#Ii}Q3+R3NSDfg! zt%OG2%&`@mz`@B5q}kEliHMHT+5Pg5$%(e~QCUY*d)7oo7njndAJe~+=mci$1@sUY z1O%abD2UDdsRopR?vuY`%{43wI27YNWQX$DznhUSvF0k4`TLh#;3ZJ<{hECH$7CBl zI;aHf0NpXjerwl$pTk=r;6O~#7ZVL_q9IX7dwc4Lq>KCaN%}SY8wuCueY%3SLsarFWACWy+^)I3?O4?qUTByWF;@ zxBE(>e93w7xw4B=;++493(gkU`?Ad;KZTqGx) z9~srTj@3|h6}#IH0}xUZ`Vl8_f(1&jz(JNM$P71}oLSL@YdTO=D~@VIscks5i>+LD zi9CVVZNzKVd^2l+8ChOYR$J0UD5r1E=&&nCN+uL=Xxlw%P{Qv=BYjW1{~AW@@-NWA z@n<}vZ#mpAmTU30W^m-_-z4T^H!#&RX7t!q5C?a%&MKuGcR(N}1zh1RKF)dfvpQ1k zV{xgaD)`f<@Aj#`1Rddzmv->%gdFbCI2E4OM;z`sC^TSgN~`D z2{imNRxXAc$e3n&KmHEw&%BkXeGBO8KHF0zS%tu0S=F=qiQNNHToyn1OujTfSCLN? z1}cIw!x{kVAoI)h%*|GS#b|-Cn2HUK4v7p65(kNcMTAmLTQM@;Qk+ia^7EteB0YUw z16)IeXXGKNapm&L{IZg~aCw+Gs=kJbgp;nnBqvXnm!&Ytq?nHh3-uTKi35d%Bgok1 zMdgPpLOnd3y`2MrkO^s9d0s_vS-C7cD^eKMSVKjNLnT2{dA_VbTm;V0F+ov55g`Ft z0hxh-lzEPhkvTEB(YaCH0Ukjf;egKsN@uC8Oi^4^6s!oz3a_i7!X(nSf6O`#+M8c zc=lad5aS+d9px!HPr+kFmKa|Y(G&vMRUVEbaXrwCLN}NhU=0uGAo?anAnnG%`+zzF zn&FyPyVMU$!tO2>e|I@^(U2q$sm>m>)1c$4asGT~NUXo*thcRU1{LrV}Ib7CR>T zsbLL6`mX_Oz-oP_s@#uVNnD8tFszt&+5{i+wb0Fm>bluK#oOkDHYsmdeZ{rf?(SV&^@hVWt(|6ALQAZTifmw03&e_gm&@t zB~=47&Bp(x-2p(Sa`bCWJs$f@PHrw$9#r9z8*F2F%IQP^5L1Fvq6Uh3>f5>q?$q?X zo|nbw3x~TL%x0wl)d-Z8@JeM9Puqd^q0` z9DLXWjyOTq9_H>&Fao=2uScOj5z1p3Jk_nxbNE=uQ||=%njrv){5XQ7-Aqu{-Js`y zk;Tk82ukCL?tOXIeWjFtqseXCF3{^*;H`i9sJ#^&bt8m=+P2oar4s~q3$g$|5~Db= zj-xpCf~tvnx;!-}CC4y>VuG_q^H|0#p}3`?rN4TNK#=KqM0-Yg2W5Fl{6&5)RDN_x zXij)^NK|N4Sdi39?l1QApcIisLGn;rI}11SAco?T5;<7ZTi4b}K;%d81pCI_-WOm^ z-GMEB#;cLW!Bmhy%@7_cG;XmOB)DUJ{k@ONAKK^gh-wc06{wE7=rVSP!z*FWvAmKJ zp5_m@pE2M<zXzpyMBLkPC$9Y%&DN zz1Ov3HcTp0h)CiN3ub~LRIMil_T@97@@X=-Z3+4}Xo0)_sUw!wuzbQ{`Ly-ks|LEt zbr4z>S~Q88L~R*r=bDY9EutOb)ac{LlQiZRt!UjibWjZs9zzo>?ixW4yZUsaYny+n zSe7eJKT=}YX$AA@d0PIxeV&mz_B27dtRM^bldg_XWfBB5gf>S3s52XVMmn$7*|gVy z;c=(M73LKd7nGNHmOEv88EYOe*z5W}oI}NZb?x0u!{z3^zE{PFOK_7_>@wj7u7?|# zKD#`ZDjOkimvHSAs%651|D-j^7^euAF!#VB_e#%#)C>yXj_R`!a_p|^z2A;J+TpY| z4e)4`KJoMT^*-+3DgfX(Mbf*mhZ4M>7K7NN1Ac-WaepA>{RK~kemeLqMJ#u{HE9uN3Gp4FBGQj51Z-cqP}+N?*#h9h-Dy& zyuFy~C=h=Sn`yU1dLPg;*Wfq^(xVr?jSq^@xy)bW`|VZ9l0G(fw+f&|CUFUix4f8h z2sn4e{IbZ>2>VdmaJ%TVI7x6)Y>T8VrzO8Wuqr?iT3JH{O5C%Zv&(Xd@^bUdvZ&xd zA8<-=mO4sYp&^-r*6-&an@*(Fg?zf0vh}M``XC5kubMF=>4Vrud5{Hd3)0YarOn}h zGFxg|@kiZg9i(#KlHd8hv~BaMXroRQuddstfG*x9($-|R&Qfb}oHR5wx+JqGD_>IT zQ0vy>wZmmi;MXx}(V{@;R?n574+?*2CLY=S;r&-cN){+(XRH%$%+V~JZ0hN+ylpSr ztd0{{QfZbnQwktO%pyH(ZC%W~O^d86-0QAgqnv3YvWj09Q4?kxVijf^B@{@5QUtBi zuDp)I)__L;;*g34%1`2)ZJ%A1T_RWHoXVyGLOerUL#^Z%GFveLitY>_svms^uwEvV zxcvc-rv|!qHl4M-cYx2<{yrF}VD#~9I86Q%j@5WG8RF&T?&xtQ?QGiF^pn971qVh6RFzctNDDjW_-(JUu3h z0Um5rbhSu+CD0c(tYaC$TURX4m!FbT{&4Hu!mP62-?}w0{Ci-F8KGn5gI7cVo;n~g8tu*- zXwH5{pf4R@_(3rICoufn+2LOlU(CJ84Br=qzZ-@hegTGGhqj@6xQZ>7X3E9#yCk@2-?L*AZ>iRPYxiaYjjPeb9JW=ffRx`?@_wx^xVz03>k z%DifXY1Dl5L3dwwXXlg7#*PWdi8f6k?u2aW#-z@~gBzLP;=XxiMF;uB70ok&ZNG;y zEXSOVZO~0NpYi4K7IGSJ2?;h=f>Sv01e@ET1*+8-LobA%3ny^y380ZWn`qhL6+S}1 z?!os@ulnI|`C|eNbmMx~HOUp}6&O_-9C544RX5S%8z4eX>!EfM&FJA77CJT;%^ZUtj)LA0u120lxKST9z7wir z^_y(2pFL7Xy+gRSA6kReY^4@*C&k_(CkEqv++b$OAU0uT6-q0!Dq^ltuLtnupAK%q zv4-d)R^tdJks7X@aub!_pV8# zb`!0-@gePsZtxa(bB>^Syl)CPO72<4w(8D^Z{LgVKYE_`F~Ys8$G;nUK9D$yBopA6 zM?ay#3Q_VkNa38AizF zlKyGYLXdX2cp)(SjN9ciiy|*vFI)nOYR#1cUCk{P&6K~VlfAPmNO`mI%HMeUn8Ve# z=NVVRUZpxan%I$sf5!7TQyogf#uc+e`eXC|8WIeLH9MT=GqKFaFrR_neNQT{vK=AA zJF_@FD#J4|@?ErgdPcSwExda%<6=RAlmL&IWv5h;T&@m{6~-|J761qm?e9Wi-Q!&o zeUH2ELBa+S^G4(0y?3W9JCXIE!A;N{oH0i`$y|4Zb&7X$4#hx>{Gb{BssJDGC1rL} z<3#69DHUH4+8Y9fL5D-hv=fO(F532{Qwfdn(=m{|XeN=d$AS*H*mZhQ)h*fgMUc9z zn$aRH7#-rZ8`r%zd@QXfsVf5f#_$c$Y%3ip8qXakf^a8k{E8jE(|>uW-gpd^C{?9B zd3$^~$~;!qxGHy_F@3&E8Oh3+P^CeguIw>7+l|lOLs`AbUrdk)*yr=mZXHwg`C6SQ zxM#5IQJ*pL+TVQ_Cc0q~nUA0CADwXRV$}4JY{mhmndhEso#x$?L&X*PH~GUfUszx* zW||V1W|x#YR}nHh&7;gTla5@l?PsQWc6OS3lgMaee@#!@USF!XHTS;w|4{bcaZz2{ z-#->+P9`^Ma&pWd4A^3?u`42?A__>6CMdo4-i9(173p=TL+`!Up))WH9qbxgOk%1j zCVTkq(frn+&%NLKJb(S}E3eDs1kUWe=FB;Juk~5)kB~rumBGqh8~ra5;szQAZ{X;K zu!*QhDcGKUEiU=05;r*2J3hGQR(GtFyw6JpyNUA5`7ETZ&yW<;a>ixF;X@KHTFwj@ zCf=iT7k9$iVQnF9eu!=kTp! zNE3}tSk0v@1c2Qz*>PBAF?I1FP(O9ir&{>agXmKo{HY%Lo|2iI zkyw=g_=O%LRd#YvIww8WCpPd_X!o>b&<(T+4=D{17U8OcVZv` zU4qH@1p?_xf7bKG$zG~BD8hn zT9?!)8u`x6EPNv(UyF4?2ZArt)rtaMD)JBwBmnb;2#=cBYmCY?7bVFSJu%< zP>|K96vG*}mXl$ zO;$1k`^{h2PY@C$exqP&#i*O9Tqv0*7BESbI+S1Gg1^*OXmq4$sq8v)*ghw;$Safm zP3P+;GcN#B2M|%nv!WHJM~%CBnCC|@^9hz2jE-hzpe_+UOfPMy=_~I(SI7*ucE03o z*A>D9QSDLMk7+M6=W?zLf+d?5_6o+D*HhULJ5Y`5-%fax_#g#1FqoisWOEwa``w%6 zRvjJFA3;Wf;l9~Ee&4`|are;s)%(Au&1cZ=d30pXe2zT%7`po`DYV+wx|3Te{pF3aMpdgvg{)6Ub; z&t2rt%*^SC=nn4=l{@ObDHQlj(89*Z49W9I3I=~X^l9;rJ>PmfaU1gD4=@c&R7+2k zw%69S752j#LjVmBO~bNi0-eHK=ry6`q0m1UM$Y_>Lq%t`u-BLHEOqV#i3K{V4p|8l zH%lR1b*xLnOni(2tBODi7bh!Etvsz3Y!{A+H-;Ys4yn^m{va~i`vmq%mFiu98cu@ z$IR&Y*VuXDoF?{!F5+Vnpio=VLd?DGZQS*9^s)@Ngpv3i7@>nBrKcgdCZIhVD7_FpG$Z?2Iy+qXFa};Bub5X#Kx}ZdIFZtw z-i+Md$0i(NbBz}67vd8F{X{N0plJqFucMcTkTV7G(eOPGgy|f3;(8~633T4yvBd;A*?_l3$t}ACF0D6Ob1`^{wffE9G)Rn1c#uVd4E^lYyHLZrR%8Y zXeU#%;!wq&vevqa)_gvJP6&)yT)ylDT1@aw_DwdJOVCKw^<`ZEUs!)JJ2H?H5C%qJ zI(i6S{LFoh31KXVA{EK%83eJs-@$)}FukDk!cM}rjn zgi@^lvnaIc=mjb7^$EP7V-Z3Wx&31qPZg7$DyMU!O8l}y^7DDHfg&Up<7f-|2rCJY z5?b|Ez&iRio8b}X1i0cQ6mL&We0rf$Br7=~DKRmTAS35g6T=h3BiGg4YKSXJEKB00 z=9ZQUCJ0|81)S2zbsxpRA2IA3yF>VZAJ9<@We4(HuthDDq))B!W3 z&$OBL_zcKEantha*Mad@yNE8~i|G_8Zvg&UW8LuqK#bm3f`k#da+=IxA=ck~4J1T%n1eKpwIKmQ(}p*UACH-G!~?E=`PbS_pf_9)Qx z8F)3?a0M_*63oWe5L*W)=%Ix3QapCRDcq*F$P(a|dx$N->=}^9;*>EIg|6YKUVt%m z!Zo-WJ276V?ZIxZXvhsR)@QLB@UHqL`*8q;4(&P2LSe7q2DwLigWMyw-^9?Gu0iTL zV|Rz{4cL$M58a1N;r3~Cd}0K!wUgl5P4VGRUNRl4fZF@;YW7-aMS z<`+|Vo6b}GhUUb!cXY7m(PH*jH};pv7gl)tHYzy}PltaaKZRB%B@Qda7i83@f-wQ4 zqi&p&KYlTz(pbqYHy}4K9q7S;aRHr>3f^;gsn=FoI<4+rvQ3b-)kEoYrf`~Ar8&{? ze!8}z{(-p(-K7a{nL&JzcTOP2IN2sfZHs)W!LugpJ86&bX}tT$mdqdk+XYuQL$$OQs--hhyN8L=X{eTxv8u6> zVk$MQEVV#>nC64OGQi7@YiDx`J=578wT<0XV+4T1mieRo^0%KPB#D#~^3!X%xkU82 z?J?)%@n*at=Z&sX=``ri)jt{Xj{W`*VLV}qX3G^8L1lY>f{r6-4yy2m+1m>h@iifL5?bn#ZVyW!(A2p zDI=#=EPe{?KQptv7Rp|^5xn3Gg|(kBFbPdI;Ea*0z#L#aQ26{6Bb|**ThN8uzo6v= z_7m4rRlcnbg$iFRXW*xfRJCu5W1)X@3)8pAy2h=DFv5CNksiNPR5w;5w|5HsNptj& z38v_09Mzmm0|+$YL!CN+H>luM3ZH@--?Fi%cmF!@+trs&DaARJ+}y7!ziiq%U!bt| z3&s?BgR}LJG5(0I=5*TOia+lfQ*sjhtYCYJv7qq9JH{%pcE3pvN*;nI#T;swK=~pv zL}8C91KN5k6l`xXhD#@_L{)N}&Qq?cu10}i9K9L(t=k<7{klN$S!_|~2=GrG#E_m* z+NWAQRWSp7s_B6W=MOM&7cK;HFNI#|VYn3@R`~Q1<1CmyW{HEq z!g8+!E9^~SfWh{C1x*>ohf`NfnRgDfzb|zEJQXk{8iT~g^lSjM2nH_tO3&@uiiq9k zA@}hE+|hQ!9qlB%ShjmJ>w*+MY$E;!f$6C((x)s@Pdgp=?4-<5PbZyB(~tm$2m~cl zAhvslU1mW;a!~SyvN1Qb({k0UF>JDe%*5VtN_(E(u0L!#ZGFUbcfek#4CI0fJd(*n zG8I3yy|DS!{Vz87N`Q0vUhcs{l}h4q_q4G6z8Pm}DwHAKxfgHSy(2TQz$zuEw!WqX z`0kS71*qz`eyHlBYpHQ#Xzkb};R!DmYNFcv=0Kl+I>_z99aCkY z>o5`b03DD)bHK}wp4?!T7gi2gtC{-o-fj`!z2BkQBRYn;bTD+bLB!{eKeG5ymv1#N z#58K(C_LVpQO0td~E~YdC&o#*%>U&(#osALw?wPOI%Rg8;zvn+z(m%QDMh`maAs zoS@llJbr4^HJG9Y%)4Qn|G(!Q4hR1;Zymn%z%bLO4d&gJQwsBj9(g(GtWOJH4m|9i z7EbrS>7Q9D`~E#uIh!#KeSsO%jT2h4qyh&$T?FZ?9WtUv_=fw2`-3_7C3J#jq<-E& z#Ts;O@J0lAU#p|Et-7fJa%Obp8n_GKg#e3u2 z<+Z|7tCafDUGy2f(XrLN#eZ=p<6QOW(JPQVP!ocNoLiJ3C0mm!Um;jSVXYbDAb7hL z?r!I4mH1l<>>9Iu^Za;Fd#|GL3UiBKuR>aC*y4{Vl%|Z+XyoeV1@UVNEkD1Fg;k6} zHn(vT3#q(;BOAA|&}n!<-w3M<*k(LdVLa*+^ay%}`|b}ec?z$Y7k>sQ>q7u46Mv>B z`HW_^^8HU#2rn+ffoYttaPBz+ZxX+uO5Dq=Gdu??nZQ$R@&eum4Gb_rngu%KL$JP( z>X1R2wPedAnv+Viz?P|K8amX&DMR#a}50}`6JL`*YbHPtsZ*43H4b7f#X zR77d*g9H_38Rmo)L(4Q;&i{Gp_QN?XL>@(}tiVLOJSQ8I4Oro>##li)bJfGXk)9F$ zaPNb`mYxrjd5YW~FHNTtrlbF1I?|u0;NR29lTJrsyOm8Vz@4Gkbjxmv;G+?2)I@Br5O3Rdcv zWt10Q?7?M=0AN4R)LcJ~(%0XK%;vITu$6*>i!_L%v#g&4o zrXa-(vD!Ol7Tyna4|3T@GMJB}D7248+LAH`cin(Y5P?-yp-~TS4Uv6__6zWS=((qp z%pEe|G`^dT58{&)dDLF!SG8?4wC6VUry3Zp>_G<9uV9@`%1y;vZ9n#hD)XbtGY|ct1VRHrr3K61-ejC0#^7Ftm2sj^k60 z)sJH(EnuP%_?Xl`6!Q86+APAGAA{ct-V8!w_%EO&)Mb6)uV92-oEeML0Et&l9@+$laDAP!yt1PvB2!=sIG8o|3MX=RL2O^AmJk zm6b~?#<#L#%B2-!w)?H1fc?9M!)+IhQCGkXQRi)#&6EvS&BmPUzEw^eI|!ppf?y$h)JrMH7_jgom%=-wu$hz|Ac`i4TIg{G759(s`=gvia9^V6~Qdxl9y&t z5t~QcWP8L02JBs$MS7Le03AYr%#bz>o53`cPJGEinbI)833%SO6?1-uFqHrM1h?Lw zAShrroWS=6oB+bm4}T9~So-^8U$P{AAEIzvB)%|&XhV0PSrayQva?8Q#(M^BUpU4R zyDwT%Ko?_s1$qX;%D|jfR8mlyUrLBif;!sK$-w-oQ$Q!XC#)Ad5RtD9tE{%Xv7r9i zJCJjl(5iaXvTT%9w{JQlr(k)BAuO7%8n4d_%?`;5Q8&V}h6lYso$8w&^0em9q1%H* zWVx4h*8-Qts60N9n{9v@8H zs;G=D1YlQrN@eNS!du7(uDbPw*@ZdzIYb$)2VeaLt01{UsF`uP?b7_U7yUI8-F&Dl zL4J|i%|cr6DQVGAGFZeR19)vfgH6#}6^61%e0t+A478VSW_8wTy>o6D&p$ov+sdi>p2pk|m|^hwOVAm$!yS}2ei9KG#{9V`_68_)!{KoEG1 zY4yRR}7&8;6o{B14zXyV=k0i*^9@yVlj z`w9HHy@Kp6Q2V_F$IwQ!_Rfn(85$ftAio8NvTymz2l;#m@)G&2xHy(44-;yPFHJ2k z@9xM#3~NMd&xb%SAK(1oat5ymuy&_AwSRDa-Bi$ASO>Y@02Dxx?|>k`1(Ike48nJE z3v87n>&45ZaZ}jDh8}Uaxz0!(NB4tc&RX#LfF+O)WA7~5@b6(9hGFEvFr;lQpk{z! z82qnc9A6qn0IeoFtlYa9&adms^DJ=6f?<5|U&9bX*#q7*NcyIrj%~^bFY{~nuaN_* zrO>6fA6gVbWq0c_{N;h&*+GSNX+h=H68ipr<<5uFH5!zAGTG||CErZ_d)ausapP5%wmXoPg zdveSeb|q&8l9e=2Z-VdVZ|Of}-p(WN3s{$+ZM1AaujU01_&QChrO5^_1Ap8@NtpBK zYq2>_1gLThAihIljpOt{U779$x$OtNaokF!sz?SBfpt}uWGy}fm&ReZG%g`|V1)|X zfmEOkSnS0$08ZI<0&hLAQ`#Q^9gR&jogm|v+>?SKs)S_|MIvI=wUNI@M@A20f)v8vV7_RM0i?A3*mj7$ zZEA2?O=)9^)XZucYF;(7mA8w5YuY&=PLxMn6Ref z<7f|+<)7olg`!#p-YEQKY!_?Xyiu{i3T9aiYe4SiAeSI;D0uYrk7X%ef&TTr+dMM{Fvo`Igm4p9ZG zgT=RhC&&8dpItXTkz!!sDH5D0;B|HFP`LCQj0dczd?Kjz!SYaUXa4g*Fd z4g(EL?*_xZYxRV9EApj`Bl&a%b93 zKXL*WvYyKz3*-7^`R2f*;cw%|G{M@3G;+C`uBI{_u_mY&!0lUa(oCe?Sn~^Rk zKk*7GHYO_WraV}706jl0EYv+NLOvoYF5#xq^~Bg@?z%Qa(IpxvnR+K=xt!EJbI~j} zvI3|YS=_A5tgPO??y1UoFhQJGVW#41q6oaN^<3T#Ng1J0-TO}C}u_% z1WE&|GXJu!xUXwn)*~yreR61aq+@vVZ44p57<*h^Gt2#GIKL3WOY9`=MTtG}3C z>DK05E2ryI>{4Nw3vB{M2FOYa2;hIwAo}8e=}ENp#M+poNlgQmZYT!vDZ#1!u*`YT z7n=q*l=SpmkZV&*9)kk#D~7KSO#|zQK$-yUgk?45jm7YFOoO`(xmy2`7VJ1PA!8C7 zb3G0S%W>f$p6>&h2!Tw9P2sNNLw-i`E$UFvWJp8B5=`*>y1Tokd!!x~5US-u^buT# zUk@4zl16psM<>XChQ)S(`Mydg509XsZt#-B0;q>md&(9BeW*CFk|Pmh_YN6Az6wjWdi- zeLwNLz0!$Sg1akRZ>y1p0BcOZPqZ{~QxS?1z@oIYy0pFsT8k3E8IYvSeyw#3_PJ)1 z^ix|MNOSaa+2O6ojp79Yl7iel_g8fVpTDl~Yg4*WE@w=Udk4M;`SG%N3%KNgx`Im~ z#f!A)B-tCcVq)2$dJr=2n1oy<$RU3#jT2H5Js4dO0A+FMZqqJGzMC>ZzC~{qYp62o za)S(~(IRFo4pz3x4k_|V z4@M{X&t_V0kMDYY5i#8|paCQ=7g)wy2lb&zyb@6G{{zO*iM)6oZ(Rvuk7D)$$`ODT zBEPDzrJyA+nTa;QFTKT{NLv$YA41w%Ky>zy!sorC^PPE?X)bbjqaK_FWuC!oUBvdG z1;qx%1SuXAe@3BnTDVi7MX+0GG!whf%G0h_2DS%)Vlf;gZ(<;M;hoVrz{NnFRb>H{ zPNdEkzn^d|Rc2YKm+3H4&J3$}8}i|$91dJg;R!n4HpmiKipMOTPa1!AHJSdxH>XyVISB8Eb>K}R0|G1w&Jn`NKVm%-srEQ8kZ)@HmRXoM|;$TX= z;<#i5APL2))Hz&q85L<__GNL$Fuo3?mSJ2qN5_tmP%6tO%QXd_YzF#^heq&KT6S?> zRc=*C67xHxElGvs+6r3LL8>b4W>IKOXsH~4dLunUox^>zZZgTi82mk`@Nx8ft$4Nj z9mJtau0UtO&*IfP=#rtug-r}qAqt*~2VCMAbVdhO3$`21J%0twA4&y$4tTi6_?(Gc1b1{;N8E${9B5a44Q*fmwz}X8{uy ztDQl01P<(>ZesPIqxh36+=yKNl(6yg=K2zTy=S9aVL)3a)2rl4qitDMU$QXaJ|U7= zP`8|Jxx@fU_6p!mX`c8K&C~d#Wlm_mC+LbV56*vIiFOc>_lU+@BlJQ;h{&kO*bw>P zSc~{;N!*|`kNCiznC`fe1cDs#1leLrZhlF5#@!5b#gTz;&_KyoodC|uYs3~XEH%GF z_;Zs?>9E4bOPg!UI(r=Ztt&j+JDAQ@THVI=dHpGqiH`}%omhpn{ft^ztyRHl4eU~& zt~^;T&HJuW6(HF`q(AgT6q?-$wH4;XJwZ!b;#6XLivWd+wEiSfr#K#3P@A=ykK`DR zl`}*7t-iJqL19@)1!8#@EN8rf0AALos(3R32-^U3h&KuvpOie%%4U)u$@nQd9J4gY zi7pAriX9UFtL!I?weYq6}@wL8kz9-`Y9}V6mL{yaIe$a*Qf<&nwHR z%&wN>ZqLWd8n!uUSm^6sBhu+O4T>lDkG4KAg6)S=zX7#?^&toKM63a~Vn~&MlJpDE zKRIuAL^7f1mKvH_kWt7j=I-QeZrJUla#d5) zG&8KgJtcIsr*ouU+KJzKdB`H2S6Gpid8tDh=x+2;G1{1ol%WfaXrF{U33%@b!QxGF zCj(mQAXB?pWmjep&kop^b*0TEkgS*&N!2H!lIeVCmVP<-u=oCuaI9-Yny`W0r2=#z zkIspJW<>s@yQtAaP1-*(XmOaUXPP%29k>Y3MeZ%?n)xA{liqpZ;Eo%1uloDmLBO(_ zqdzD-BQ8cWm@P!TV{f&%!v4rwfdS=c|NC%2-UQab z;Qc6`#78NAZ)Yc0PwyJP`k)4&D@_9Oiwic{={4uFnGts3jv*kT4ZjFN$zbOIH$M-r z66Z3H{HvDCD*rmSQZFk@GkXK~T!2MngjD5M7L>z=DhPTk48b^b$&f1cTAX1*dH~l8 zaFBCdaoj3^GNk4=uS3Q_0q~)h`;-S%2LI1RhH0r;lT}x3Db#(c2?;Suy`q&ERvR^3 zC4g?;)K%M8F#PT(UZC0Wj?RKyFx9 zU;FLKS)g?-+EaG;tBZK=$xTvZXbKzh0&OMm(CwZA|6Krv&hj9DW^eVWTI*`VZ2KEG znc&>)ni_`4znhU>>Z3HZ7JL=0-V9R4s;q2))-e51X8c2F9pIo1ujtWQ5$fUQ{b5X= zDED{nQ|n9@DX%1RFkO%)xR?Ppm%Cl?Hr*XLk!9dESv>$Cf;&Ws3hQhbwgth`21Ry* zdxv`+;j?(2B7c&>TkvwUMGJ4aq$QNBSJM>BPUwi&4~s6NEp&Vu+d&0{4zhpuIz%kD(*uB;ugokRs)yXQO9f3nsnc?HygSzh1+Exq|yWvTwdX$KYrt+o; z@t$HT2yb)7oAmeRL{#{4IaLE77P?D7?l$~F&^h+ifgondMClveOUs-P7B-`0^rZKt z_7Oq2hEj(LO&GQZFw!B#-0%Xg4EB#d^3jUYziRNO{5_8jri1b|C+o{MrikH=jEID) zxQg<1u#9PfeYG?+pWx4zl=W9MdL_HhYKZC6CgR)rNOj^ z>E&)^Ywyag3uy{%ht0*?lT@)~zFw*|Ac8M0?wZ8Dw8G}n;gY_yMNHU>MN5pK!{<3I z?3#~8fK?M%Gnvr%xzR2DLa$CaT(Y$fUK{0f>2XPg~*bB-iL%_>+x<)0>{!ig!KzW zUz_h>V1tY3FpX{FZ|mdeU1neHR%CgNS?$&2TXdm zGGyQQYrki?S6K-lLZD*b$lprg6BUL< z0*H0}q3$%+6qj16RGOZWT@CIpz602c-d8+Q(+<5hse>Hbnoy{NKQTqww7~EHPCx|Q z*05*;z)k~m4AAm?S(PQyY3#ebEV~5gM(~3(4%Ec~=(HF(o>hQJ`zbG`e@}>PA-%Dp zt*N?+ARU*f+Es^?gXEmqO~a3-46Si>?X+fax#jt85g8 zj;=!_uv-1r|KB0ahHt(*_A^3$SHYtzCj4y`%R;Jwmcl#7nqt#Rl}gf6A*_69Sbfv! z0|kQec2NH;i;%Ic{SenUnn##>xMu_~g8kOV$I6lh%VqE(JWgGtA3|I#Rg2|RulxG{gt(dBRMJ{qT}1@3-p9q>SSnA469%w-*@2K4 zD3wt~J!=i{CkE(~gLH*26ByD@>_2f%hB$wN7(WSr61t5MoVDgXs$hMV5r9%3~;E z?1z!>1?*fzEvaKG!taZ90#6 zqbj&35E3qOiByl#(CLz^=f=em(-FeLG4FRJ*okUMORqj-EmFwPC%nYk^?Uc5c z2_qys;eH%&%;~7*WnNTGaBg^7W_orS^mHo%AU+IVT#vty^6fmiVN;Lm{@}eP#5S}@ zypyBL4kjX_BVyU|A(nB;=3pq4<|*Blb;7o+JTWgNH?OoJ^Ij&pBB^7LmJDTDa%akL zVlT0iMSdQg(vd8EE!jgWXwL7*=|cO{kw!jRU1>1D-0h1G1>r3mA_3o`$bB)WiWad~ z78lS+o8__kl$fM(cFRgu(~@cvs^j5tdR1(r!ZnL%Il z{v+edkx|$*&A2hHefK(`ZAusC7w=FQ_M8^}SUD2D!afPXNF@!~79eIdf$yqQ`8Ih* zDb5px%o{a<(j9MwWRxm0FVso3?}Au1`i?*>`_`5_RG4sG+Q5FQ+5?>W2z2kH-pIOtxdz3Lvw5C$o`e|yh=DnHBg!YK}kP%q^i0@EgwjP)K9fpIMMYR=!#oYwp zr(LMXL$bK*Iqm!?RQl^R!VzPUt;gOA6RC|a0Bss*Um~_ zI%1mS!jh~7?NgCWp<#;CWHB?U*00|e?ppQ)IHCSQMIDlr&}m6k36=5XP*)PWe^}@4 z-wr5$Sh+w~U!xQo+%vtnliqy0{$a%fDLn}_ya!jQ{UdXVBlGq%)(ol~FdiWPXYsd1 zS1Q=S*UH;6ESJp-%qwrH6IAwIDq%uSP2bzfm&bIqZgK6pR^lw@=iuw?@9A)pS)~7c zj+=WqyT-RTQ0_R(8?6sVXX25u)V~Gma`D+xyoJEuXrr~XF#L%NUb#^_SK7%R@@%l< zQSlFL&TY2Ep6$I%_Zq#PD-Ef=Oju=PpUc-SGSM|VYjeT_+^2lg!tPW|HwfAZ_0|EK zDf!uwTC_3~XiGlXq1nW~>k+=L@)ma0!!AnvuGap_QOy#jzomz@lLIUyx;WswEkSkT z8)+q_fd%$uPWf_w=BEZG2PcHe^%X_7dQ7_a%9#an1Gs(}esXw4Xz$F(x9xZ2B&W(L zwxhp|pLKW+J2yDDytR6ylD`xSF9RPduL7ovWs7U4Wx1oA?^1V`;dN$~F7m8RW+#Ov zgv;>@qguTtJos|gg29o_FWpaWPk8U#&=2kRd?sUJ(s?xUEl2r&HyV8Wk&+VIrQ0d`}4vl zt&Z(B#a`W=OxLx#QVKz8*U=SXUZO&!CuJw~r6Ji|W~OBiFRIad!mmf}T4-8e zx?h^F-1_jo@qx!3U&~!CTB;uU$(@d_>Zlzl9+5NT`Ds|64G)$J4~*c1-vWJ=M`CzJ zGB5cq*VDqsi4z*=9l{QYf!l&xB8QNy4xqBqN|Mpm{0tjrUtVORzx0i+hNT6hN#AH= z_`ulUd~`}EaQVS#d?0bya`F@U)CObCZ&&*aCM z?&h8j;bB4E!NKrGp=s_?Ux8zHP%-q5!{@Yd5`8hbV(IhAf|dYml7-Kf;mrhobm^HZ zO*vrwo)e1MQDxp4VGn0(zvVvaaOrj^^95U0=L*d(lUgDTtoromIp_0ricJkHH66}) zW`zN{CuF)pSUcF#t}$dgf1@>~rl|m}2u=0M3(qDZ;bZYjFi*0paP8^oXyR86=~ctk z?Pg(C5y@RD@H=Ix z9Ofq%<{uFnb}KbF%`G7eaJ0$yxSrPD&YUnm&!E6y_&_q;62k~FBa6yT1OJ92h1_e> z52PtT`X*Pxi!R`g!Y1b?zZ?2qPP>@x666x-CZ`-#%ded%70T%p_^Mi*f)2Lufbfv; z*p%ROI9Ud~$pfy3jgK2A%-16@0Nx}d!xi3SmA&xik?%)+O3--``V!K@X91Ws4K5?+ z70!RmFoMhep@I%Hd6?0)bq*Mu@PK57H#ev`pDEcf-6mdU2hDB|EXAAX1%Vws*V>N> zPxi?nE0H#}*a%qP?iy!B@~a$1Xa?SN9`1*nS}u}CY+@2|1|kNrK`wU@pT0^FUqou)G(zX8AVC(aklC9IN(!q+ql-!`-ZGJE|JVD@1vjD_^(LXo*{Zy}M8=q;> zc0zCp`U5TgYy8isU3lw{@5jGuY5cpSo*oyyq)35^v&?6`p4e)NXgIi&Nj?_BP^IBpS}X^{vHaDyypvozE&g)J#$}!>TbNC z_&Wv0GRFCs8&Mwq$Kpcd9d9HSDj@|rK6{-C)_2jcvJu!aJG&}x=R?2ykIB@4;ecv^ z(O4}YtBr17?k6z6n(Fryn3p^8+n8Q8Mop$=1bMg;Wu_PU<@s@g@ofpRSI^@0s zGsdVf)HZ_Ko}N2`dHRT_gMDx_Sj&-DE6>q!-OV&jl$|PzH=#tR9iP`?A-@?=0i0K` z14Fv~>%YTBRBm`~SZ)Ybieb(oB{~q212!=X$C1rHGN)lBva+14jtKQJV;#pRF`OO(`6r0U8PO<8iU0ot61tWwdH^aB4p z|IDC~I;Ly;#Zmn>;t62!B7~@W6n8&9cN~}NqVh}F3kVoq=b#dg^b|Do7$kGeD{M&R zh4DgpY`oIdj37nurbyZXnWwHyl7bE0X)6aG9|KXV>%6tm)PK&}7+MnBa- zRKkpI@E!66@1*EA6#mo|2ZEU>tf11p6WGW&4TTn?ZH>eUE6UHr7J69-fSvaiMbvrr zdo|1H`Igv~nq)Z2VMTm!`WJBlR3Z=JEwrlkx*@11Nhrw%iet>a8eruGqz9M6ENKF^ zga}1jC=W0s+N^#7nAt&@k=eP~d0F{&-VJWW-d5^{Cfd%~VR?SgV6^W3PiR8}3bdVO zY^G(DLMIh`%Ja|A$wL&jSJ#5>`qPu>G+HCVYtYs|M4P|4cz}-WSMEStf2K*A#lY!B zE5^`Xq47u+ze$L;!`FW6^wUX{F`+;5WVA-0H$y(%1`Bm#s>rI)Ak|^CoO!*@cTgIQ z;y)o8jyNg^>s?P{r}#iLE+?UzJhY*iczbbF;Ujg1I9r(tFtI!1vXo=%DT%1{>h*4v zGw?39FELGbmc#$T+a_O#qYloXZF^XiZD6wuL9xcyDNih8x>@6VZgd$t6E;mnmaUx) z!pa%qJ6gCwkg2~#AkQ~HAlKQNSrk;_Ti|DLPTyEl8WXtVBc%twqKz#m#M+$k6)mke ztvIEGfT$m*Zs;WWKW@mQrmm6Fp{8>KIzp!m;4J54ov+cEb_Cxqc_H3( zfAKowx%#1vI&W04?4`eHp++_rc3W50Fg>dDJFnFxmd?A)ONSB;P+bN0Ce7#u4<&Ic^{&u}MTZJJV5Glt|*nW^fey1vt`Ty}} zFc0Zpy?g&xXkr9Uz#HKRc+ax{lKFq$YI<^RJAYXcG=Sh{feY>k-a3!ABK{=i&!JfKKr( zqTm!dG1)RuAjV-xk%n{iWXT!f^K`T<3;u~6U}71L>;trX2rr+YqvW-k zc%=ba2^})R6k7V-bHV^n#ztpa1ak$5BFKC5 z=#z7-v%G-2iaGZIi8z*D{@1^0k4Ix}a(viYaU6M%n^L2S4e%DXk?kbyt%Fz$jn5*p z5i}`4+eY*qw425}=A#;afDe+T9a*QrvIqVs7MgGJ29PM;|+Sb64%3#po`wc1?kky2(@ ztCP?f60-9+n8G|DSdy*#WUxru$QG#>o%*C>3TT1EC*@{z5!3 zT83t^iwF|_Tj&8S^y#}WmIhE?)nxFebTk4qyS;ITVwDwvkSw!RofgLW1pw4(PWU{C zH&^CG6!~*O8m+O6^YUKLpJmX2g>~tg$?R-!x6dxVk(XHk$e+Bz#8j?Qc3OI7Nr^ z7M%bn-$#&pIYvu(6!+}=b&|;#$RnK+T>W>TMUcweH1qS=ps-~;wEy=B%^&tp*@|8b zlO4n2mC_hPHyYkcGsG*Y;A5UgZS|qMGKd@5B&>T?03LZwg=ouls>Gy7Bh{k6kQvqC zyAH>tyU}3dbo@k*782iWAEck!xJBTVZatfK8>B>1)YKrVz7lNfVFegIs{F* z2haYFcr>VV#ZfuVxi(Vr$j*!C_eoV2r#^rm2v&U!so>MF65@PIS#bj}rU~LtNLeI0 z;)_@w)0Tn^{ZASzp5E^HsRFFE@o~Cl3Oh?0@lgmdV{JZ!S`W58;EMyJO?S$aCrD`11@5Rbdo;aW{01d|N116ThM= z%&RYD+3_Jy(&#ek)&cr~cq-V!LBq?cGlW^uTr*lRK;XY5Y>IrYs$hJQK`ItXt|1t( z0rmt5D@v03SG|9Y7f>aIpNSu!X8?V#sITs*Y=waaSlDT~*+3$QCW0J+OOIQgMxgq5D#+5|qL)o4TRPC;ihcsdh~I+L1M!D2|2}$-o$zzoIdD;steWdW&n*_j z|DsDLV2OT);9_5hE=mJAA9VQoLhS%>dG!>2C4P>8@<}hRt86Q8QYmK!T082vFHK;u zh+XZ*cdwVz^eb_wyqXPhb_EGBOklq64*z!VLf`$dlGc)?IRIZQcKYee3@O$O2dPaJPTCiKYh~xB7 z935m;7jM(ai2gYuLt2|4B8<+Dk+p46+^3D;9|-qhVp#k;GLeKo&o-}=&9hL zG&QvFdhXW|-?8y=-;uv>ll^zaief0;${jhyLK{~;L~YQ zB)_bEyErEkmq1DSIz#_=3U8-t-#l)stC)SgG&m!oq%o^I6?)dc)TBk< zx)l|x808li@1>L;0v^n4qOl~No2SG}PtGe_hoIzT3>XEZXmEgqXBGissXQM!^r(BZ zXOu7cUt|4wR616F!JjbJ@0P|21N(o63qz$#hf2EHO4KF0Daoc$K+dgKr@zf(MR~== zyDFuH04Re^G?yi$@|02%Wb?Vnx#0fwlrB*g@1~aKM<-dIOXo=Y0|d3Y08S~z>qlv5 z{d!6lUeNKJ7^yH+M7-W_`vUV*dLB%q@ew#v$qu>HNWoBVW}k{|1v8UQqn_hm&yjYW z1L%QRo|3r2wp5qNK{a$PCo3>1tSAMjBxwZp67i8Eo!q>V1MNz&^!m!|wj?1z#xGO- zRgY$v6*{4qIS&t=MEH7f-hIvD*u}+L1H&o<{AmQS&GDsNq7?jTz|=$@SUm(sB7@^7 zv}3SXu&wD}OY`!e#NXwzN3E3hNhOW+CdXlh88O^F(7-q zW}>6JU5@-$o9fjD_DxyIegpJ7&DGD|*FMOuz_P$Li-2aT{getlELJwu0Mvi}HxqbD z0y@5r*HCB;lA&K$^L14QIrN^ze<^(223EcTZuj@yUSBe_Jckm4Yw3DSyz)ZcziVaJg&gNxfK$dd(hLgP+_KH|_+4e)ZKecsujdEQGwJ^Nb&T0Qee7V z;0Se;bbmku57fs&feAt??YdWjy}p;7o1K^Eus^e{wQH(*%+!SevJV&DSoKWvP=g$K zJ>^<^2n5u~K>RVy+0V|`DabtEjAxMvO~aC9l*DvSyh&_^AAke_I{XC;+jwXi1`l!t z+6bfM9hV_qLtk}f0_9FU69T$XS9pJRiSQ>o+cW1pg_cj>5VcH^uPep2Nb}ty>uf>b zGet5oIqy&CjhQSgeGSXQWKjBZ36+~SBHVos#|6tfMkfIb9DEJcxm554m#^#$#~qo? zf~r?iX~9qN)R5NLSEc$>0C^tKer^EvXj6-;6zCod(#aA1jn$}2u<2O=b@8R*KP1{o zKS#6-Ae8IPS@?+L1~ud|ceTc;o6mHq)@e1WB*dRiVf$wQBP0Lih(htv`^a=w@SWvA24#`BqxJ)XDShKL{iscyX<Y`ud+(Nw0b>lf+p;8Ax%b|Ck$a~FLI{M^B_tu?N_z$SJ7bdU?(;mK_x%G) zpTU;yIWroad(O8^Es}B(mk!c%K4f=p*qKw`RrV-*gc`g_3k=hb#9NN0b7I>g?}iRg zUq;jUM)?}amZL&WRJ+d|pFZkI6kVy)urJGOynqwfY4gRUAqx8e_+}rrd`wCS#Y=d( z4(1%*%?}4Z=CIs?oZ{>QkYW#+GhdiqAIB=tRimSSJEe9 z4=Z66V*>b*3SjSj-SL{s6*o$Q$ec4xj4}^96M0d&H#VtI=^o*|_k#XT>x^g+WQKhK z=VOhZJG7HRg)5km?_$8hDfVwyNhrBh z!L0f3eMB-$zKNTUV9D3_1HTNGtP`t%I7F;Ntjkq*STcW331`atX!g@61u1u{ocnI; z&V{YJ9h8;wl{0lxuHzl43i)QIRKQ(wg=H=Iw)d%|suOMMGafMRL#mMC4bxvw7z_qX zXAJpw|2ApVKPxfqiJ8qPaqnRNo}$KGew&3pePcmO)wjyk38W6a0d;r-cjgVa(=}3c z=UFKS1YIlWbbfXwdVeM+EjsHOU&EFoQh7y2ssQj+NlT$`C7Domv!rlY`wxA%#u(A( zNUu2et1hZ1iG=cm>f7aSzhoKQzGik=`|?F)w3zVMx7OKZ0uB;EDdF-|){BYu&-3S~ zz2rrI-|#3^W?`uSDoubnGiQMoIwmFPYo?dAqpjd#Bm$Vfwctl(fhf*MePb*1xJ*go z^oUN?)M_{r^_f{u87{GZ3NM+`ybh}W(O$5Ph`0`*^w*wbN}&?$%@Pp1Zc)-PvvX=O!XB|3!hIl2p7)(4PHnvX)XX5z`{N}YFtjnoQXWdGiQLKOiFh` zO0yuP)Y}@B|B=(TPgMR)DQmF;>XWX40?04U^Qwoc29ea1)N#9+I@m`W7sE?NAB;9* zJiwSt@3hFA&orC~md5`|H&8d`(;W)wj%3n(9n$T|T=@TyZWh!(pY$JP(p?DYMv8sp zl8b{s^gbP|?KSO1s|SrFhy!&uOlTB(ea3PnhW%*q7p6VK5|jSG3HI+f@6x*R4N!T% z?Zw|dm3{(ErB8^{s2#4+v1;ijE)z^r7a1tXPe@M!+rlT#0#hRTYHON6I?apJ z&IpvKrgkySh^!m8(^g#1xLz2SNySD6#zjV2Fzyv`iQ*OCdXY)+{+|rKNdY}A;&xGd zVcxQSv|xZ-a_GKM|2@!2m~0*I>Kgjxk7>XZ+I9yjEOzA2QUlp>I@2@LA;F8n$Ms~Y zLtx$}D9EZ3RXPAMg42)F9v(e_6O5$Wh$xRh+i=eUFdqpgT2iixLu*5dm5<4xvfFlw z0kMWai+=klyq&~Q0UR@if>E_xjH{9VX&h^a zVvmr3P<_CCFM!47@wgv~SqDDXm)z+e@3_-8*U>Ba2P}RId^du^=-kP1w0DLb`JME- z4BCG|u(aSyA?}op3jcOYL2wPMs}FrdX9TAEqySNXZ-iD8R*`&fzDu&VGCrd(+b+RI zL}r1YB(vBwor4bb;}qg@VQ6KLn8G3~rEweb*2b|$DE25G(U+Z);GsL{c0ay7jzUly zDL;>ofc5WvwCyMpqYvO>_Z~G+!9EDC%5BHykIlicV+Jbnol-}1oV8?p2zGR52aph8 zo`B%}=X8MN<+wIF!0u3xjd1BKmRuZ5S2@<03!M7PIH47`9S(p^IU=C(30$Ltd`Nx0 zZKiv?G9z+)Q$wFuz8D|70}B4rNRu2p_TtR9E;4P_F7cL5y+I0vj^IXg0&K_R12iIt zaPvSbUppVMW2uXnFHFx$&jx7$MVEDYJB}Dhomm3DATv`~;Z^OD?|I(H&_>&nAC>Qy z5`ol4Uo+BP?)Dj+$T+2yjQL1@GN^d=DWL5KXna9S51RdJ$I;rip(;OvSIwxxTD0<^ z{8{8D~$xr`eQ+4rl%m z=4JA2CV3 zfSPI=+A9D6@;7LN2VU^81Ljn|kI2@MQ|MprneTJK$k3MS;=*EgciU$`U|UuG2n-zs zp@pmZydym5*zdxC1cc`DaJKR`@Hy+vH1hrIPY8Z?_m}OX#bx!RUa{cT3Ieyt& zrW`@X^~3pPT$Yp2Nde>X%{bUi5R&bd8i33zkn)4aX!)0bwqJDs8M&rpWfuzs*tP&~ zczgma&#dCuB@dO1ws-U>W9tcYk?cGF`NWaQ6sr z3Lkq&duUmoK4(EC^vCzMEz2XJY?E5=kXsX=+d6fa<=8jdIoUbWi{1w*O36HZKw)}j z6df9Q1c2cRrg?ej6tJt&-xO!ip%bJXx=3gGWqPFhP+L?o#2E#t1v(;*%R$RShQ?Il zX#8-`vO%c`MS$E~SgBWoSGm47N9dRBo&jGVMYx5)tFPem2k|OERF(RtM|ONIMpPPL zd?NW_iI;@}fl!bI7^2Ze>Fz$PJBxO9;+>Q8KWEU6az6@&V`j7hn3;w zvW>LV1e0@{b>Q%s<^`Ai&z4$|S92S6$FDpaO9IwBM9Z%tPfQ>B+$>7yK z%|;8-kV3BS{CU(SBk}`EL-JvkAZNWhXHinqzfRA1{7BC-_F}ES&d-uL3Ai{~3_R^a z@Ack=nrGAOE0E|M8ka8|7&DD(^m2jhG5#c5Q=Y8yg#ePNpxmM1ce9s~$k;es7Bm2|d5JUu8If{Ut z4N+Wr0#T&Divq7gSKlox<E3SlpP>n2_3P;fTI8Cn5R<(}1HN>|6CnZQo8t^Z5}{+fSmOO`-9y4s@Z{^?*$6BM^ot8=&!Z{1iFM zEs2mTS3|>0<8B(Og1oUS*m2))yO9CJ<+POCre(5X@{Qusw+knMoMa8cYHV+RuU2hT0$WA7DO)_897_fSNEg4-IuxpjEXU4jD0rKzM1Zk&X(x zc{l7*peR15$55rBhG?`y&r$_Pp?z=+sLXIo0>|_%RpiHHAH3^bZrZmdBP7cqF|@d< zXufv|*RCO1G%_z`yX44V8(OS4qz_-g{b-@YU~n4#{mMvOOn*7v2#}I8SaY@_nhAhYsJ!U;Z$sd-XL<6uEmbiKmyQKK-$PxJ^-o`lL}Du9zQ`M8wArD3Pzoa z6jnpi1j`iJ8zy*Qh(v5xK}QtPB6zxplv{)2D_Xc3WCj;v8O`SIQUJ2b3Pivhzn_LD z3*oB`mrB(ivCa(sA~769#2_>@^(C_s1IaJ*!OcwAj{>w3!h3)0(nN@m-S|R5M$7!@ zH^DKaesn6cZW66r%3heJfZktyNCg-%dujH29A>3j$4K_jCLZ^6Gi&)t74}*sg;&TG z!g8G@*&kn3KzaZ{!g}%?n!Q`3fYxEc9P|)@nFLQ4l5%J1dWdn&DHQ}XN}6rqq`=Uf z5D9MwVB(d~-vmL9eCZ12^c}@pK#4erX+4)z!~6*(w7VE-*Wz&it^+q?K0HPl)o_66 z*h*&p2yo-X-TxV&;+O)uBdezWSbx8n2D5%Ayzz$T@;?H;H-C!We1yuS>ev;es^2^Q zXE6Hod={|$-3fUYlLm7_)-fmKub9{de^;1~>yMb&i+3sfXIxd%PiZ^6{uEw&1e8%l z)(L+Jd{0lvEXe}UkQ0pppKa&p=umI_@Q@y2Urmq9Yyng(G--FwT%YNfFq`X{2^IxU zvwt5!JaDAHfHxk-8}_W{&ztmu%u+oA@b8@gV;GRYoq}6*s1XgU%I{0z8=hc)KLSJM z-$%69=>38Nk@XqeaRw}Q&zxMXv&BsS;K0=2A8TICO+5q|&N<{vj(+yjv41$EiE@jC z8JmjN59v-(D0UQs{7c@D+zai8*P2Fu!;6p*lJWi!et(X6b@O&~70icThH}86mNy^Z~ro0==(EXKiyW3&)f&zHdTETV=~oCA^gJI5H#ajH`7;*6lDN z=&*QT2}@EK!b@R0J9rDcFgESMo3^dX2+qC4xb1+AwX>BE3IUft(KgUJ z(AwA5*70M<;QY5FS}<-k{lySkH1hqN(G;_DIEYGV=~hGeRz_K8{t;!t6Jt1V5^!#B zrJd+4iG$_a=&#(L*>|~(HgOC~H`MHg%Gl*5ZSJ(eaTjAv2=KG7bAPHC0u{Xv>T#rO z1*4z*C5*vO4I+nmRoJrzy2szI`|_T*a&*+$Kc7rjq6n831|o` zO-_#AA9&s0G4RVgJNnsh(`3QF6f}4W@*h-Yl7@xTJ>d9iA&mw>M|5KizCoNqfco>4 z(#VsP9|c2fwrw#|RRcA&AE4k~sdiXJ7rx)%wv_|@3-(hD*dKV)C*qV2;|^`+gL_2% zY8@WiKDN^+eQn8wFY{u6vZ;L9XlcNshF2jUw$=kE#`F@@f#;U(QU!ZNCUi6rb`U5ib?AUFjZq$Es zn*HHI*)pk|7FlGTN&rXJg(v#6uGI#Q0U%qx41uA7-8{oA_z2z)YhRZUQ|yx(Ui~2N z2mX)1mKnN_PBKg1kO1mvj2kYq@{7#MSqAt5ESw7q=hhMIC+ll*1C121$66IAwGP9& zKgNmEC=qeSF?3WvT!2*3+Ho~9uOojVw_h7Pkd3|f`SWKukv zGXq}A522=cuk(3Vr=+9%PRA@rnjM(}e;3z@@xhmaBQvfe?8wj21W20d%RYt*&1<#% zGTuw0m)r46LJLe)u_+*&O-cDq>`te9W;&(1Q`k~pwm|~I44vPe%8GIex)AKFd_*Ox zET}0IpcL0W-zW{`o?gv@8nX|7HfnH29sZHX1D~LOIl6{G7K*P%g#aW|jOF)q)jh`YTW3uA1@yVVJIZbZdN{75=SJB-NOc(Bdn3j^>ba^2 zkzgM+n+^6+Df6RO@L}F@?MkGxHzPU;_E8{E3ieSugPGCmE<3Y97fx+|$+sY3032vq ze4#{_?>L;t39odO_%u>zqdr}u+q^f|Qc}wCYdinQVTkI7sa()mr{f-0UBR5<#)`4x z0ZP938l7)mbTY%H6Oebc9#cNR3{!>X2iQy%^@#^_CFwvmiOQu7{Z{+$i~uTt|4kT% znN;1b<(jitIB5jAa~%^{HOW<}C24TC@1XIAB>o@_Z^PR$OO-#*1^RBL{Y&weoyTVI zf}*o+9USjE3=PBc5-O?XW>W(QU%~v=q-U`2S-xM+ok8>-1BRZxKly45nf8AL@bnzn z6WK!=FLA>G0a))(*t2mm3duohYA6tRnWlp+oQ*EHb~+i`jaX`38S6s3ArW7nze7X?vb-}H#;oZ7;wk`nuB=SGiRm)82KM`VQYy}&JD zkq9j+`Js(^dj6FwQVLEMiNJkwb;id0{neAreSOul*5_C_fdKfmFkR%E#K|HAzW_eJ zH!JHd=-J)|J=+w@?D~}$AOFv8gf4f!aw~hq*CcX)7Ne`pMJEe)ixTT9*Ln(P%07WR zx=GJa@3Z`X?AbxYg6+?`>8~c{GyPop58@Trv#KmtNOXPxjK1Lu*{I`u$+io>i2Cl z$|W4FEFBEpOp4Bx*+Qc?Shfx`;)rs3Ii+ufsgTV|h@yFky;62;2y0r`8=LA-v~dJrPF z19D(RQ7yC6@0^hxBrB#$=JIDLo*9joPd$)^9-0OI0sCJrxb$<4g8=@16fcjb_u@6W zXw6D|?Tqw`QJnodS}P%|Z5y2${nCVa1w#IbvXetrP%iHr!N;fM)j%s;+>aLz95=*= z2TmH|rABD!DK4qU(!?4%Xf1K^^ z256$u;ZtB`1VRsoHR17p2CF{tY(Cibze4qsR?8;nj3B;W8Ytiu|2rIfpUjtnfA~{2!GdqhYtX9@PhsCFz_g-xIP3^CEb2>v$v;pqHnBQ0@~S|XOP>( z%v{O@vgl-Yb<2`r`_$mkX_N2KTEp=vHm|cTq)Y&c7VhqKF~9?)-XcKZ&bwIOpOfX6 z<<1A`_)TU=ppS#iQ1EHoVl0DpsbmbrbmJHaD(Z%TT2@|8QFbvUmj6NrID5LddPJ5* zSH}RJ(z6#_jGo{i;%akjTU-~VMZ{a&woI_1@ZQgf`lOnavJ~rP&Zz+WLCBh@#E7K8 zTj9;O+7sH6C{4W21noOWp1EuobN*_I0SE2MRa%FCV>#Tgy_ph{!a(Z!Ync585YUAc z%dAuoTNu}iKMqxzMn}k01HQS?ieh*DeBQ~--PD&Knd_4h(UR9v)KpICR<$?|EboiY zElmABG$9}j=$_iwE(ZiGcXBIe_gc|XSJzzHPQ6(t`veR)*6+gVn>PV?_fk@5Sw(4G z30&QiGqPi3^+fqhAruJ*hVBe}E$Qr==@{=Ao1bd_KIJ(5V7PiJVHA;2VVBFcf{SIe zYp`{Q6J@C)EX^#-DAN>i?AK_oJEl#sRSjUz;gc3A5{a@!Alz9X{k#un%^>>@Y!CW1 z@N*2^=m#`U>vJXi5!`&V@yT%#orLealVb?zKB0;y@JZNde^tI3Yts0KDO3rylR9*u z)DelTLE7|M2qSH3c0=c8YH*4Vpu#|jFcrjN(o(W&mcfQ39rO+f{?ap?KlD9 z6hJuO!JU&0f;BTE<kKn-ZJv*78sb{?@3`K^u|mym+|&Uc`r9eigU%Z%r7KF(lSTg> z(Rb2`d5y2y@)kzpYj_qsnC875K!gb$?e6Mq?{4jwYMTJnQD}NkoEz^S4;lPf@_Lf} z_P$gPeL-7DtvX2%8;4Geu=#h*mEdhSMgOe1mO#6w=+~NR1XxPz(7$Uj|1m-Tti}At z1pQh|jnIc8?bO;ygHu=o*GNZE4L&{zGm$sjX%}A;cPoD%aX?9E8HEZ!!75)vK1JLt ziZ94tc1PON{YDYE1M8x&>d`f-{OBUz)QIl6f^XBGQjftP`Lig2?$b1+1orYm<=B3F z++PXpdrpGD*snX#`b*#d{6OJHNoN%c`)9v{1fFVkmW1cA942 zKF{6Y`+qNfo?-Wc3dGPo9{6Vrd!*KTVBtk+NT>Z;#?|~dAztIeL3@6Hg)e@!3#na7 z5eoA&`KS3t;&%&gD0#PV=L`Pc`Tj+JE?k2L3upgqaH~s68r&fd?wDV_p348V`1_#l zW)~0~%B+4*YIvqsLMU}%e)U_J)gSuOkh`Xv)!5tyn{ZJ3Vvl-FmUY$4baXdOJ3!u% z25L^E4P`q9Ncj#ah*$2kIH6+xHI3QO5@M71c_S^|ft}?$h%+EX z@<{MRGGaWDwlkhcr+%h!oYV)!k@DBl3a$#5RoE(S&+VYhWy=R}`?C`}aS2&4qO|0| z7Y1CF?_p3pix!M}OjULv&EASx*D-eFYtTo#3C_QV!D3HCnh7>8X90>~{+tHOpc=H4 z#PWJts)?QCn#37w#F9$5=bc!Z0Ls28y$oK{Z&=dGcg}5_cdg~mJFApdmo_q-zus9; zg(?{7+;(LxW=rDV9pozj!-7y_JzuqeAayUIS5gH-!*E(1roVIlQ4y@4h4fNb^;UgE{HkE!u6!9&5#SHw55198OmYCL^ z2cehs38>+ZfD8%TBcmC&^Gp5Dd%qeS?SC+EzI7D*udSH^@{6&+$uLkAn>#*)~%ZVW#_sDwI znNyBzVG2n(tB=#<EPot|7kO-Sfh;RU{Y=UKo(GeP90iY!XSeC=riW%ZCWdA~ z#UliyQ!AudjbxX@8BGOqs!TB$e?gvs&hejlj#v&3HvcQnAUp$kPX09+_3|UK{Zb-@ zrTj{MO~&@@jpf@ZhrJf3PMCoMmRCxoq`h^Zda6vjUcdW%iZHj3pJC8uHSTf;#2oX{ zs!Z?>V!z1a^^IX)$aV5S(x6rfClv2-$Gc3i!p*y!cZv2QQgV`WlX55n5V`#dtM4Z8 z2FRl$UayDOA7{KO?Gl6Y%kye-pc~sh0fRE}XwJ9@w7M1=D7?s~*Ka)Yqm~2(laovF z4apM#tG*h2yGiBK7vSjP*!xRw_i%gnZ}Xlvh#t0{6pdf+LW!2(e-UeFbhiU2JHS)y->XQ1>>}5_!X%)EGefFEYPzCS&jefmKjnG9qXzVDJAV*SO z|E7!1)ccW-=p6KcNcq~J61zJA+qo%0Go&WBvY@!2539U_Hp%Mqsae^u{W_kdQ)$uC zQ6Q;qtun3SIC&V_ob&K5@h=YnwD39PM6i1lp|9=JsHm@HKTTfh{=69n-OkiXG4ov<34lL`B!1nzn_nr^z_f;$ZGq5)r z)y$~tFH0=giB=UgFf5BsM${Ep7K}!d^f}CHojiyFqOLW#>|E+%AB-w3J#1>O#^T2+o4(M<;lpa>w4x5P@%;qE^%P!)-0|sDl6^7 z+}}P$t1ochBUmRzy5(n^Itm7=nk%U#mfe#b8Pfu^9emd3Y%WOAF7>+GbnJw*0NK22Gi%i_SApdt!gxk*QQ`dSBj z*M@LJ;@rw?PzD`AXFw*KrC^JyVI~@_&`S2%3}5Q^OwAH9j+*baVfMHCUVwIv?oRx{ zI{c9lKTP123^H=?h66RIc!TA1r5!Y{>_Kgm=%{OIEQSAh@{t1G_Qrrl z4^QET^1CebP@4qz!}n-BJynbzde5K*3|;w2W{GwoxYt({1{vl~7;+kkm2 z@g2)edL;oN51%3S3N6TNDN~|ULy00kwU^Fzhs=7_i#Rdjz}i5_?6r70$}_+^)T=6# zBM@g-F{;v)Fd;gNx9-DRwyS5%mogQ#6)lXl!|)7@P|L=Ort+Yasi~unV+t?dgqIto zhh@1Yh7FetSM@hh=TK)iZX(-jnj33c9`8k;>_v(eNm=|Nz5o*qz@$F4TRUzWLtlgF_tyX;=F#}hSQ+}-V*SVXhx7`9HXuidp#Y8}O>rnUQ%uY2iSU`)%g)$IlL+ zr3a9rWnvb;EJJ`7*5PG$58cTv;Vdy6L)>BX!3apr>cgaXA6z>C1se|^`+Da2IQxeu zNF=+@rIxaigUc(*YD;1F-!ls$uMIQR_loaB>GkB-e4731lTYyn`mbP$XLRbULwcmZ zHz};Cy1BOs0EfrNfV-6Ep6`?vRM*0RR}9b6nF`3E(qDr*z!M34>wWX&70LeA8owu( z(BMSpj)`45J?A(*b>;mzF!G^BfAT`>mxF`Ht*q42RDnYr-XG^3Px<)9-}HxK4rL8v z5K;pPplENNPjuiw04J+Dw@=t{{>^fhd`<^7-~mE2+oI9+>x81)CAUf_)FYjvVI`~a z-}HVbU5}d|H**BnUcaR9hH~Kc^ig=%IJ{i>*@jaBm*!4RNWsZ(oS>bvct}r$i5SMp zRlJsw=DOyZmYUYu=GvyY{uyW#z#UB)Y&)taEZbpoJkV}Ab-CAMoE`Z>LmH0q=nK-N zEu@@8hrmP?3}D#_BRn~Q6KN~y(qd~FF9#jT6#4) za@q?=>f7q6M{)F^>!eMsOJyU+u1vE|2Y{PV=muBk1q-d;LW9&WWY{YdGJJ^;`G#i~tK%Y|5+%{}W@CpK}gJE7AC|?Yv$YdSr zbeu+Zq+AhyOg}`O2goHhEu+mXEsZTr?SmaZ{dF--)lC3h$YiVm+GziPQCcemz6;C{ zx89fU+VSrBn~!9FvD`S7xQl0))Exmh{c z)OHfDy^LLs;_a$}E2Y8wNa{Y?%jk=4S%#JXPcq!d#cp3<1`kB!!+$Gk=oLJqo}D60 zQoZ|BGY4T$nVlDZ3uq9+IKg!x6&v9j7anmAXb>TBNqH*()zy!E;fl$v61=-&E-_ck zIDm%J0@A%x{V42gDdo505NIC8#a~ylJ?Tb>;PP#jH1$CS&kgcf4TV$;_xi&>S8asQNtnXP|-+ zSMY+%*1QNR=1NQ)Z+WC={4HQJhbH>O2Uo;Z#EEX@#`6<1GPCoN2b0m}0v6UIZspw+ z$HO)k8g;G%c<*CG5?3$t!}8pc!jy%Df)ajN=Bk_xrTZKXTb$9f*83mzbYHovE@Ma`+sy?8Qt~ei&Y_q-hLaB>0lF;gnI$G)@RscgL&E)=;Pu ztCQ&k{L=K2!zr8?Po6`h8}%vZrM$lnEDEJp9mZu)6FO zWj}_lLxNW_7-s6mUW@S!J#!-h1YT}tt$0GRYtc?99>F2=C?OJti`>NCLS?)L6whk? z8ocT~EbYu3%{A_OU_lBRX5btG?liT6LLIlob2oz zY>KRl>;RD;C%;R(TiH5Tx}GhwulBA@O5{``W%}ZY?T5gGA(NLAkQ&;a+n8TdP~cGM z)abIuX07L1urK68HG>vqqlM+)by1Hl{OX971SV&RiujpWk-xF{aP>rM&rs#v3wdfR z>_lYc3Uf1oY)eaTK?xGji!49^AGC`<9+e&#TZF!o0bOEB+K#49<1~O#rIBb=DlHBx z_W$D~xaUB=lW{OD4ux|NTwDU@z&#I+VS_0+FU3KbVMTC^@1EtGPN5_}S{z;)AP5te z1Bl2CptZo$0QYrsy`9E;9bvE=kj{J9@ZIRZ1cf*}`r*Zwr@L%nxNKIxX7~^&(O;J? z=7I_p($J!-gX;b3e9yx`*3u^plA9deUf5mHUfbo;=2GfiQO9u;Ta{Rr)K}Himeg4a zIbIHq4t6d!;){89z|Xk`)E&=@mmDs*o~^L2^{%5j&?fr);f?$7D&x$kY)EZaUQ0n; zQGso>eT&o1^Q+uehNg#QdL{*<_i~X^)sOwuqjSGpLLV}zEr!$*nH!1^*N(JzN-FMN zfVrnLQBas$lvNDv_>kV_p|2pTfLEZ#P|3vOu|?pD)PBv02$!vT!JWBx@AZW3Ufh7*gsZ2x88F$TiPX2;?ikoq)2>YBJ!MsUubo%!m|u zCWN3P#UnkrzryX&gwPcFfpzIK=(dRqZrsjTab0k|=(>oy7Umfj9K7#F@baM8r2G}V zXx+PO;s{(5-Hvo!K%-BC_qj`EE*9SDQOxP+EbgxEXy|llaVhbF)8Q_@SZY~XS5;kC zT+f`2OU@3LTrL(^iERNiAdvUdE@#hMn>d+Q*w%X02{Jh)fC9X@fAcP^W|$exoRIGP zmZJKS0{be5CYSA2D?Qc1(nAHFFo$MkBeLSBUh0c;zdN9h15-po=7f-$>x&Q8%ykZq z)qF93tp^gKJV72mk6Lng{0!P{IMChP*Vxz8(?2shGY@>7?Rzn*Gojo4aAsvAI zEV!-!UmDjZ;-=&0C`}^%c>KwmrzmU$yR!9$;_*3Y&_t7tn)d{>EZKeO~K#8;mx)Ga?;m!MC*O&uy z@uI4tdT`f~_fCT-ddYYpW6-?>Y!Rg&^)?KfLaP@x#(@)~Gwh!jstB`}xg|HE{cP*WB(bgb{Lj$ahVzACCjh zKWqlYki)xw+x4wAI3xnkX;=A%Nj<>%2M_O2DJn0nEr7nkWz;}d`PO<=dstmCcQWzk zM>4{C0|kB6ZS9Q?wGLwUu10WDv#B{(+tgnkjdLX1UdLPd$@-P0!2X?c8(lTcy_Qb^sJ4Mn{i+U zDApy1A>|&l6j;M(hy7b4mLRc*XdiTTx+Q^TY37TXltdiPFC+SO$x-Le(d|sYU3U8ozrnM0Sh)&82 z=~>r%_D!CXV0m1!uSNqlL2BqNz7A4Ro1RsE-Z0Qz<9GF+3;n z0qG+etB-$2vdNF69$O}nIxhT1V;f=iMw348wEEKSAbvz%Kx+6`*}v5dHBtB*SWn_F7QYUdzr<-t!M*j=&ELO9L!w! zzKLNEieA@0sxfWmKu;X&030*jm+zV8O|i*XoQJoJ{zH0g!#Kd4N(o+ZP<1G5M(AkT z25;8HTMn*C56ren3@WNBY!E}I4)7Ck4qCTDtP297e8&iqe&*;K`TrhqS;n{lv}l!(7gHtS=+!Li6GiDOQpkPGpnk zlt&l!{bjn*sQEyaIp{bBbX~ZA5nyN@F|@zYrK1fh508X5PmWCUyEXsj&l6qj-QB|fc=MD9&?Ew} z;}tSZXm5d2v-*yW0C8|g4k@W9t}B8j$M`fL-z&ySrt{%Obx;BudK<~ukodk;b62yE zxVLWv!1U{;tc>*774g5PdhO zK2+Guu@xK<7$*ZiUPEefi?$9w>}$cxcjk1Z%w=}Mt?1lpdN$%m-;U7C7LJ8rQ}&58 z3gpg#01w`!+<}L{G7Fx9&S4nI-ngD?*#j$E`Wu5MOnN>46Shh0>S$((#xRKMZoBj^ zg^fG~zDkExvvp&8OHD&_V{J=oJ2TMwdbD^BDb69*y{pCP6Ko~$DV$&|ZPiAE_#UIW|M@)jJ`iptt?+1+CrSYU|+ z=iy3_+tMi(C?HwM2>YE!ey4Gng^b&Y+kpgQu|NZPv|ff7A<)zXGH{r`iBRBL;^U9M%2*!=uBQF4_jU32t(gJ=)U_KoGtp-*N{%%L^+5+>LSqd!C) z(qjF8&f4J%i2ob{lGJ$Bn(mOmKlE zDmX`C8+iuJ_FBLbCW>#NJ&BYFjy#VdPva&FnK~0M3Tee5pjD#411$qC#4BLpZLjpM zGH$gfQ+_@luObnxqGKHp;TjWmO_^ChVq}`ftuP8r{Y2M$;Mf=?c8a2cSX7*s16<;L zggipp4#d1I2k;hfE(rqH-jK@riuTfONP;=I$%B~#nyC@Td{S9M`sZyqR73$9V`p_ zq#XdOsP4ewJ*c)5*Y+PZ!sQmI+?2!vn`v|DOY*Wuw0pFFZ#YL#A?nL$vjVFlC^rlt zX^-HuRH%eATG82Nxw93zc%A^h`)c5TxSJz)P$o`cfkc<}ymJPKXh~`r5iN0YVrKa= zwB~u&b{MG`?2})C{sGsykRT z4WCEAOzxRB?#u5pMh8#d(HfZQE|%!evd4QhWT$0^VG7}6739?r#K{upSLK0O$wvzn z@Q!sfJ~sgDZTt(&B=lsPfSWRKLIV#zJGzrVZgL^L2frc{is#J*PNI)#AT#ZYfZpE1 z&r}7xJTTiGZLJ!}9-yQP@%CBi(c?P!$5EmZ4bf>4>7i+%fMGNnCU}QD4Gb>vqjQ5& zq8a}+g^|{Zsf-_~F(P)`HRtFk`F2*!)hp1xiwd}L(|biyL_$D3e0j4zR1n7%-r&dQ z#HS~wr3 zOTK7Bs$IVMk6@6Q?g$xD3j&}mIncA_deRq z?B3YhG}5pKZQe)nj=7tfIHgAk{S%_b8>=Sy-%T>7} z=m;l;Cj>BP>EE=nxVlL!T%jvd8wSe4)e>HPL^WrO)C!VC!Ai`fOV$-f=#bq4uZV^QF_Yx3YV$ixxD0D?gu+r$-H!*1mLZI4)z?gUDm+c?0vw5KZ&+a+1BUvj13NT zmdvnUuA2|P%&o#&zMD?p4tAU>3;FT9bGwIVy^c4qN1pnjGXSJstQ z8v{Mn*T==zBe2+`!V_l4_t7S<&Kp(&v};TxWTb|tvFW=^fxGWIjj`scnT+qKL4y6E zM(p-jXC`4E@~*~Qje~@NTIx+d5TAqV1N7-0X=Uf$h|gG&bu%p?EuAmA-FX{o{*)Vn z8>$7M=$PuA=9&mR_j+B~u3L7w0WYCpAI!6)z_b zg{iGMrYeXo2ukOTv{Vd;1}P-RJ7=UvPiW&G!L3v6LzH!MKDn=$g`E;9)Av4Kr zzvp;$&y$|><6Tfbf|Aux#8~yfElKo1$B-2g;=fI>X+s6s5veB7Dum;tJQjt}J7&Iy zDK_lXHk&BO&!QjtVLpNsLC+GH_#gK(nCCmR#*VwdjZ{Pz_5gc9s(`xbz?F__=Z_0x zN`o^Xt0Sen>{3y!00ssA;*O>CGhwY zJPyI*OL&yO0{IajqUa1G{bBQAuq}>BiWx2&KpK+NT;{X${YcsHy}let2i&R7vPT1? z#lV-nG8!=iYe*6&790=bvjg+d>#Y0?!=x-1g4iiK@|m zhv8%IYU}EDu$v=4kJSG&gor;v5a-d@gFGhmnN-buawBJXenI?g{)$-s^}_1{DmKjP zdPwko=#T_lODS0~icI>k$xq}G^8s+cicCkl`$l@E`Wd5d*cLAsd)<3Za{W$T-{;eI zAjv)gm)=X7y-DnU&fe^-XNMi9r?R>~zndDoN(&q^EpNL^gq&-|p>2Rr_$i#ubILoH z;y#eai7NJK1)wQX2%;P8+Kh4>`^q_f)n|KbYN_)m7X0(rk5llDDdH%)0tj0#6Xd0X zn~hMjCiXIAo_83_j`rb+5fp?z_^y8oV&Nb_;qJgXG~O|Sb`WxBc{U(dH9=3QXGCT+ zpbZdKc^v#VqF-a0>|Ofqu@4^60yrT5Q8s;;r9069j`wYyp5qmrz|(7=gJMg4q5^51 zntU+mFm-ptWaQqI)szwV4c=8lL(pWbwXHW2UFwA%SB*93Q&Ky@GpWwL*%*#hasq3t zdTfDJ^%6eQzyw_sr-1k4BWV9Q>|iXt41#p0uBc&`HgpUf!N-OO>?&u|^0mNRQq++C z9ckgoHiEqibg`NM&jIxpjeL${AGk9pvaJ<0P~0Ift7E!0t};a$k;gCzq_ZS7bQlzd z79RsiPb|STa;XZ|(k9rk1`2>eb|)@5SvxvuRfOYFbW@JN|g>Su8qm^ZBh? z*{P+;f(7fV8mN_zU6d|U2iR%)e9qz%@lgr#E7XbN>KnAo5SdYC7e`g zG9c1V#i?P>cBF+4W352~`-5(jd=WVruf+DEG|*#^i+tP+X#iH9%GtpQiB^t!H?Oa0zm7a=#KKGxGJh2Z4 zm9y76W$(3qh5vNT40~Oq!ImG zeF2M|SHZP7&(g+eowyseJ>ONfj^2fp&upmr$d&dVk{wxX8e25Gxrr$?3FLC+n*n`5~izp{P{ z;r;vI8T@}AJsfo&L%R>3q~JJx99=xDef?|gTV0{2*s!>6={S297LWhyIMIa)kiR!E zK_(XS$dZX(&!EzI*y6TQ`+)a=xuRzREg!R0#R@)|d^+_bJ=I-6M@R0CD9104Oi$U2 zvgbYmnSz5be_aFf*KK#+e9C3ovwnOusf{6kBLxt^GeSqvSb3DQz2z0tKL+>bfvNv%U}h4|pQF>&*wNcKL_#QI zUFc8-OTA?Qh2%8)`Tsn6<;u4)up5og`*__XT8EYoheSle9FSq;^w(FGdcbF=DJ8Jza}CqW*kYvQZnR@G z=$z$~cIl~CLn00V`q(=ax?g zg$kGcMov(t>6kUp+nOt>-mo#x>fq)MPDqJ^!WDWn^}BCFak1gy;r9vl*bxS~^^|sqz+>BMXj%KJT=QCBQLjyD6APV2X*LW^6siRD@;@FQ2#Q(Tnv}Q?*?cfzW zaD_wbsy5Zu_Lc$&akK(3Yh4zl-bw{0w9aYBvxUS-=nPeJLYF-POV1Yk_RY^VvG3eM zZ(}i$SQ%X(3%+(=JzGbap>e#DnfTj3Sq|6jZsbRP&6@4JE~qlB66Q%8S3_A^2J)6W zYWdn*NC1`GJtm6>#*f(Zv@Rf&{@r(5ASU1ab?N8#TbSBUG);eS>)zP_XOasb5F4wP zTq1{VFB&uJZ`Lf70b2-JL#zH^5iPxU>%;j=!?z~yEQou$CkIB&Zn67JG$V`oRDw^i zU6g-YBuB2OXfKE1NXw~Y+bA2KfNt=Atdpn&N>URhrsBMz(Cgb4$|Zk$-zfJSF zx|)8et#@EY0H?LKXgFU*YB%C(xC75W(eOe001lO}Sxu$llaV{|DoZiH7<6Xmsyo^$ zlu%IdlZSV7ae~Y3n%yeJ%G~K2_rVElRVqFhz7?~r3i;BI+&Fbrdvj&qfQQPd#;>b~ zbEW?DphasD{bImF9cJJptrx|yfB-o~mUzpl$8gFRcPiu0g{Hvkk?Ab^Q{%vRREdTl;~>h z=&tQKE9W?$KW1{$t%zS9EQ~J)Gp%pbn!8MRGrFj~MZnRFf`X^$Y7syGYI%8ib!mNZ zv?RJ9rlyvYD2uDV!f3T^J>b7691`$|TZ6S$uJPq+eK2XsD3u ztk0rvt3rjd0A8C27IU$}7y+P?fCnYp7 zERlXKyc(>=fs-UpkjKelBYZ;xLPCY9vV{DrB_(o&yt+7A3?EnrmFwatdAL+u0pC{X zEaW7IgQZGn~8;11R(j5&V2ao zs5$oXl>p=Kg(By98pi)Cg*O3;e-jgbuC1mly*GKh8xA(kVFj^Yl>M!B3YLd2rcht_ zeeJID8|&d*tlikUQ$ezS+627?2Po@XLp9$8s`^%Rc|Q?0=p84&r|@T*dh{7{FX(o~ zUJ0-c^{GtctmCnlLI0bk225ej8_WtpLVaLwK$E^%c$sgRs|eg6xOnr-cbe;4=FsLN zyt3-b)=EIP|6cPZYu`NjQiYB_2U_dQ+0LMyGiduP+{3T%H5)X8l;$`-sy$9LgIhp# zd}nBBtfGq(Qs`3RS4jH99iS$zBcv=w(a8xFyOjEgNn|-k)y8*+l*d%U>%@+7Z!x@X z9aR(C9Sr+AIH3}!GT6snWT2P9IQ$0osKFmR$RHKv)t?Iu`8nV?FCuNOB?qOf@sC0r zbSl#js5Et4-QBF`0u;6__I3KrRqgdnplI)fC#hzBTWnjbQ?z4@OFUo- zqVtn_iUvytN|oV#;SDiW%^d$i_ac{~R#`J}1D)g?uSmB@=SUlwwbT&+Ca*qYa+$^~ zWY|T5jBI7`YW#w~F6udh3z_aQF>wI3juHC-feHm@QWBcTwRezAd;UU$%etrw5Ek?c z<5LU-fQ^6`uGqKmjnYOrqO|<^Ev4m;m4oOc6lr%Z{5WqujrPMG_Y^*bPPeGNZ?Tt^ z@HOi+N=kD8pVR^iIqfk+V{nMhM%7X_D$1+B5E;hjK))Zdk(L~svL+xS61|>@PLj`b zARXrxSsGO-VZ&sINaI z=LGsVI66R-dtD$8aPqD-;^PKD=GQYj&^p~ew`iz zukg|R7*Rm*8$&CVcxC@tT+Q@}PKmo_Kz~F5?UWczqS#ezW0D~eR0@#sz_r3_W$DHE zb#Kmxzr6%9QNS@YAsHA7Vf{SxlJd6;Jp2-oz#BXZbTIFjtABhH^q2gAS=+Jr7wU@X zVW)!ubdgoS@0Rx|TIy9{Gk#5xwVj+R(({$pa-m32m@lSd|KrfukoDl5e@A`V`p;3H z_M2h!e8 znx%;M&=I$CwDuTxQ##lCklMG2Zh43TPU2eq`l`nIy1FwEUSCH?M~^{3uvhz#U}xGu zuzg20q2mvs=R88x&{F7!@XGH0g#JHa|6d`0^xHEC`4EKMn2tDGPY))rQ0yD*hN#0w z)Npn8v*6hN2sZ|10@cWGjcb8R$^kAZQ8H*RC-xQ(l?;|C!}`J+qU!)jEp{(C(I|P?)675 zR1C9OhZzVj)=(H}G7y4we=|etpbem~&JpnVLMv8%gmn%Pc!dRZ2QvX>Ziko@W*!2L z13rd=DD7EQ9~Af5&8ztC!Gz-b*ZUop-l_$AG>ZKS2)Mnq1$5cGzoXFlEtWj&4N{8et*!K~Z(-L~OP+QsvVOoi z&=N3)_Vg&bls!#7P2Jwzox>_X0)Vv6%;=P2;>zt)cW+F7F&sB)HO)3IFxHfy7pV2j zbJCcP3?zSFnuNJ)ge4v0zycBuf*Hh`uX>2c^ z6TLDjhu|&NE(ZVguONeEVkYh1DB~#lL|LY9|q=wx%M53 z-Mt|PaV(GWkcE;$t1aQ$wm{%<^IyBxf|qDpNYCZcz|IyQZ($fSEQA+JdVdu^NYmhUjR2Gdn#Y9FLwmH7B07`cD& zHW;}Ftr`O758kvY!6xiXpvQn8h{Tl4i9p?BV>MOh*WfJi?+1aI8qW@IAZc)IsSek! zi!zVVDo#YJ z(e4>xVH>W8uL-(VP-zGdJqrm0+dNLU1F%_F!&Abq#z3%(Y9;-m4r|Fp5F@?Cz;#2Y zV+eOlo&*n(G#mwGpk!=+2;k0=t2N<5I`3NxQ3%?!8o@l|HK5TPcOWc5uw*GKT#NlY zWUT_DBa07s*cz~oh-*pjf<))kpjQX*tFzE**f)FPG+w@-83j4H7f#{Eza86%t3h!DZ%)Eq?|{%0)bJwd$mCNdEI0e4_R_wtz%(JFh@kMo6>&=K}vm77_R%`a+ub4|J?sk&%J?>k$Yfa z2vV3c=;gV#*{Yj0vrRX_6E)!h)V+4mADvZ#Ia8<3Z@=`lFGZBU zAHb1^miiad9T|X^;7y6xbuDBC5a_@u{{b15BKs_~on+hFqZo5+jt5{QHXa8&j|58+ z6am8cdo^DUj1F3sa6m)B{qnr$J;%-~`eU6=b;g}18uig?G=LpPP{9b^dhgfawJ^Zt z8!$9Tse6cH%w+#~2}j^2aDhAob{WY0Be?Dk!F6YjY+;}A(!P5dW0v+3+}f@2=J)WX zUH>*vuWqG<+6HG9w}Mbx!;(u@CCMogJIydKR(Jd*k>B;IP19a|-DK zFk9>WYz24oVHOUjC5lwtz0KXtT|KQGKXkMWDdFj|1)RC3t%d?-J{&5%qZqt37dQ(Z zFEmN-9+dhuHvwD7{`;#8m=ga)q2C*C7Sdh+J`e5v z;g5|yu<_#h?L2n=TMWEevjM~}DlJf@C62bzbPwTlC2CdSRyDKinVNd+^|kA`e?uSV zlH&cweQ?FUfm8%LCx-!QV=AOaobg}pc_K_04+Nh0`xS`M`moWlnS)LcTKmN^kT3!F zn@w9b6-LwI!wpUK9ksorRy_kUVXe0smt?|vVc1EL@qSV%d zfwO4so%FsR>9PM^hp(|f!7x^bKaUj<^Uufn(rx3n&?5?aXy%XycqjlN@B*&1M3uHn zY#$tBrRjme^`kZ=Zkr^Y5AGY+G5^Lsj#Y(m?FZGMua2NZpRHW71{z!U zRmKx6r$8Gh&i!)C&SYVdC@MSQ*P>2^c$|ce$jh%92Q%#pqI(R4Dj)+%<=3+c3^RoA zz$hfIC4^t+^BusWG%h_yZdif9@>P3*xnh4iy4?Sh@~&!5rG~oKrnzrthKFTSH%66X zR+EUF^dDmX-mW=?)={x0e(wZW4u^88hASQyPm&WEl<<81zAU@Z0!~_Y;wK3cs#LNXzybITKi$vKRwdp#dkLRlUQXci zuNuI63lfCE*<8y77Te^tQ_!%mVwx1k%^4Ja0#Q&Em3)X07IPugDu(>*UlAFy`mdO%M+^QGld6Cr_75L3{_lsSQNdV> zin=96buZR<5;RUghd~t!OfW*IR5}53jdKv=aG~Ce8 zA@8ia&~GsjP#}?&iA5%*HXwFEA~xKam_~R5aYLFZEnZy!Ux-&OF!AbV-P8k*#}0$O z>Q0VX?e?C%O{CUo@%o#r&j%*^C;G+*`9gH?xj2g6gv&gblQSkhS~xYy{>R@n7tsWT z@I{R2DB%n68`Z#u3B3`E)zqghU)T(JsXI8Pja&QoK#|-hjdF0=WWOg^5?2}~i2F?O zw0~lNWdG#~_FGJ%F-N9J$4pHOB*`V=`Ee?xd@gsK{1k7QLR&r^!y(bgCF8MU=*rJ)2yK7SeRgp*>ZUtnNN*R3C0CeqvK&CUr${GWX#()XUBe+=)Z-6 z@A%BJ!v-~44VYsFZ-wS7O4UKZ+vzUssP-zwu8oX3=5x&1PLiaE6eV^lTRtg$KsF{( zHMVu9#jc~3oVX4zwQnDZSHeT(2TZLeOmu$0udgRGS@3%$z|~iw5Xv2IJBzm*dRr7F zcgu~gZc%hqfIQ1MEuUOIAy*fx$(&f~84LL{*`Y5jFhM;zCNH_NtfIQC9)L$)N32hq z+l#p;%UOxoIq_gjr1m{loagmycf$f&0wc05z4dy@jmjIvxng-`jo?cm%8y}T6(Oz= z(WVh>oh%fsc|p5O`@&5k@Qlj^dnc03IHhR>K}K$0XOF5@(bv`1+1lAfLs+&f%+EB; zm8b!hoO8Qkp8fESW*232kpxHr46XZ&T_uP{RppwC;1R zhc4O+ST3hp!n?dmg1hTEaYZ3=P~=6q@64k$1l!PkE6N3dvXUWP)HM2RnHdri@m9L{ z2+rMr*UWqlMi}eRQ3`$GjK3g0)#-!{CjJ7ifIUvIhyAFq#F#qAyj&1fm11CRyT^L% zW$_X#rc>G`Yn0dcH3hYW9SYeV^=1ONm4txm;$Mn>F8{oq{L5Tp}dAeSj5MO*T> z7ab_S-8|ggG-P*~h4YDGu|zBeQVWY1Dw2%l8pdXabTNxTjteO!wIRk?f)Z`>A6&6c)Kqcv($1TWtSD+QW8}^59Px@ zg|d0{nXVZU|92=;2yF2G5leHN%)OAsG7j4vy@3x31$3}jML(52t|hSOzf-n)L1muTDsUnds9I}_MUEqZf8@XedJ3LXd7ahRCnbuI>`!?N?g-Wb@ z9<8I^MeC@8o{<^I;Fr(JX82SxLU7W6h0s=Mk5weGC0g6-5?@h=D*` zZ@S2)HKSsy0-HkNo}w}W0&Ns!gWhABI=}C8FigZR4$k9G$fv7R^+k!2L{Y*C2TV9_ zixBY(!*k-lu0${P-y0yokB^_1uory8PGc$vA_4s{w4x5JoBeW5ZkA#b5<$i%#HL5B zi4Muk3N{qPrw~Bq=Q^5}LLjec*b# zy`-b8sm!jMgaOR{=0+oN;B1qs3omki(ZnDqFxlxt~ZT3Sj*%GD%r;=6h+ zGb_z7EjvRfU5#SkKz5MxnjYerM-!J_&7#Nq57jMIG(F&3|2^D}rvD6=m9sS5cRiPe zdj^BWo9%IIOCUX+1!M~%*Q6f*ADU{49xC}ihFYQf@1Y(-w%eCRn)cr#J@tQMq$KCh zBR!@BBR1!$$Ky7W-;7rSvhnEKFdyK6>Xb5fM)CJH^|@a!qP-U`N)e$mlHB{WTW*R0Tr|j;COIyEw^&qQ`cs$WDAN%T)=e zxVAWTbT5e%wC&^2voT%|_Z7e|Dqko;FR5$MvI4Y|?8g4YrL34jNUd0Qt+=3sHXSU^ z$`u%j^6~{a(ww5KzRZr8+(^2P>XhLPvqs2ma_Tk6ZE{9(3LkQtl1k_HT6Tu0WHkz5 zvW@x1w>3}=e?t$ng0!zv+wqR`K&*U%=Fn~bGO}4CVSUZSjxY^84{}N9vnkL^!YheX9@0Gn#waEXtjol9=mq-JIi_?O zLzzndzuOH9Dj|hhBhaUYNWwli$PMbc?|~)yGoGImykDW04?VZJ<3* zRk(nI?ByXjC%!3gB&d_b(dkr#Nx&IjJAhEe3z8Lm^>@qEBz_Nc2aX1^WW0f;AeONB zTzyv-dtE8xCX5v%zc)~Ewt+{&97ufT0(uKX$&DDB(Rvmx2;?P!_h+V`L0+`rF0Z>L zQ&3_k7v>a$=XC*EF@;zB6|I}Z1hb^Ge53?sa+iR&=BegWy6 z?vW8|kd}BY`5K&;*Ag?5llWk^ntb&})-}Vltn}R6)d*HIFz7pUuD0Sgsd$^ z(C~A+0K+q4piP_bw)4n{aNcuf|Jh5038f)<{DJPlsm@svgS2Zov(>r9zSOH5D3TrK zQ`Q4u0y~8bzej5)-y-ClrDO5|5PL@^RIy))yT~7m#?Pa#rjGGCiuu(co#A!*EB3b~ zsln|7jVjRz(1Y5(@XOq1(^1L~)wh)F1ZVVUv5CS^%^iG%x253AVEY7}hMLGiKKTUg z5TRJ~-fAS&41*Wg$6G1eu-y?y;`8G3BeD{zvTJi{a>;XVS$@v+w{SA^@F|D`fOt$p zZPQTo5Q%pv;o>U~QTT~s>uARfv)<5RQvOK!-aTf0ejT(A!FtXan`u9wu+Ms^a;b4F zn6Y1)#Fy@$!-XIe>%&ApY5Xbc(Cy)e;+}tJO?G{5eI9vs9ek(XB?mKCUqPHGG&iQX zwq>wJN#b2fWXP=dYjZ1#sG~u}$47n2$%=m{jXq>HA_pEAL`JHf9uN!}^lF$KyFVIMnw$>{gGg?2{5EnpohrT}b;$GUFXANEM07_tkRxeS zNpQKRD73Sj!!HeP2n7#et5>8B@4;oXsrk&v=q-&1-x~e0e}Ye6>^|uKEr^(kxJtf){EoQ8>t~h* z%7R4UBMqE@-t)ICz+h|HOAJdtMNqYGg`YkS1*pETv!LgsfRkjCU?1m9t|Llo)5%r8k^&qwb6U0b)fN*s*=6$8Jv;$cJEaNsIkx5F#z$S|z#9>7d#gX7dk8CJm zpfCu;qmO}eABf1pRQgMF3EFtg-UDduz);>^%DC zqLM1}Ds#&Bo1{_OAzi_69LYxYpa>>(&9}_-QbFmgK1rF-M&8PxBv+*Nd47EoPI7g0 zPgEm0mqC>VS9l6TJC-t8A6h|bS0_-hHvX5qy_JES=GN*-2{e~30Ae`NHXgdIBn~!1 zwnRd3d}wSKs4nOUue`dpUD1BLgcIZCZ4=_FjOO5Ngt$%AA%HvP z>v)C&RrA8oqA+PViKhs8iL|u1j68;ap@z^#(5T-?OQUVH!5fKhbo$XoTPFTa6HY6c z?Zw-7y)BL|^#o{AQ%!qSH|X4HRr8C7m<@AFYV9kIjiJ*c>Y>@8)MswI-x>CNhSIIg z4VjxP0sPRK-x>S)j2ND}?pAFVdU4^e@S3xH#xoQ6eTL5{yTktFGvmbow1%qit8fY$Eb;J~d3pycjF*16stV$Rk2$iWDJwsB)9kz>9|hDVox(~>+E*KL4b$)b`i zqfdpnb%t^(IwiLYfLW8b=)^tc!!ki{TijBi3l0lCX!AJU{K?V%_`)a0_cPf$Uu6Kd zNt{tu!LCE)PwGlW+K zuf0_9-=es6Z5taH@q2h3HfFMlY zYcp?c0zf4(!$6!|877DyoU3{$en4vciDays=nx;Tac0H3W@bAZ3Zim@ZbbH^cV&P+ z4irTsAXq7!5~3YuKtE;h-ypIZvRZC*09|Pr!!|o1O*e9BPgbYs8mCMF#w4J^=WN8^ zjmxN%bGlk^+Xnq!$kVye?)dhmxdy!lg`Q8zLeCx7;@5I%t@(_8i<6PtdFFGj`^mU?t>yuIwU0l zPPx@LsATJ?lVNUs!JM)#$t?jqN3Jir(n(>z#4tf?%x{f+^&6WvzJpE&IDfD= z={bgtJ~;yU`TJ{ySVkqjp$n69QN5^9&`e@KKthYdi-JK@|J{oq{pEmnL*opy_7%oR zz4BtZ>RMYGJLq_V(rTi8d_0+)lAIBo^2MyG@6ECe-u_UZ9 zqCy{-4w@~%cuU%(i@zu2HO2Ks;J1EY(ro4nrSDMNP^7YA=-O!RSlVRM=rG8izK7Q> zP1uLf`Zv|lV>f0xW)|j<(Ok+Tn_+B>CKfkRVi%EB_GKt>nKE`=K$^)dL{U@GptQ$I z!g(<9Y#9%~Mz9&A(T``I1@hj)D=#7d7%*IxC%Z;njB_tb=Df)(&rfTK8;kDL2gWGi zR^912_42LQU2DeNTN?SX0o$9;T2eYzmsEV4Zy1psC<3M96W3A`0>j>eHv5&UIkli; z=Ay(dlgMTW+l(XUA?!R3-&3(1xg!tkeh0-*;dp{AU=@CPl8M{2m_nP5oZz9?faARB z2ormuRg^YHjZUIXYXf=721!BJeb)%01%X-7T^ZFk%`#s`M&1l-2u)SMa^klzsYQzHi@cHmyv#KIh8MzJkU zGLkOGSj4%Kro2IUN=-~}Y_q;qRH3KXKF?c!_c_gr>Kz0q#CD3?ND#5!1?%P`Ah-nM zTtGXIGI1b+L%6O+m(WhIrAtg-+`#gH(=9*vMii-Pye=v=EED8P8&+%f(c|0=wbb?J zFo0Y|fFI9|F0UL2mH&^x0YTraGja9cE>W{s|VBYX@0;O*VJU7>gaf%!s>wn#E38&ET%S!qV#g zzklg8a3HF0R@{~Y)Ywh!KJn?8bRwysblSB&Y2wa=iTWn|Eon*)xAOHjKby2dkq%Q| z&w9?H6*t+8m3%Y~Q%(~UInKm#C>F38M#dM>Fp#@*$O(Ga>i{O_!xkUwKi@((8mRiP zX0M{qZr~%8d(`@rz*$b}jiO(uDD!a35F2v*U8Hv!>+Od1w~5~QFE9(gcn)n~TAsRK zdIAg@G8h{q@s$7yDJzC~lnl7~v#n^$6;Kq_ImOZJ$EnO_xCzt&o9Br7176=J?N92! zO3DlHmyd{W*D$x>%OqUbCd-VqdKfC^tMINoH} z8ofHl&ijmy$`{u^D`w#gt~vZP0RzS=uWH*upZ*3;LalGNe*>wx77gx>Yx&%>Gc0z) zYkaQhW)?a&jaPH+(^$_|FTMuC7u!Ji;^5mts3PV@DPVeA4laW|GXM~;o~xuir`RUT z83OGLXsb?+qgS1#ApFqy08D`adKDWmR~+s~x_$F~-Q6X3dH4lBALdVSHB=2Q!g1)4_bNC*BY)ikAfC#G+F*-wgFVB6D^TCM9u?4T?d}T()LH zCch62$*kZ!U<{a~r6l=-o;es)C1;lye!*l1t4zQmb}#6;z*Gp1%e9=71jltA9DkBC zzbw%0W#S|80IB+*3xCjp?3rvYol}^}G?B-D3iu=v@L?)h(Iibt2!Zts=?YFeL7_B0 z>2eAPrpuTg<$X~!B_}~AxQ9Y#xkLsFELAh-DR_MAA%_!CGoe2*uOzoD8&(240BC9V z4TUa3DB%x;(v==8F`~+biq>+_!0MP;+{3Jz1C#Nn+<5&G zp;E(zqvs_+TE-P_SEX6UOsBS!4=xl!Y+F8ETcE&**B47|ZyA|@tkDIX$24(p_1 z2^h;?@nk{zy927=ElvhX(|gWW=_5!~vKc;5>)Qh20GfBrweNzm z5S{bLo}jX*x<*d0)Um>=nAEl{g0LsF>2?4wu9OIi362SclP#N&iNW4nLS}vq_i!7M z;bnLJ(q30!|AyqpwwA#)r-5=PV;xER%I;*=$f`=pOQGCCmhj!{5@4p>1ec92-lGko zM7hP;p9$Tpj+$(B7AML;9ZDuBhW|(mcz33~`94qlcDOLUh`w%5)Ow*|Wiv(kiz za9O2Eg3Ss110!G4#H_8>1CAc&9bmX^mIfpp{nG%nHt~ zGxqCzwgC847MKU!$35iLSU2}U$kD>*HNIyTOeHr*pXqUAu&qfFBvI2AHF3sQGWWlm< zsDD7!*Xh!Ezq12id)pQeZ=`ZOVD{Sh&z+v98`;d(eh&^mHC6^t7FuJ$?D~u=uiYeP~-nS-WDq_C;S2ZlwDSlU@&h7ST~ApXIh3^&zP*6Ea@}Kmc&}|yk|LBIVpXZ4yezOL zuqpVc&)%?o@r7~4L3z<%Nx!Um&`#dI{KW6)=zOV6Q7GOf+E;R_dZt6AYP{)G3bm0S zLR?Ztp^m%om{b-6>_t|*Dq;&iD%1!S!a*81J(0g z@Mw^`=SS56pQaj|UdC^8U;mA@f@?Ixx~a58#{tl_fEaUT^X~JL~Z;Mp-Z&}Z? z8OIptTPRN9jh`{EoH9Lk&KYFRhD{{=@(@9Sv`|_kg*jC8e3d4l2dDR-OP%;qCo=8C zrX47(2ZuA0XVA>hI|ldSQ9jngXaCAXC&527KL(_~qR30U zLYYt|kY6dF5vuI$q*DOL{R~QRN1+sV6hSlZs53rFJl5$zM_rgbkg$OOwM%_abt5OB z)T++00{R)i21-mzOUy9f`(MxWSW*arJLnJ?r1;6?DSb6U6>I1EOP zXY{2=K(0MRW(brYmNOW~@VOD>0eC=y0XjwlO#qZ8myyFD*6=PY&W#xWsDYb0Bkec| zhpo_DW8sR$?JRr@mPd>N zr1WROcjP4KX&=6tf8l=a4}zy6{ftrkcD(Lah&O2z1%e z;xcR-Rc*!uUu&-r=kJ6WTV2w}A~g zSk9f5#@u~=>|@gM?{oj)^OYT@1F2BEMm> zxr~(zF2@aI-;xe9xV%#=_6sjCKqqtr0-a>cKs>aA$rii>{ih3XMjjz9N5{v)JdZv; zVU*A(8$^rE|GAU9uWo+~e~sHvJ{}F#;c;R^fC1-1eV?8=M@v zIXFIA^VO~=PF+3y?J5fpJ^3U4$|6MVR_9EG4BuC}Oj*swp;gIqE+gh2!l94CSx+(+ zmoN6yt8|9ZCA2{IK}jQ6E)Hy_@C7B}!46}FF`zVfBM4er41OX*tJ*?2A*zdsLRUGV zRdCU4c>$2NEPR4%n9jm<2#WHn3ec%08a`Ssg%B`~K!+~qj}W||_n#2x@SkVtu$~bL z9mPFFQh9uJLb+Wm2jm{h;mZ#Lag9C>^giTE2YR$?l+S|!U-_qCP;Pkr8NBY$7Lr}O zX&qk}C3Vk@fu(U<1>8jLPA!@<%STHmKsm_{&8qM;Gh3{SY_xo?MHlPx#FLSx@!x;H z$!Dm3|NSREW90ET$Q99srR#5gIbAnZqf8xAOr|KiMu1iDq8WeDdoc_c2^)6t@ad-% zTDbw5$oM#H>1{COv5ht`5E~#QB^I*dInK}lPVI=83x(!Y(yI8=5hnudBv%y)q9nyY z`8~-3iI@FK6`V(F@^x`1D6Fxa`KX6^Cnq;1CoU^qzrQrG%}?#ytM3pcj1oi$BJ}Z# zX+zVa54&#Zn^%Rq`q}u{>&Ho=q*29+=2~S{ucMr!DMDV)mSL~QM9k%2yD*PHAbHvA zvY#AhwEA>8*LXUba5NEUno9DDw2yWtcN4`;;$~6Hc?lKRII|8k7_RLkrJw`8$!*ZtYsVbu2oI<$J2VakJt6N5r4Fyk~2e?V`(gcAgv}6NmM&{tYVr zOw`eEtHRW>6hW$NpyXpQ2wJ~VNJ(vFr}Le=#hm2!@aZV%q=py3sj0|Chbn~joFPeK zYxsQlkiH#1KP*2aKUg2UpVhb3Uk!etZ&?}b6?rAxNB>}IeOJRs<*@#x5?xI(3eonZo^W)M%k0CHSL6j}a`6SQZDbOoEKGZ)lGAa%B2W7#0`rDNnd(9f? zWQn{A{K*Xj$jsLXYcEPT0lOV{T3wVRS4IhwWR)E4+Q|V_F@axt{N3=Dhw%UBNOx=t zGr2aVJ+_{_43G48HhoTq`uq1c5tnqne|Hn1b%sk9q^VEgbqBVHB54b9kfv^{0KKfa zxkU@6VhTWI)8vYU{_(!?zR`i0qWZ2UgH!!>{ex2aS&7!qTd49zqm|#6H>iq70+sFy zfZp$Aqg{Hyy>8=L3t5^x4FRAD?N$KZK5`uJU1;W*)@|+H1C{r%bjt4XI@fo@B=Pbv zVch4XKX1&Ly*P+lM>XFwwV<(G}|TI)}U%~fLspOd|y|5u0Z zP{%Eq;z8@&Bl9`onD9kFz%Pq(jWR+!>)3(Oz;l z{c75^)C@8s{m;OobN~Td&lRnnM!GP*7mbXu;S^%_W9BVF`!MwZ>pxMKcKrJ9sPCG; z+g1s#9D)(P^6TnmW!`Aqko7`mgI6c}=Z&Q1Bx<8Hgo&0Q>~x<0M&+Ac-$V3zua z{`K?bC#(tapzTuLeCfOuFXc@{=WYHRUI$TU=`tP=Nn8g<7Co4P1W`yOgI%|R~L0)!#t{|&Cvow}YSiyf1mX<-L15)g2O7iv8H7RK{ znkYRhBd>7v$B-@Kb$=Vg;Mqg;W8xOEB1rvUIDaZ}cwiuTs(rw(rb}(sPebk3-<(w? zjaSW{hB;;XWcYCZ#AoB|k0ZMmpDfN$pkGQRT0|U*up|!T$-xU2lR(;Gond@#gE`oWVBcT12XSoh8kh}7 z66vnGc-x$ z8PXrd`U9XhvX?1J6vgMJ$m%%ShZ7?xL;WUbQ6hP7UK{++cU-w zZJ^J05xr`PIhPRUdEDNH2olj2?*ES4?WV5o%D(O^{jQ3@_Fj%(&BY$OhHNnhZ6DtD zpSX>oSD5TdejwV-tn;a8)zy9lfy-2HLF?{M9Yj3($C@WQ5VQ|>LrwhkFQ|pxM+x*b$cjMFcHlKzp9k$*%-ae<`xdX;N@$Z7J3*7{ z&VZ`7r+Z=AsjID{dDOYHXUGcer*F)6)Ugw3v+9X!3&&>BEAy$72{Y`!wU=lvgBmzn zmoP?C*3hs&4QnW_#)QVS`f6vXNHCm6tF)86n@JU35jtdZUuUp>h^OS< zC4hz)0e#S_FhCBG-^Wp{S9)wpyoYNzq4f^^ZUA$KnzME%xC9l275f*2ldx#nK=Qvv zXSZ+p?JlpaBx$KsqR;+@EJ1s3P~Q;zuyC)K@XA=uG2(AuQ%|<9C5q+JN?9cdnsvpL z&Eoq5cv6L=y;#}<3)a(G6%YikIbeZ35?C;i*P;#8z>D_}c8mxpOyz6>lnG)Ug4U5!4!1t#1}q93t};!2@~?-md;hqgV$b8{2r&^3oc4E%|Ik#RJxUKo_vp z-DBOO{jnp`zRt0(flmhL>-X{W*%>djWUOu?XeJ)$66nnt_Wma(8iU1n@O>R^=C0{u zY3H70QH5r@>@=9_22|+o9oHCk_A}Wpzr?^R#}UAi>+F6p!K;f2q_6pI?Ac~)&crV# zcW^KS)=VOI@c~Cwdis^DeS)skurp(U!l9PkE zC+K~J=O^?xw6)jwlXZ42&V4=wMY8fjk!7uGm(M`U?TUwzrzH3Y9wYK=bL(@VOhkS?nV(eS7}i(ryiJ}18kVpn6fP@L@N5^Gpfop(Ar zJKMXOdpf$?dS3%mxsOML^ZbFCv1@au=Msi*4a6_xkFuTcJBuX9DFbaO?Kwmn|`tsL9Nt-Z&pNIZCzvT-}$b~F&=QNhgQR?WkK(OwdF zFN)}A-Z8v&|68J{BKO?rf2{@jW*0wT*3K*LmMoBL!Mp?xbd%&k*=! z#HQw5=^gFVjO!T!sc;%q94(C~j=&2EoL?G#?f7}nq=Dlf;~VQ6i_MZbZPNCVR_LB^ zs*yJygu@RdEU#P$2up_Uz_sY~{CHZQy*NjagARb=xHH4}bdY&uP_lijXOe$yb4gv2`sf?ZKs8C!Mgw0ZDwB znBxwxb9WFYux|zOyZe3~4tnOMiDe;+_6$*a`3)d$`~>o-$GmhuWVT70N~+4pN@9F1 z()j=xco!5wSygmebg|UBrbX6K-l%Y{;e6MNt(y_E3o{$hhAzCJ?J3yJvR`B9;mhyv zV5Oec{u4L6(Ftw5f?sw*FJH!&>`^>%-0iJ^?NOks9GVYm z!Te>6Ug7^k+IvS;ad!WsATl!&gNEZI2Nkf3U8BaXh;#%|5etfdAWH8YL1`A0-g_^C zAV?7lAOcD~=TPm&XpF`fqe=GQb4>0YOup~?y}x_cx@+CPaxzP1p1tRpz4x=r=W{dd zdKTkN9}5f(MtBvJ4RJo4@ofs8PCgAX+?On@I<{VA{rD7LH zQvnCAuSc<;{OI|4E{GQK6!SjcXLf>7oSILJW2!F}^43R296su&jkOGi@Pw;sqyDs_ zZvW`=0a>^_2ni!_P$Jhc8idXMUPz%!G(3qF@#6>LPZYW);-k*Nw}s#Xd#8dHBuELp zAm8<|YUWGqv3Qo*0s8T3Ua^j;eyI#u8xdi1)O*WV%_P2Fjg!KK0esZR=$t`Ws2PFa z2XJgqPBiMnZUtw8kb+x0Q6~OTS-ez!e1;PrEWS>S`we0TX~k^=+@Zf^OhRO2Vdx&1jDWjXj(duL z#Znb|Wxo3h)y#KDy7MgOI_Si!dB@tP1mLY!A*6@3=Nbf+wGE4{P~%i+@^d&+XZgAy zNoIs*dSH%oyf37EUkw{m0Sj0U@njmk3AQJ|rF6(nS1z+HJeuh!suk}3kxT~Rj91%_ z32#_5?`brXns^IuKDzax_W7DT;j!gA$YitCh`53;_!VMLkHEQpIZ)@Ouz3lkF)$Z$|P1NBQV=J!3T;S-GCKo$i2B3dAq)#R}{IwRL#a>V$QUe*~s~qI3@wF_mvUh zE*5T!k*3=+EUr?G`-%po#5{*=r!)`c-iAF7kd}Aq`nSr)wpaEw?x@?TEOoK};q{L& zaTo;38;Ui^48&2Nef+5Maa#{}&xi!?WLq8@Nly$fBw&pQYHNoeXz1_1>qBIQ|NeU< zL}m!Ci3A_GcxYuw2EDe(^S9~ayA*!Ry$tgcanso+2c$4WDI(1GpaYqB&U4znld)xL zkWNe2qp%o5jnm>L(dJ~;Ork^rw`K(9x+eHS{P&0(z2G_+Gn~GJ5{ydMQOVsgq6_^D zyhEI{KM-_03YN-mq0W+}@`6!MQo@pBU=2akY0U_o6NY08ek}oKu{Io0;@k#5ZC4rk z%SUBwu|IIh|5>bRF3dicJMG6jTyFxd*CE;e-}O4;dOiNL-s6Z)qD>240Y{zQ9HDta zmmx(|#;vTbv|AgKgx+f%XS(Sz@Hb^n*32RT60DICn8VAG^bBQLcnx+k+y;qsF_kQe z9XLPyS&$dxEhQL?0u^};O7P+=QIS_Pl5n#J!KPo9doT>X5;>SZdV$?GdZ3^FR87k9 zV~M8&$&RCBC9z+h5tNGzyb$!$HPFrk2L^VMkJKuDZ)g3%@9peH&`418j&T_KBA1>H zbG7zZiF?~7G`3O=Mxhcv6Kx4MZU}Pmqg*uvKMlhd_phL-2_7|Pxr-!MdJ%)opIPa5 zm@f?^J8keK`RHc`0O zBEvl_-Iw8xv<{7_P=g0>fcuVC)a>Wz``D&nBGCd1=~&yqfR zO`Zpe@&Rx8G^Y+W<1J%xKj9`{6dC4q#A7uU_ie(kCk^@#CN7*sz1R|p1nu zIPp!GmJZI0pfBVm7sle9$(K?UEvTg3w%cv7-$__j;OrT{cSKU3%Bfocp<5# zzbi!TWux|TrhT7tg*R?qyhTELJv}1K(+0JNZ7N~0Q4`X;c$RDU8muYs_-Q)TqwsxBEiB{rfJ2f$Z=xf^J*{1N zt7OGX0rwG3H!jB`(=)|Kx#!CMff?_XiIt7*>W^Qwsvx5QZDh)Wc}?E# zLT(f0&A*mTq0jCnu{RfDFNNvC74(RX5cWD zW6@dlgIJ^lbNk`r2ndIZ#R3lIVwDgn8Z~HtvcZbqJG;{Za~ID7C;afn4^KIdg8sn?F4cD8mwpDd#7YL7=TA3X@u(Q&n+_VUL z5I?U|r62wph8dsN>5nzhLCc3`!+SJM*6&+qlX;@VB{k@N-NRc$UH5m2c3wTy+A2KQ zpnYret@c|jcN)61i-p!E`*-i!yQyVO(^hQGy?|UIFQsUHwjoTH5gkA_Sfac>C~uAf zZn1N{w?H|Ymvd{Nn!PEopWgtVNB*I;8N%c@7%qBj^;+l7{jhCP1}Bx`IvB0=wti#p@UD%REu+oQ%1` zOK;lV|8(a)BdDvo!RkU3-Lv%l>R-)L5CI@PZN^nXz1;PNAu^zeUqHG(mlh{lZD zNsI|aNMJ|K(i?b@GC^TXq;i-t<~aFi;J-zRMUl?(BKhGlU@o!Qi(8b}wog|pDCU4Z zqrp|MhR72e` z@q8x_wCn!9k@3DscDxbP?ne?=Slx@rsyeV)3G@cQdbn)l_mM8KxeCN`AuJb4R!c*v z1u9_8#jyfto+nk-;gqqUbCFz!6|^^*xpxI|vq%rfu}6aw+13T)^I#iK^6X2kEAV`U zW>Nt^z^6G?uxRYvkA+QaWVoNT>w>Y^Ga7fg4;TQlG{Jaown#dgUZC=h<|E1TePAd$ zUg2H*8nt+{5moq7Q76BbnYC>xe-ID8ZVyi#m*^#W!a^9 zrG*TsMo?*7qS+eabvQL}Y)_qO?fsAsnhZ9hIZ*2hJYVAqzr+^$SCA_asm*CWvUu+z;&~t~Fv~g47tXfdg$a4Eo%w~Fq&DPx|BMTtgnRHIepXYa z&udhe6&V(2JAIz20)vQ6l(T1|Ez)VfQUo$JE~R?=nL2U9m`;oSB?~MkpR3 zu)N8f1B;30ehe#k8xeV@6PP@}Mv3pp>Cam(3tMc_$Ne4h7~kn!{+-&7?K;KNVSYAa zCmyRX{+*tt`KS4T1nG*Z>Z_UxuI?>gu7cDHsW@PkoP*S$q?LtmVnD(v=*2WGWZ~~D ziu53C`Q=L$Ri#K|5KVaW{3GgWO6lK~o ze?Iozs4UM6&UZ&Blr+9YlOf@dOCKat)aMf~^kZ4h81u#S{Rxxs7{}D(h{T3M&o}t0 z1-M6dt9*|V&BmB7#wpn!TVB764D+?b-c|2uheG-AHi#B{lEL29E_5@O8!opLHSR0- zQXx@P8umT$5^X1;Y*khHrj0-W9$+4{GPafr;vOA13^JW?hSj5d!h00*|k=8j2` z)y+ns%a!f7_gu!&1RkQRe@sZ#`AdkKX+{8^jeQ`h_fMd&A>Vc$&f2ao@_vsethwj% z@k1$Ov@-CcrnT*md|OPuaAC zXjqZ~Cz5?aIX)oX`MfVohy|s1n3Q~m$O=h_E-R2WV-DuDUigj>l+vDpF8-BXz*m|} zCAP+PTWO)d&nM0PUW}hl?M;bA~XvhOlD1*gGFdat=s35ZRD49ey z>*Kl1Io=O;-fw_e8DPkO8pbUfvpt`xX1vAtpg*SqR53olx6L1pnaBu#2e0*frX-x- zzvWOUrg3G`iP#U+Dzc5nF6XRy*c5v}5yd&Qt zc@#HsJpNjazcdkJO&ZIEbFuWNV;QWpYJjVvIXNk2;{0(oW-MwP$A3|$LLTK-;X#By zDSkfwqLs9DTQPR#;GMWXObP(a{3*w}QM1RwyWiSnn^^N^pJqVwD&WXhubnDe{6qQ#W#v z#n$L!AfzHThiQixFw%E`mo94Sfgf#4wrFE;K7Hlb3W8ts+DP6v92*qjTU<{KHEuLjAWj036+PLFF=HfagQIT81wQwDZroB*?*4Ary^-vF_V^Kp1s(Ku{;br$8 zoW^JoJg>r()8A}dLRn1hv}e>YqNYpc5tb-rgcN#WfT#cpeK5TSgvioBAB+ynIH7tX zJvTBh`6B8qD=#`OSuH&wF(o!DHv2+r)D6E_Pb4yo+JD9}(*JC5cxZSenvkF~CxiWX zw!%~AFGQ$CM4yg}n+lhV>9jU^*n>hm7vX?S#+&GuQ7F~2KIs}=FsIILiCpHlkiizd zjJr8M<5VzGE)7(L9(E;ch*y|@XvB#~23H>+l*R+33%Zs|HKW-0_=~ZZr+rU3keS;t zD>{)($v8PC#y2pt8`hV@a*V=yHmIci^RO@bq*HWZ|JcI<7BXv8GVW&ooYcVRQUCRz z;K(@sA(&wflB%((8Tr|;{?G8#Q~8lu{0hiKEqMqsAR)9(fInip<5H6SQyuweR|7p* zFYLt630>H{0XRS6I|vpdseS05M`yr6_LgOrQHgbPgV3>7zs;mp@6D(#55#e`Tq1SuW9GG=3Z`u!e6`;(UvoYUC#zlnr zSvZg{FuS<-6rThSg?F!}&b8Gl46y|SI_=to*SX*M; zj0upjgLN*bJN9##^rxxVi2}dFOcK~%OfOLZ)#DO41&hh=l%km!>cy}?0?HCyOdQV| z097%l4H<~>XN5@cwwV)+Hn7B+EI@B(iL0!To*MHJ>kr`p&+Fesp6h9aud`u4?plO2 zTl?QjtcKO?#fpwVOzA4<^OuiDUU%^gP#I#rXE9yqMNZ~ zS>jvfS>i!1hX`*pxAZi$Z_GnBX9#rU*9Q39R zPp<<*3>*k#-&%sAcKg2DO+urac^=qH0UMCV?trm=Y#@-E^Q>Y z489x-M)=g>0!%0zb6+&3+vs97@T6bGW_hRkB>O9O)gA6w@@#X5vWablYo&dKy|Q$c z@4Z_OB|{_1>n?6uMr2nV^Hg^5@CfvZOz=*!#jajH-nH!l0pjug+-=d&if+zc3|jZH z1|JOm)+_kg5~{d`INnGEINvDv`6*~|?d&S)JiU(-dZi1n{VtXSu?H3}-8^?cf4ENy zs>!V>uEOT4{n#q&NtCv06HmL;z$`vOID2jIMG>rI1P^c+nCMWO3cRNqJywW8kEa7m z;V@m}SnFEnjVS;muOTuGWc&-Ww|L|4d%rNJ7q<4a_Zi(a5+7^>6|_}L`d;*J1I-pO);RVdL`ydkFILw= zC4%^2Wu1(hkr9k+zJ4$XzQ_dIScqm`NVidQYurIqwG@)iU7Q9~VL8aBs0p|?7y@DI zHnTE3*k9k2c%8GF_HAI&bu|tfN*W&i)w9mvL}{s-sd9tx)Z#61iv;T)-yspdlMt# zO>IZINOBwVZMW%98Qf%62i}NiXqd&8*cMw9Sda)m;mwg7zgGXEl_A6gjl)bMLi;-V zIR$zLM8^9kIa~;eiW24}RK>u>f+WvGuUOyi5}|wV&R=&TT=B2XpIng?T=zFRlK6#? z-3y3J1|kzA1Y#*_Vj5V*!Ug8JREwHh?G9?HWsp9m%7QUf`b0Z@*5e6(JyYU$&T`s{ zfTSC!$0v3w`aRIx{ctFt`)!{tY*Bh7DwM>F)5JObaCiVF^a<{pz#@cm7uvQ(0^%;K@zW;v-~9x6MCUEI^br?IFeG}9wYmGE zIpmlw98;&cy%5O?UYn2x94mpP(#ZqdWLP&u)WV)Fy@y3rP+W|f2`D@)j=E5ch9p6D} zWByn|j1R{JZ$%Gs%g3BO#+bb49CsSiH)9d`;V}LzWsWC}YB~T!tdF(rPAA)|{zAc% zr`X4~ZVOpQ%&3-p|4T=kj#n$|xt1NR*q3oknQS4>5?DBTkSrX)Qn?Avt6si#@hT?c zCE(r%b47Z+k9w5`2ObT<@{yduJNF+BTZ#pbpTccUkHw_keEd8$z@8 z&bF9+vPT;gd1K@iGGj6DC9+hl&-dhy6<=XJRPb2zAn&sr|C|YBPlpBAxXi%_z#^2- zg6;$)%%nr%9_zKoa;v>LZn~#9t820u5p}my9hZEnt=f*%DH|TYY*}rRZKF)~5a$6{ zG-^f`4Wks>sj|AtTctM{#j!orB1!war^wVY+*VoQ6mg;?tsm?qFsV;)PyO?@y0;c` za9id4&$hZ({(~#E)*nwEla^tNLcV7*ZmX%k=fR?%`E3q}{~kAhg^P=oA^W zo8W2SHYm0qLBl`~Xu`g-ZrD7m-+K{`k|S_r>tLuvkhzOn#GPV6j}k04!Mu_lJ$>F9 z1wn!p5{)9|%4vFCJ|2^^kq#q!+9OOGsgFe=W4U~#)8T$ckPrh4tzj`}!B5lV>DK{d zvBgs2aS&6vyigH4*_K-p@L3*)Dm$Wa|iAQ*>0ZX)W_UXEdrtW6p+fQ`Z$A$TA{a8nVDW4V~! zzDYIbb(CB$F}Wpl_Aqxmh}TCibEuC}lJ$rAIW#P1L$3VFFMv~_@d4t75X#!rTnf8=6# zxp@AcF~KJDf-#>3(?G!%=KOELbX5M8ON+>`0!!bg;CMo0pNI-L@4b9Q~6an?hLESqIeh^_fd# z<=el;DbRxF!!rEmh4-05taQOjoqWgpiWdPP)0bUW5d5$gn@gN1!7qDd(nGQW6HkhV zI$(SSmQe(M?3KZ1USkcP(;EV?DRl8%Rpiw3O*(jV(PW+pHvQ5T`%g6HjDj3k4t8r&cfM4gQ~n$_U9D6Kd_hLdhV^Dhd^hLX9DZop9pi zN*u$>zVwe=X>qBUjNtX2r@6(kPvYP#Q$QyMVZtCdIlUl1BT>J7FHpXqgxMMW2bAE$ z>Ze)DF0HBG&X7*(rnvG|_6L3o{N4$|ZkX|pQU3N88s!u;%JGuq0nkLJbH|}iFnVCj zEaRzp2Bz;gnE*CHAHKZC_#O|E+G{LC<%7D0P zHHf3}22W8-Yu|!#A2G(Zo&&s^Mi0y(hjrnwI=VyL4Wl0)xlro@(8SAQYO7jwU{eH}MLeBL~5UgTB!&3I0jxS@9Lo?Fjsngup)=PDciK2O1;5 zzI!B}n4@UeU2&?57&yvTM(O?R*(*xehJ53 zq3gj0Y?U`EL=FGt z1Q|@DW1G}mjUE1`frn2rqwk=P6=*3qxmeg)I5Mzb7g;ql(tUbVQ(7_CcRK?+EHKv> z?^K|7JpP!1KUU*JIi6cFkIpBp$GR^HO&;ccR?oj}Z|(*s-%{r zm#1JCcf1rSKD352U+4aqs~pi|eM+9&s)M>&Lgwlq*or%ie_?cDSMZ@w=A%4ts2O!T zA2Z*5NIYPmBQ>PAX7HN1i@Y4InaIKui@BY#uVN)8L48jgz-+AdX+9fkO5aZI&TPV{ zMZf2_4p8m3=!S{iP8}jXZcFCfa@)micT6lF$+)VG40rhI6*S zU=S-3CKRVQ9)B&vU;ICm)_~F2k>Dp1@;wG`{FklxpWsb-`kL1Yg4cUx?xST`^A>da z@Z>CbwRg-@;N^cUDDOO=vy_!az%Um<*3-oxCl6aiQdJ#TK!w|dT8CnWOjiOoI*sna zf=;8(*Pm=v9*ZMI2?H^G=ldT?&WMr*L;FewPqztb_iMlsY()TfvH&>}1U(BvxZOPW zSvXmUMMCn5++YjzdJ7(u-!D@}fV0UK@`8#58VEYX|LGtUk@-J^Mi0L`yY_3m*BrY% zhZyLC!6Gt;6w!k6QjH!elC1KXwZJI-M6O$cAIJ~pgHjw8exDIn3Ou<`R?xFw134K5 zJ$p1n2v@h-aWu@d2<}R0&_DU`Q}R3aJCpf$2f2C(`mFbCOH&g=XbM$ET>J zB_yQ8=EUSg-;QebjYUqSw82kz)eX?izK1%$O#lMA5@> zJ*WGhNY3;n-#gh?G#H7RROU5#8VS~k8jAIgCe?UN&g3=OfWXaZ83h@z>RCi;Xi;P) zp?3<$bZI`OG!dkeSs)8*dQ4OVUHTgDx8P-cT!*GUXkt&$9Fjh!ObPnImAwB z8kPA~JeJy?;Wo4M@d!Mn%0C-HWn5y>U$}9PSdxVz%|*>dkZoSeiPG-06S*$P4lui? z802HIwDN%D@tRHJHJcxqom~8nW|L8~i77ETu{jq|vxvf&8uX85&!A?R_R65kErOt zaU|A4awLWEVs~m?AW@pLL^UJ$^J$3wBO4aJo?7&j#Lz3My;ybxYlWQ*HrR!yeXD6p1LN_C9gQq>;WA<6LNEokO49R0&R@6L5Q@E-lJ1lQcq+3o2)%VCaNZf<>Rai(~SK^U<_X)#5l5!nC7-D3`dZlwvyhfbqJ@-YH56uAC=|x23~D!x=*HiiU&g? z|4)f~fXRUM=YeIw^dKcLl{G<;Pjge@G}+h)$8pAw9+yKV4*%G=yvcMgQEJ(-XYIkq zTjs+aR3@=RZU(nXk76Do!eoYY5XCa)<-5beIAe7c6nQ0HGT|?^<0TVy6&&8pS&1bc zP})M2qyq}lcPIIGdRm>~->JLMKL2p(k-Wplg>|ksZHrwivW5GQAlvA_^v&z~FISa+ zHLWfjXEK8&sZ`4Vlkg(U2@o#IIbWo$m-?k!6g`! z!OWLGfl3qX5|i)paWK$kRZM1PX;K<#24yUyiAV+PK+M!(u67W{W4?Rb(64aDpd0Fk z1Rvq@Xyr^5!Jn8WxnL80G{%mR%6-~CG&&C3G>?&Q*Uuo+_3*IZoahfzMUOzP4wlrB zg-9+7-{>c27nEkC5#enO7-s`Z8Az1oP=OKQeyYA_4bSRdNcKuTe!-i0(snN8l3ICk zV*a(M+-vfVK6NX+<1|jwkF7`+moqwfNDjJ3)7hTGwWeh|81m}>WeL-83CQ}h>>3f) zqm5jv6PKM+k(xnPNNnIsbNC8@qy9fEK`KWdA38L!x`R>N+Xw4iz(^lkku>wSr6i|i=glAVY5GsN<*;o7W# zT=yiOn(Ugq>H@|90hE`MBt1wX>!hD=JQZ)8*Guy7#(6#TWw*;7HJ(YixrJG2WX<

y>v& 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